diff --git a/.travis.yml b/.travis.yml index c28a05377cf474550d44a30e9b22845772ebfc96..5b3a0fea7b25c3da405ad07e4027a7b6600d3e22 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,5 +1,6 @@ language: cpp -sudo: false +dist: trusty +sudo: required notifications: email: false @@ -8,10 +9,15 @@ cache: - apt - ccache +git: + depth: 3 + addons: apt: + sources: + - sourceline: ppa:beineri/opt-qt571-trusty packages: - - libqt4-dev + - qt57base - zlib1g-dev - libosmesa6-dev - libssl-dev @@ -21,14 +27,14 @@ addons: env: global: - - OSMESA_DIR=/usr - OMP_NUM_THREADS=4 before_install: + - source /opt/qt57/bin/qt57-env.sh - mkdir ../build - cd ../build script: - - cmake ../workbench/src + - cmake -D CMAKE_CXX_FLAGS="-Wno-narrowing" -D CMAKE_BUILD_TYPE=Release -D WORKBENCH_MESA_DIR=/usr ../workbench/src - make -j 4 - ctest diff --git a/Doxyfile b/Doxyfile index 1d0e24be8b1fc1231752699564a71b861740b918..632e830f75e70d98969068533fc6bcbb0cda6d87 100644 --- a/Doxyfile +++ b/Doxyfile @@ -43,6 +43,7 @@ INPUT = \ src/FilesBase \ src/FtglFont \ src/Gifti \ + src/Graphics \ src/GuiQt \ src/Nifti \ src/OSMesaDummy \ diff --git a/README b/README index 5004461e1801bc86582b06f6cbba24be7aa32b5c..720348613ba077c104cc3d1a3b70b89b4ec2db6e 100644 --- a/README +++ b/README @@ -1,18 +1,18 @@ -Connectome Workbench requires QT4 to compile. Recommended is the latest 4.8.x release. The following configure options were used on linux for our builds: +Connectome Workbench requires QT5 to compile. Recommended is version 5.6 or later (due to an issue with the toolbar size on earlier versions). --system-zlib -webkit -qt-libmng -qt-libpng -qt-libtiff -qt-libjpeg -nomake demos -nomake examples -no-qt3support -no-dbus -opensource -exceptions -stl -no-multimedia -no-phonon -no-audio-backend -openssl +It is highly recommended to compile with OSMesa, which allow the -show-scene command and the "render pixmap" image capture method to work. Use it by setting the *cmake* variable WORKBENCH_MESA_DIR, such that ${WORKBENCH_MESA_DIR}/include/GL/osmesa.h exists. -Optionally, it can use OSMesa, which allows the -show-scene command to work. Use it by setting OSMESA_DIR as an environment variable, such that $OSMESA_DIR/include/GL/osmesa.h exists, before running cmake. - -It is compiled using cmake, on linux do: +It is compiled using cmake, for example, on linux you can do: mkdir build cd build -cmake ../workbench/src -D CMAKE_BUILD_TYPE=Release -make +cmake -D CMAKE_BUILD_TYPE=Release -D WORKBENCH_MESA_DIR=/usr -D WORKBENCH_USE_QT5=TRUE ../workbench/src +nice make -j8 For other OSes, see http://www.cmake.org/cmake/help/runningcmake.html +Note the -j flag of "make" is used in the example to speed up the build by compiling multiple source files at the same time. If you have a different number of cores/hardware threads or want to reduce the load, adjust it accordingly. The "nice" command is not needed, it is used to make the compilation have less priority than other processes. + This produces 3 executables, 2 of which are useful to the end user (Desktop/wb_view, CommandLine/wb_command), and one for running internal tests (Tests/test_driver). To run the (few) tests available: @@ -27,7 +27,7 @@ make install It should be noted that wb_import, provided in the HCP binary releases of Connectome Workbench, is actually part of caret5 (http://brainvis.wustl.edu/wiki/index.php/Caret:Download). -Connectome Workbench itself is licensed under GPLv2 or later, copyright 2014-2016 Washington University School of Medicine, see LICENSE file +Connectome Workbench itself is licensed under GPLv2 or later, copyright 2014-2017 Washington University School of Medicine, see LICENSE file Some source files are licensed under an MIT license (Expat), for easier code reuse in non-GPL projects: Files/SurfaceResamplingHelper.cxx @@ -87,3 +87,9 @@ rewritten for different argument types, modified Resources/FtglFonts/Vera*.ttf Copyright (c) 2003 Bitstream, Inc. licensed under Bitstream-Vera (basically, modification of fonts requires calling them something else), see debian/copyright file + +Files/PaletteFile.cxx +copied some palette definitions from matplotlib and its original sources +Copyright (c) 2012-2016 Matplotlib Development Team +Copyright (c) 2002 Cynthia Brewer, Mark Harrower, and The Pennsylvania State University +licensed apache-2 and MDT (bsd-like) diff --git a/README_BUGS.txt b/README_BUGS.txt new file mode 100644 index 0000000000000000000000000000000000000000..1a89ff6e5054d4932ba42190011b0ca46ecb4196 --- /dev/null +++ b/README_BUGS.txt @@ -0,0 +1,26 @@ + +********************************************************************************** + + +CMake Error in Tests/CMakeLists.txt: + No known features for CXX compiler + + "Clang" + + version 4.0.0. + +--------------------------------------------------------------------------------- + +CMake does not recognize the compiler and it fails to find the compiler's +features. We have seen this problem with Clang 4.0.0 and Qt 5.7.0 on +Mac OS X. It is a documented Qt bug. + +Qt Bug 5466: https://bugreports.qt.io/browse/QTBUG-54666 + +The solution with Qt 5.7.0 is: + (1) Edit /lib/cmake/Qt5Core/Qt5CoreConfigExtras.cmake + (2) Comment out the line "set_property(TARGET Qt5::Core PROPERTY INTERFACE_COMPILE_FEATURES cxx_decltype)" by placing a "#" at the beginning of the line. + +********************************************************************************** + + diff --git a/build_scripts/commandhtml.sh b/build_scripts/commandhtml.sh index 1680b1aebfc29843579d9a6c305ca80e5cad15bc..025d6ff8c06d301cc0ba48d3ddd2d8237ced91c2 100755 --- a/build_scripts/commandhtml.sh +++ b/build_scripts/commandhtml.sh @@ -36,45 +36,54 @@ infoLine=`echo "$initialText" | grep -n Information | cut -f1 -d:` #include -help line as plain text echo -n "$initialText" | head -n $((infoLine + 1)) >> "$outDir/$startPage" #-arguments-help +make_basic_command_page "-arguments-help" echo -n '' >> "$outDir/$startPage" echo "$initialText" | grep -- -arguments-help >> "$outDir/$startPage" echo -n '' >> "$outDir/$startPage" +#-global-options +make_basic_command_page "-global-options" +echo -n '' >> "$outDir/$startPage" +echo "$initialText" | grep -- -global-options >> "$outDir/$startPage" +echo -n '' >> "$outDir/$startPage" +#-parallel-help +make_basic_command_page "-parallel-help" +echo -n '' >> "$outDir/$startPage" +echo "$initialText" | grep -- -parallel-help >> "$outDir/$startPage" +echo -n '' >> "$outDir/$startPage" #-cifti-help +make_basic_command_page "-cifti-help" echo -n '' >> "$outDir/$startPage" echo "$initialText" | grep -- -cifti-help >> "$outDir/$startPage" echo -n '' >> "$outDir/$startPage" #-gifti-help +make_basic_command_page "-gifti-help" echo -n '' >> "$outDir/$startPage" echo "$initialText" | grep -- -gifti-help >> "$outDir/$startPage" echo -n '' >> "$outDir/$startPage" #-version echo "$initialText" | grep -- -version >> "$outDir/$startPage" #-list-commands +#special code to make page below echo -n '' >> "$outDir/$startPage" echo "$initialText" | grep -- -list-commands >> "$outDir/$startPage" echo -n '' >> "$outDir/$startPage" #-list-deprecated-commands +#again, special code below echo -n '' >> "$outDir/$startPage" echo "$initialText" | grep -- -list-deprecated-commands >> "$outDir/$startPage" echo -n '' >> "$outDir/$startPage" #-all-commands-help - takes 2 lines! +make_basic_command_page "-all-commands-help" echo -n '' >> "$outDir/$startPage" echo "$initialText" | grep -A 1 -- -all-commands-help >> "$outDir/$startPage" echo -n '' >> "$outDir/$startPage" #remainder of help info allCommandsLine=`echo "$initialText" | grep -n -- -all-commands-help | cut -f1 -d:` echo "$initialText" | tail -n +$((allCommandsLine+2)) >> "$outDir/$startPage" -#end page +#end main page echo '' >> "$outDir/$startPage" echo '' >> "$outDir/$startPage" -#-arguments-help page -make_basic_command_page "-arguments-help" -#-cifti-help page -make_basic_command_page "-cifti-help" -#-cifti-help page -make_basic_command_page "-gifti-help" - #-list-commands page, and its subpages outPage="$outDir/`command_to_page_name -list-commands`" #header @@ -90,6 +99,9 @@ do make_basic_command_page "$thisCommand" echo ''"${lines[$i]}"'' >> "$outPage" done +#end -list-commands page +echo '' >> "$outPage" +echo '' >> "$outPage" #-list-deprecated-commands page, and its subpages outPage="$outDir/`command_to_page_name -list-deprecated-commands`" @@ -106,11 +118,6 @@ do make_basic_command_page "$thisCommand" echo ''"${lines[$i]}"'' >> "$outPage" done - -#end main page +#end -list-deprecated-commands page echo '' >> "$outPage" echo '' >> "$outPage" - -#-all-commands-help page -make_basic_command_page "-all-commands-help" - diff --git a/build_scripts/linux32_remote.sh b/build_scripts/linux32_remote.sh deleted file mode 100755 index e70349d2af840429795e21f8c131d41eeacc41bb..0000000000000000000000000000000000000000 --- a/build_scripts/linux32_remote.sh +++ /dev/null @@ -1,7 +0,0 @@ -#!/bin/sh -BUILD=linux32 -BUILD_SERVER=linuxbuild - -BUILD_DIR=/home/caret/${BUILD}/caret7_dev -ssh -v caret@${BUILD_SERVER} "/bin/bash -c \"cd ${BUILD_DIR};./build32.sh caret\"" > $PWD/remote_launch_${BUILD}.txt 2>&1 -cat $PWD/remote_launch_${BUILD}.txt | mailx -s 'Caret7 Linux 32 Build Result' john@brainvis.wustl.edu tsc5yc@mst.edu diff --git a/build_scripts/linux64_remote.sh b/build_scripts/linux64_remote.sh deleted file mode 100755 index 001cc26b8f4f4b4950b5e466ff3b940576af38ef..0000000000000000000000000000000000000000 --- a/build_scripts/linux64_remote.sh +++ /dev/null @@ -1,8 +0,0 @@ -#!/bin/sh -BUILD=linux64 -BUILD_SERVER=linuxbuild - -BUILD_DIR=/home/caret/${BUILD}/caret7_dev -ssh -v caret@${BUILD_SERVER} "/bin/bash -c \"cd ${BUILD_DIR};./build64.sh caret\"" > $PWD/remote_launch_${BUILD}.txt 2>&1 -cat $PWD/remote_launch_${BUILD}.txt | mailx -s 'Caret7 Linux 64 Build Result' john@brainvis.wustl.edu tsc5yc@mst.edu - diff --git a/build_scripts/linux64_rh.sh b/build_scripts/linux64_rh.sh deleted file mode 100755 index f34edea65a1da42eed3bfe1d89635e595b1fe076..0000000000000000000000000000000000000000 --- a/build_scripts/linux64_rh.sh +++ /dev/null @@ -1,39 +0,0 @@ -#!/bin/sh - -# -# This file is executed by myelin1. -# -# -# (1) Update source code from repository -# (2) Build Caret programs -# (3) Copy executables into the distribution -# - -# -# File for capturing standard error -# -BUILD_ROOT_DIR=/home/caret/caret7_dev -ERROR_FILE=${BUILD_ROOT_DIR}/result_build_linux64_rh.txt -rm -f ${ERROR_FILE} - -# -# If something fails, keep going -# -set noon - -# -# Run the build script -# -echo "Starting linux64 Redhat aux script" >> ${ERROR_FILE} 2>&1 -${BUILD_ROOT_DIR}/caret7_source/build_scripts/linux64_rh_aux.sh >> ${ERROR_FILE} 2>&1 -echo "Finished linux64 Redhat script" >> ${ERROR_FILE} 2>&1 - -# -# Send output as email -# -MAIL_ERROR_FILE=${BUILD_ROOT_DIR}/mail_result_build_linux64_rh.txt -rm -f ${MAIL_ERROR_FILE} -cat ${ERROR_FILE} | mail -v -s 'Caret7 Linux 64 Redhat Build Result' john@brainvis.wustl.edu tsc5yc@mst.edu >> ${MAIL_ERROR_FILE} 2>&1 - -echo "Sent mail" - diff --git a/build_scripts/linux64_rh_aux.sh b/build_scripts/linux64_rh_aux.sh deleted file mode 100755 index 6f285e8a0910b1b77e6def4f58a398c87bf14c22..0000000000000000000000000000000000000000 --- a/build_scripts/linux64_rh_aux.sh +++ /dev/null @@ -1,52 +0,0 @@ -#!/bin/sh - -# -# This file is executed on the redhat build machine -# -# -# (1) Update source code from repository -# (2) Build Caret programs -# (3) Copy executables into the distribution -# -# Based off Tim Coalson's "buildbot" script -# - -BUILD_ROOT_DIR=/home/caret/caret7_dev -GIT_ROOT_DIR=${BUILD_ROOT_DIR}/caret7_source -BUILD_DIR=${BUILD_ROOT_DIR}/build -cd ${BUILD_ROOT_DIR} -echo "BUILD_DIR: ${BUILD_DIR}" - -# -# Go into git checkout directory -# -cd ${GIT_ROOT_DIR} - - -# -# Update source from repository -# -echo "UPDATING SOURCE FROM GIT REPOSITORY" -git fetch -git reset --hard origin/master - -# -# Build, assume the build directory is already set up as desired -# -echo "BUILDING SOURCE" -cd ${BUILD_DIR} -make -j2 - -# -# Copy to distribution directory -# -echo "COPYING PROGRAMS" -DIST_DIR=/mainpool/storage/distribution/caret7_distribution/workbench/exe_rh_linux64 -scp -v Desktop/wb_view caret@myelin1.wustl.edu:${DIST_DIR} -scp -v CommandLine/wb_command caret@myelin1.wustl.edu:${DIST_DIR} -scp -v ${GIT_ROOT_DIR}/src/CommandLine/wb_shortcuts caret@myelin1.wustl.edu:/mainpool/storage/distribution/caret7_distribution/workbench/bin_rh_linux64 -#put the completion into the exe directory, because it isn't useful to run it directly (and would make wb_comm behave worse) -scp -v ${GIT_ROOT_DIR}/src/CommandLine/bashcomplete_wb_command caret@myelin1.wustl.edu:${DIST_DIR} - -echo "SCRIPT COMPLETED SUCCESSFULLY" - diff --git a/build_scripts/linux64_rh_remote.sh b/build_scripts/linux64_rh_remote.sh deleted file mode 100755 index 50b2e9981ffef1e7e84d76c686aea05638bfb5a7..0000000000000000000000000000000000000000 --- a/build_scripts/linux64_rh_remote.sh +++ /dev/null @@ -1,7 +0,0 @@ -#!/bin/sh -BUILD=linux64_rh -BUILD_SERVER=login1.chpc.wustl.edu - -#BUILD_DIR=/home/caret/caret7_dev/${BUILD} -BUILD_DIR=/home/caret/caret7_dev -ssh -v caret@${BUILD_SERVER} ${BUILD_DIR}/caret7_source/build_scripts/${BUILD}.sh $1 > $PWD/remote_launch_${BUILD}.txt 2>&1 diff --git a/build_scripts/mac32.sh b/build_scripts/mac32.sh deleted file mode 100755 index 64eb448e846041d3e62e84991738797e05351466..0000000000000000000000000000000000000000 --- a/build_scripts/mac32.sh +++ /dev/null @@ -1,39 +0,0 @@ -#!/bin/sh - -# -# This file is executed by (via "launchd") -# /System/Library/LaunchDaemons/edu.wustl.caret.build.mac32.plist -# -# (1) Update source code from repository -# (2) Build Caret programs -# (3) Copy executables into the distribution -# - -# -# File for capturing standard error -# -BUILD_ROOT_DIR=/Users/caret/caret7_development/mac32 -ERROR_FILE=${BUILD_ROOT_DIR}/result_build_mac32.txt -rm -f ${ERROR_FILE} - -# -# If something fails, keep going -# -set noon - -# -# Run the build script -# -echo "Starting mac32 aux script" >> ${ERROR_FILE} 2>&1 -${BUILD_ROOT_DIR}/caret7_source/build_scripts/mac32_aux.sh >> ${ERROR_FILE} 2>&1 -echo "Finished mac32 aux script" >> ${ERROR_FILE} 2>&1 - -# -# Send output as email -# -MAIL_ERROR_FILE=${BUILD_ROOT_DIR}/mail_result_build_mac32.txt -rm -f ${MAIL_ERROR_FILE} -cat ${ERROR_FILE} | mail -v -s 'Caret7 Mac 32 Build Result' john@brainvis.wustl.edu tsc5yc@mst.edu >> ${MAIL_ERROR_FILE} 2>&1 - -echo "Sent mail" - diff --git a/build_scripts/mac32_aux.sh b/build_scripts/mac32_aux.sh deleted file mode 100755 index 58f7829a0e4c71e34d60598916b04ebcfffbb164..0000000000000000000000000000000000000000 --- a/build_scripts/mac32_aux.sh +++ /dev/null @@ -1,79 +0,0 @@ -#!/bin/sh - -# -# This file is executed by (via "launchd") -# /System/Library/LaunchDaemons/edu.wustl.caret.build.mac32.plist -# -# (1) Update source code from repository -# (2) Build Caret programs -# (3) Copy executables into the distribution -# -# Based off Tim Coalson's "buildbot" script -# - -# -# Go to correct directory -# -BUILD_ROOT_DIR=/Users/caret/caret7_development/mac32 -GIT_ROOT_DIR=${BUILD_ROOT_DIR}/caret7_source -SRC_DIR=${GIT_ROOT_DIR}/src -BUILD_DIR=${BUILD_ROOT_DIR}/build -cd ${BUILD_ROOT_DIR} -echo "BUILD_DIR: ${BUILD_DIR}" - -# -# Use Qt SDK -# -QTDIR=/Users/caret/QtSDK/Desktop/Qt/474/gcc -export QTDIR -echo "QTDIR: ${QTDIR}" - -PATH=${QTDIR}/bin:${PATH} -export PATH -QMAKE_PROG=`which qmake` -echo "QMAKE: ${QMAKE_PROG}" - -# -# Go into git checkout directory -# -cd ${GIT_ROOT_DIR} - - -# -# Update source from repository -# -echo "UPDATING SOURCE FROM GIT REPOSITORY" -git reset --hard HEAD -git pull -u - -# -# Configure and build as release. -# Build twice so that if the build fails, the second build will -# contain just the output that shows the errors. -# Catch output and echo to screen. -# -echo "BUILDING SOURCE" -mkdir -p ${BUILD_DIR} -cd ${BUILD_DIR} -cmake ${SRC_DIR} -make -j2 -make -j2 - -# -# Run 'macdeployqt' on the App so that frameworks are copied -# -echo "RUNNING MACDEPLOYQT" -macdeployqt Desktop/wb_view.app - -# -# Copy to distribution directory -# -echo "COPYING PROGRAMS" -DIST_DIR=/mainpool/storage/distribution/caret7_distribution/workbench/macosx32_apps -scp -rv Desktop/wb_view.app caret@myelin1:${DIST_DIR} -scp -v CommandLine/wb_command caret@myelin1:${DIST_DIR} -scp -v ${SRC_DIR}/CommandLine/wb_shortcuts caret@myelin1:${DIST_DIR} -scp -v ${SRC_DIR}/CommandLine/bashcomplete_wb_command caret@myelin1:${DIST_DIR} - -echo "SCRIPT COMPLETED SUCCESSFULLY" - diff --git a/build_scripts/mac32_remote.sh b/build_scripts/mac32_remote.sh deleted file mode 100755 index 1c351b468deb313f430920b5664ec4c1cde0c0c1..0000000000000000000000000000000000000000 --- a/build_scripts/mac32_remote.sh +++ /dev/null @@ -1,6 +0,0 @@ -#!/bin/sh -BUILD=mac32 -BUILD_SERVER=hippocampus.wustl.edu - -BUILD_DIR=/Users/caret/caret7_development/${BUILD} -ssh -v caret@${BUILD_SERVER} ${BUILD_DIR}/caret7_source/build_scripts/${BUILD}.sh $1 > $PWD/remote_launch_${BUILD}.txt 2>&1 diff --git a/build_scripts/mac64.sh b/build_scripts/mac64.sh deleted file mode 100755 index 6e636ee767cfd8e5140735085ad25ff447f16f39..0000000000000000000000000000000000000000 --- a/build_scripts/mac64.sh +++ /dev/null @@ -1,39 +0,0 @@ -#!/bin/sh - -# -# This file is executed by (via "launchd") -# /System/Library/LaunchDaemons/edu.wustl.caret.build.mac64.plist -# -# (1) Update source code from repository -# (2) Build Caret programs -# (3) Copy executables into the distribution -# - -# -# File for capturing standard error -# -BUILD_ROOT_DIR=/Users/caret/caret7_development/mac64 -ERROR_FILE=${BUILD_ROOT_DIR}/result_build_mac64.txt -rm -f ${ERROR_FILE} - -# -# If something fails, keep going -# -set noon - -# -# Run the build script -# -echo "Starting mac64 aux script" >> ${ERROR_FILE} 2>&1 -${BUILD_ROOT_DIR}/caret7_source/build_scripts/mac64_aux.sh >> ${ERROR_FILE} 2>&1 -echo "Finished mac64 aux script" >> ${ERROR_FILE} 2>&1 - -# -# Send output as email -# -MAIL_ERROR_FILE=${BUILD_ROOT_DIR}/mail_result_build_mac64.txt -rm -f ${MAIL_ERROR_FILE} -cat ${ERROR_FILE} | mail -v -s 'Caret7 Mac 64 Build Result' john@brainvis.wustl.edu tsc5yc@mst.edu >> ${MAIL_ERROR_FILE} 2>&1 - -echo "Sent mail" - diff --git a/build_scripts/mac64_aux.sh b/build_scripts/mac64_aux.sh deleted file mode 100755 index a3411374e5ae7c59a6691a2becf912f4dafc9d6d..0000000000000000000000000000000000000000 --- a/build_scripts/mac64_aux.sh +++ /dev/null @@ -1,111 +0,0 @@ -#!/bin/sh - -# -# This file is executed by (via "launchd") -# /System/Library/LaunchDaemons/edu.wustl.caret.build.mac64.plist -# -# (1) Update source code from repository -# (2) Build Caret programs -# (3) Copy executables into the distribution -# -# Based off Tim Coalson's "buildbot" script -# - -# -# Go to correct directory -# -BUILD_ROOT_DIR=/Users/caret/caret7_development/mac64 -GIT_ROOT_DIR=${BUILD_ROOT_DIR}/caret7_source -SRC_DIR=${GIT_ROOT_DIR}/src -BUILD_DIR=${BUILD_ROOT_DIR}/build -cd ${BUILD_ROOT_DIR} -echo "BUILD_DIR: ${BUILD_DIR}" - -# -# Setup Environment -# - -# -# Use Qt SDK -# -#QTDIR=/Users/caret/QtSDK/Desktop/Qt/474/gcc -# has SSL linked -QTDIR=/opt/caret64_sdk/install/qt-4.8.3 -export QTDIR -echo "QTDIR: ${QTDIR}" - -PATH=${QTDIR}/bin:${PATH} -export PATH -QMAKE_PROG=`which qmake` -echo "QMAKE: ${QMAKE_PROG}" - -# -# Go into git checkout directory -# -cd ${GIT_ROOT_DIR} - - -# -# Update source from repository -# -echo "UPDATING SOURCE FROM GIT REPOSITORY" -git reset --hard HEAD -git pull -u - -# -# Configure and build as release. -# Build twice so that if the build fails, the second build will -# contain just the output that shows the errors. -# Catch output and echo to screen. -# -#CC_COMPILER=/usr/local/clang-llvm/clang+llvm-3.2-x86_64-apple-darwin11/bin/clang -#CXX_COMPILER=/usr/local/clang-llvm/clang+llvm-3.2-x86_64-apple-darwin11/bin/clang++ -#CC_COMPILER=/usr/bin/gcc -#CXX_COMPILER=/usr/bin/g++ -#CC_COMPILER=/usr/local/gcc-4.9.3/bin/gcc -#CXX_COMPILER=/usr/local/gcc-4.9.3/bin/g++ - -# -# Clang compiler with OpenMP -# -CC_COMPILER=/usr/local/clang-openmp-opt/llvm/build/Release/bin/clang2 -CXX_COMPILER=/usr/local/clang-openmp-opt/llvm/build/Release/bin/clang2++ -OPENMP_COMPILE_OPTION=-fopenmp -export OPENMP_COMPILE_OPTION -OPENMP_HEADER_DIR=/usr/local/clang-openmp-opt/llvm/build/Release/include -export OPENMP_HEADER_DIR -OPENMP_LIB_DIR=/usr/local/clang-openmp-opt/llvm/build/Release/lib -export OPENMP_LIB_DIR - - -echo "BUILDING SOURCE" -mkdir -p ${BUILD_DIR} -cd ${BUILD_DIR} -/usr/local/cmake-3.3.1/bin/cmake \ - -DCMAKE_BUILD_TYPE=Release \ - -DCMAKE_C_COMPILER=${CC_COMPILER} \ - -DCMAKE_CXX_COMPILER=${CXX_COMPILER} \ - -DCMAKE_VERBOSE_MAKEFILE=FALSE \ - ${SRC_DIR} -make -j2 -make -j2 - -# -# Run 'macdeployqt' on the Apps so that frameworks are copied -# -echo "RUNNING MACDEPLOYQT" -macdeployqt Desktop/wb_view.app -verbose=0 -macdeployqt CommandLine/wb_command.app -verbose=0 - -# -# Copy to distribution directory -# -echo "COPYING PROGRAMS" -DIST_DIR=/mainpool/storage/distribution/caret7_distribution/workbench/macosx64_apps -scp -rv Desktop/wb_view.app caret@myelin1:${DIST_DIR} -scp -rv CommandLine/wb_command.app caret@myelin1:${DIST_DIR} -scp -v ${SRC_DIR}/CommandLine/wb_shortcuts caret@myelin1:/mainpool/storage/distribution/caret7_distribution/workbench/bin_macosx64 -scp -v ${SRC_DIR}/CommandLine/bashcomplete_wb_command caret@myelin1:${DIST_DIR} - -echo "SCRIPT COMPLETED SUCCESSFULLY" - diff --git a/build_scripts/mac64_noodle.sh b/build_scripts/mac64_noodle.sh deleted file mode 100755 index 0d5ec1a47c3d364d94af8dc58182cecd04075b1f..0000000000000000000000000000000000000000 --- a/build_scripts/mac64_noodle.sh +++ /dev/null @@ -1,35 +0,0 @@ -#!/bin/sh - -# -# -# - -# -# File for capturing standard error -# -BUILD_ROOT_DIR=/Users/caret/caret7_autobuild -ERROR_FILE=${BUILD_ROOT_DIR}/result_build_mac64_noodle.txt -rm -f ${ERROR_FILE} - -# -# If something fails, keep going -# -set noon - -# -# Run the build script -# -echo "Starting mac64 noodle aux script" >> ${ERROR_FILE} 2>&1 -${BUILD_ROOT_DIR}/caret7_source/build_scripts/mac64_noodle_aux.sh >> ${ERROR_FILE} 2>&1 -echo "Finished mac64 noodle aux script" >> ${ERROR_FILE} 2>&1 - -# -# Send output as email -# -MAIL_ERROR_FILE=${BUILD_ROOT_DIR}/mail_result_build_mac64_noodle.txt -rm -f ${MAIL_ERROR_FILE} -#cat ${ERROR_FILE} | mail -v -s 'Caret7 Mac 64 Noodle Build Result' john@brainvis.wustl.edu tsc5yc@mst.edu >> ${MAIL_ERROR_FILE} 2>&1 -cat ${ERROR_FILE} | mail -v -s 'Caret7 Mac 64 Noodle Build Result' john@brainvis.wustl.edu >> ${MAIL_ERROR_FILE} 2>&1 - -echo "Sent mail" - diff --git a/build_scripts/mac64_noodle_aux.sh b/build_scripts/mac64_noodle_aux.sh deleted file mode 100755 index 78059f0b6fe0241fc938d714fc3c6ff6a5f9a196..0000000000000000000000000000000000000000 --- a/build_scripts/mac64_noodle_aux.sh +++ /dev/null @@ -1,126 +0,0 @@ -#!/bin/sh - -# -# (1) Update source code from repository -# (2) Build Caret programs -# (3) Copy executables into the distribution -# -# Based off Tim Coalson's "buildbot" script -# - -# -# Go to correct directory -# -BUILD_ROOT_DIR=/Users/caret/caret7_autobuild -GIT_ROOT_DIR=${BUILD_ROOT_DIR}/caret7_source -SRC_DIR=${GIT_ROOT_DIR}/src -BUILD_SCRIPT_DIR=${GIT_ROOT_DIR}/build_scripts -#BUILD_SCRIPT_DIR=/Users/caret/develop_build_script -BUILD_DIR=${BUILD_ROOT_DIR}/build -cd ${BUILD_ROOT_DIR} -echo "BUILD_DIR: ${BUILD_DIR}" - -# -# Setup Environment -# - -# -# Use Qt SDK -# -QTDIR=/opt/caret64_sdk/install/qt-4.8.7/bin/qmake -export QTDIR -echo "QTDIR: ${QTDIR}" - -PATH=${QTDIR}/bin:${PATH} -export PATH -QMAKE_PROG=`which qmake` -echo "QMAKE: ${QMAKE_PROG}" - -# -# Go into git checkout directory -# -cd ${GIT_ROOT_DIR} - - -# -# Update source from repository -# -echo "UPDATING SOURCE FROM GIT REPOSITORY" -git reset --hard HEAD -git pull -u - - -# -# Clang compiler -# -# OpenMP is added to compiler and environment -# variables must be set so compiler finds OpenMP. -# -CC_COMPILER=/opt/local/bin/clang-mp-3.7 -CXX_COMPILER=/opt/local/bin/clang++-mp-3.7 -OPENMP_COMPILE_OPTION="-fopenmp -D_OPENMP" -export OPENMP_COMPILE_OPTION -OPENMP_HEADER_DIR=/opt/local/include/libomp -export OPENMP_HEADER_DIR -OPENMP_LIB_DIR=/opt/local/lib/libomp -export OPENMP_LIB_DIR - -# -# Mesa Library for Offscreen OpenGL -# -OSMESA_DIR=/opt/caret64_sdk/install/Mesa-7.8.2 - -# -# CMake executable path -# -CMAKE_EXE=/opt/caret64_sdk/install/cmake-3.5.2/bin/cmake - -# -# Configure with CMake -# -echo "BUILDING SOURCE" -mkdir -p ${BUILD_DIR} -cd ${BUILD_DIR} -${CMAKE_EXE} \ - -DCMAKE_BUILD_TYPE=Release \ - -DCMAKE_C_COMPILER=${CC_COMPILER} \ - -DCMAKE_CXX_COMPILER=${CXX_COMPILER} \ - -DCMAKE_VERBOSE_MAKEFILE=FALSE \ - -DCMAKE_OSX_DEPLOYMENT_TARGET=10.7 \ - -DCMAKE_OSX_SYSROOT="/Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX10.7.sdk" \ - ${SRC_DIR} - -# -# Run Make to build. -# It is run twice so that errors will be at end of log file -# -make -j2 -make -j - -# -# Run 'macdeployqt' on the Apps so that Qt frameworks are copied -# -echo "RUNNING MACDEPLOYQT" -macdeployqt Desktop/wb_view.app -verbose=0 -macdeployqt CommandLine/wb_command.app -verbose=0 - -# -# Need to copy OpenMP library to wb_view and wb_command -# -${BUILD_SCRIPT_DIR}/mac64_copy_library_to_workbench.sh \ - ${OPENMP_LIB_DIR}/libomp.dylib \ - ${BUILD_DIR}/Desktop/wb_view.app/Contents/MacOS/wb_view -${BUILD_SCRIPT_DIR}/mac64_copy_library_to_workbench.sh \ - ${OPENMP_LIB_DIR}/libomp.dylib \ - ${BUILD_DIR}/CommandLine/wb_command.app/Contents/MacOS/wb_command - -# -# Copy to distribution directory -# -echo "COPYING PROGRAMS" -DIST_DIR=/mainpool/storage/distribution/caret7_distribution/workbench/macosx64_noodle_apps -scp -r Desktop/wb_view.app caret@myelin1:${DIST_DIR} -scp -r CommandLine/wb_command.app caret@myelin1:${DIST_DIR} - -echo "SCRIPT COMPLETED SUCCESSFULLY" - diff --git a/build_scripts/mac64_noodle_remote.sh b/build_scripts/mac64_noodle_remote.sh deleted file mode 100755 index 4ec2af81b32cd0ad38cb7f41dd0abe6aeeb05bc8..0000000000000000000000000000000000000000 --- a/build_scripts/mac64_noodle_remote.sh +++ /dev/null @@ -1,6 +0,0 @@ -#!/bin/sh -BUILD=mac64_noodle -BUILD_SERVER=noodle.wustl.edu - -BUILD_DIR=/Users/caret/caret7_autobuild -ssh -v caret@${BUILD_SERVER} ${BUILD_DIR}/caret7_source/build_scripts/${BUILD}.sh $1 > $PWD/remote_launch_${BUILD}.txt 2>&1 diff --git a/build_scripts/mac64_remote.sh b/build_scripts/mac64_remote.sh deleted file mode 100755 index 517d689ee9378e34930dbbefee56d9ebf71f79e8..0000000000000000000000000000000000000000 --- a/build_scripts/mac64_remote.sh +++ /dev/null @@ -1,6 +0,0 @@ -#!/bin/sh -BUILD=mac64 -BUILD_SERVER=hippocampus.wustl.edu - -BUILD_DIR=/Users/caret/caret7_development/${BUILD} -ssh -v caret@${BUILD_SERVER} ${BUILD_DIR}/caret7_source/build_scripts/${BUILD}.sh $1 > $PWD/remote_launch_${BUILD}.txt 2>&1 diff --git a/build_scripts/windows32/build.bat b/build_scripts/windows32/build.bat index d14768f56901b685ee673d926e636ee9c206a970..9cd97333b6ea4236d1a02e3dd21259da46d7494e 100755 --- a/build_scripts/windows32/build.bat +++ b/build_scripts/windows32/build.bat @@ -6,7 +6,20 @@ REM rd /q/s %DIR%\caret7_source\build md %DIR%\caret7_source\build cd /d %DIR%\caret7_source\build REMcmake -G "NMake Makefiles JOM" -DCMAKE_BUILD_TYPE=Release ../src -cmake -DFREETYPE_INCLUDE_DIR_freetype2=C:\dev32\install\FreeType-2.4.8\include\freetype2 -DFREETYPE_INCLUDE_DIR_ft2build=C:\dev32\install\FreeType-2.4.8\include -DFREETYPE_LIBRARY=C:\dev32\install\FreeType-2.4.8\lib\freetype.lib -G "NMake Makefiles JOM" -DCMAKE_BUILD_TYPE=Release ../src + +REM -DFREETYPE_INCLUDE_DIR_freetype2=C:\dev32\install\FreeType-2.4.8\include\freetype2 +REM -DFREETYPE_INCLUDE_DIR_ft2build=C:\dev32\install\FreeType-2.4.8\include +REM -DFREETYPE_LIBRARY=C:\dev32\install\FreeType-2.4.8\lib\freetype.lib + + +cmake ^ + -DWORKBENCH_FREETYPE_DIR=C:\dev32\install\FreeType-2.4.8\lib ^ + -DZLIB_ROOT=C:\dev32\install\zlib ^ + -G "NMake Makefiles JOM" ^ + -DCMAKE_BUILD_TYPE=Release ^ + ../src + jom -j8 cd /d %OLD_DIR% set OLD_DIR= + diff --git a/build_scripts/windows32_remote.sh b/build_scripts/windows32_remote.sh index 3e8fbe4d2c6a541627584641151c0e2f8a1a491b..69633f8e37019c6bbe442cd9eaf215d73326860f 100755 --- a/build_scripts/windows32_remote.sh +++ b/build_scripts/windows32_remote.sh @@ -1,4 +1,4 @@ #!/bin/sh -ssh -v caret@windowsbuild 'c:\dev7\windows32\caret7_source\build_scripts\windows32.bat' > $PWD/remote_launch_windows32.txt 2>&1 +ssh -v caret@winbuild 'c:\dev7\windows32\caret7_source\build_scripts\windows32.bat' > $PWD/remote_launch_windows32.txt 2>&1 cat $PWD/remote_launch_windows32.txt | mailx -s 'Caret7 Windows 32 Build Result' john@brainvis.wustl.edu tsc5yc@mst.edu diff --git a/build_scripts/windows64.bat b/build_scripts/windows64.bat deleted file mode 100755 index 7bd7717212ed2c13750f633099a19a7e3e3c6d16..0000000000000000000000000000000000000000 --- a/build_scripts/windows64.bat +++ /dev/null @@ -1,37 +0,0 @@ -@echo=off -set CMD=c:\Windows\system32\cmd.exe -REM -REM Go to correct directory -REM -set BUILD_DIR=c:\dev7\windows64 -cd %BUILD_DIR% - -REM -REM Go into source directory -REM -cd caret7_source - -echo "Caret7 Windows64 Build Result" - -REM -REM Grab the latest Sources -REM -c:\cygwin\bin\git.exe reset --hard HEAD -c:\cygwin\bin\git.exe fetch -c:\cygwin\bin\git.exe reset --hard origin/master - -REM -REM Build caret -REM -cd build_scripts/windows64 -%CMD% /c build.bat -cd .. -cd .. -set DIST_DIR=caret@myelin1:/mainpool/storage/distribution/caret7_distribution/workbench/bin_windows64 - -echo "Copying Files" -c:\cygwin\bin\scp build/Desktop/wb_view.exe %DIST_DIR% -c:\cygwin\bin\scp build/CommandLine/wb_command.exe %DIST_DIR% -c:\cygwin\bin\scp src/CommandLine/wb_shortcuts %DIST_DIR% -c:\cygwin\bin\scp src/CommandLine/bashcomplete_wb_command %DIST_DIR% -echo "Finished Copying Files" diff --git a/build_scripts/windows64/README.txt b/build_scripts/windows64/README.txt deleted file mode 100644 index 6d8910cad16e879025ce69dc7cb95a89ac76a068..0000000000000000000000000000000000000000 --- a/build_scripts/windows64/README.txt +++ /dev/null @@ -1,3 +0,0 @@ -setup_env.bat -sets up the build environment -build.bat -sets up environment, runs cmake, and builds the release version of caret -c7env64.bat - sets up environment diff --git a/build_scripts/windows64/build.bat b/build_scripts/windows64/build.bat deleted file mode 100755 index 546f32f3231a6480e943b9ab51521e9c51692206..0000000000000000000000000000000000000000 --- a/build_scripts/windows64/build.bat +++ /dev/null @@ -1,17 +0,0 @@ -@echo off -if [%MACHINE%]==[] call setup_env.bat - -set OLD_DIR=%CD% -md %DIR%\caret7_source\build -cd /d %DIR%\caret7_source\build -cmake ^ - -DFREETYPE_INCLUDE_DIR_freetype2=C:\dev64\install\FreeType-2.4.8\include\freetype2 ^ - -DFREETYPE_INCLUDE_DIR_ft2build=C:\dev64\install\FreeType-2.4.8\include ^ - -DFREETYPE_LIBRARY=C:\dev64\install\FreeType-2.4.8\lib\freetype.lib ^ - -G "NMake Makefiles JOM" ^ - -DCMAKE_BUILD_TYPE=Release ^ - ../src - -jom -j8 -cd /d %OLD_DIR% -set OLD_DIR= diff --git a/build_scripts/windows64/build_debug.bat b/build_scripts/windows64/build_debug.bat deleted file mode 100755 index b53f4294be907904d15c07c9ee6dc1dc6f17f068..0000000000000000000000000000000000000000 --- a/build_scripts/windows64/build_debug.bat +++ /dev/null @@ -1,11 +0,0 @@ -@echo off -if [%MACHINE%]==[] call setup_env.bat - -set OLD_DIR=%CD% -REM rd /q/s %DIR%\caret7_source\build_debug -md %DIR%\caret7_source\build_debug -cd /d %DIR%\caret7_source\build_debug -cmake -G "NMake Makefiles JOM" -DCMAKE_BUILD_TYPE=Debug ../src -jom -j8 -cd /d %OLD_DIR% -set OLD_DIR= diff --git a/build_scripts/windows64/c7env64.bat b/build_scripts/windows64/c7env64.bat deleted file mode 100755 index af814c251fbbf6637a2673b56b25933eacf5cbce..0000000000000000000000000000000000000000 --- a/build_scripts/windows64/c7env64.bat +++ /dev/null @@ -1,7 +0,0 @@ -REM set PATH=C:\dev64\install\Qt481\bin;C:\Qt\qtcreator-2.4.1\bin;%PATH% -REM set QTDIR=C:\dev64\install\Qt481 -set QTDIR=C:\dev64\install\qt-4.8.7 -set PATH=%QTDIR%\bin;C:\Qt\qtcreator-2.4.1\bin;%PATH% -set ZLIB_LIB_DIR=C:\dev64\install\zlib\lib -set ZLIB_INC_DIR=C:\dev64\install\zlib\include -set FREETYPE_DIR=C:\dev64\install\FreeType-2.4.8 diff --git a/build_scripts/windows64/c7env64.vbs b/build_scripts/windows64/c7env64.vbs deleted file mode 100755 index 646bd92da2c537db3614c2e65638b269a6386c7d..0000000000000000000000000000000000000000 --- a/build_scripts/windows64/c7env64.vbs +++ /dev/null @@ -1,21 +0,0 @@ -Set objShell = WScript.CreateObject("WScript.Shell") -Set colUsrEnvVars = objShell.Environment("USER") - -QtPath = "C:\dev64\install\Qt\bin" - -Path = colUsrEnvVars("OLDPATH") - -If Len(Path) = 0 Then - colUsrEnvVars("OLDPATH") = colUsrEnvVars("PATH") - 'objShell.ExpandEnvironmentStrings("%PATH%") - WScript.Echo colUsrEnvVars("OLDPATH") - Path = colUsrEnvVars("OLDPATH") -End If - -NewPath = (QtPath & ";" & Path) -colUsrEnvVars("PATH") = (NewPath) - -colUsrEnvVars("QTDIR") = "C:\dev64\install\Qt" - -colUsrEnvVars("ZLIB_LIB_DIR") = "C:\dev64\install\zlib\lib" -colUsrEnvVars("ZLIB_INC_DIR") = "C:\dev64\install\zlib\include" \ No newline at end of file diff --git a/build_scripts/windows64/env.bat b/build_scripts/windows64/env.bat deleted file mode 100755 index 3aee5c10600edcb1cf8e5badd8ca76c7e01d209b..0000000000000000000000000000000000000000 --- a/build_scripts/windows64/env.bat +++ /dev/null @@ -1,21 +0,0 @@ -@echo off -rem isn't batch awesome, no else statments... -IF [%1]==[] ( - set DIR "." -) -IF NOT [%1]==[] ( -set DIR=%1 -) -IF [%2]==[] ( - set MACHINE X86 -) -IF NOT [%2]==[] ( -set MACHINE=%2 -) - -IF NOT [%INITVS%]==[%MACHINE%] ( - echo "Setting %MACHINE% bit compiler variables" - set INITVS=%MACHINE% - call "c:\Program Files (x86)\Microsoft Visual Studio 10.0\VC\vcvarsall.bat" %MACHINE% - CD %DIR% -) \ No newline at end of file diff --git a/build_scripts/windows64/launchcmdvs.bat b/build_scripts/windows64/launchcmdvs.bat deleted file mode 100755 index dcf238c305831059bae7579e016cee5a9c4c5350..0000000000000000000000000000000000000000 --- a/build_scripts/windows64/launchcmdvs.bat +++ /dev/null @@ -1,3 +0,0 @@ -call c7env64.bat -call env.bat "c:\dev\git\caret7_source" x64 -cmd.exe diff --git a/build_scripts/windows64/launchqtcreatorvs.bat b/build_scripts/windows64/launchqtcreatorvs.bat deleted file mode 100755 index f10593a959347f3fa51acf3dc189121578476a89..0000000000000000000000000000000000000000 --- a/build_scripts/windows64/launchqtcreatorvs.bat +++ /dev/null @@ -1,3 +0,0 @@ -call c7env64.bat -call env.bat "c:\dev\git\caret7_source" x64 -C:\Qt\qtcreator-2.7.1\bin\qtcreator.exe diff --git a/build_scripts/windows64/launchvs.bat b/build_scripts/windows64/launchvs.bat deleted file mode 100755 index b948bf0f8d6f92e7147e7f8e2e1ffe27dce66d45..0000000000000000000000000000000000000000 --- a/build_scripts/windows64/launchvs.bat +++ /dev/null @@ -1,3 +0,0 @@ -call c7env64.bat -call env.bat "c:\dev7\windows64" x64 -devenv.exe diff --git a/build_scripts/windows64/setup_env.bat b/build_scripts/windows64/setup_env.bat deleted file mode 100755 index 7c2061860088def3a101665329772b1d274fae1a..0000000000000000000000000000000000000000 --- a/build_scripts/windows64/setup_env.bat +++ /dev/null @@ -1,2 +0,0 @@ -call c7env64.bat -call env.bat "c:\dev7\windows64" AMD64 \ No newline at end of file diff --git a/build_scripts/windows6411/README.txt b/build_scripts/windows6411/README.txt deleted file mode 100644 index 6d8910cad16e879025ce69dc7cb95a89ac76a068..0000000000000000000000000000000000000000 --- a/build_scripts/windows6411/README.txt +++ /dev/null @@ -1,3 +0,0 @@ -setup_env.bat -sets up the build environment -build.bat -sets up environment, runs cmake, and builds the release version of caret -c7env64.bat - sets up environment diff --git a/build_scripts/windows6411/build.bat b/build_scripts/windows6411/build.bat deleted file mode 100755 index f0d100316a40efe44e339904e16fcebfc7f7cc20..0000000000000000000000000000000000000000 --- a/build_scripts/windows6411/build.bat +++ /dev/null @@ -1,11 +0,0 @@ -@echo off -if [%MACHINE%]==[] call setup_env.bat - -set OLD_DIR=%CD% -REM rd /q/s %DIR%\caret7_source\build -md %DIR%\caret7_source\build -cd /d %DIR%\caret7_source\build -cmake -G "NMake Makefiles JOM" -DCMAKE_BUILD_TYPE=Release ../src -jom -j8 -cd /d %OLD_DIR% -set OLD_DIR= \ No newline at end of file diff --git a/build_scripts/windows6411/c7env64.bat b/build_scripts/windows6411/c7env64.bat deleted file mode 100755 index be2ce630348a040a4bc3e38346ab05fe0d4db694..0000000000000000000000000000000000000000 --- a/build_scripts/windows6411/c7env64.bat +++ /dev/null @@ -1,4 +0,0 @@ -set PATH=C:\dev6411\install\Qt482\bin;C:\Qt\qtcreator-2.4.1\bin;%PATH% -set QTDIR=C:\dev6411\install\Qt482 -set ZLIB_LIB_DIR=C:\dev6411\install\zlib\lib -set ZLIB_INC_DIR=C:\dev6411\install\zlib\include \ No newline at end of file diff --git a/build_scripts/windows6411/c7env64.vbs b/build_scripts/windows6411/c7env64.vbs deleted file mode 100755 index 9f22dfbf5181fb0835f9081f39049b9c84c0b54f..0000000000000000000000000000000000000000 --- a/build_scripts/windows6411/c7env64.vbs +++ /dev/null @@ -1,21 +0,0 @@ -Set objShell = WScript.CreateObject("WScript.Shell") -Set colUsrEnvVars = objShell.Environment("USER") - -QtPath = "C:\dev6411\install\Qt\bin" - -Path = colUsrEnvVars("OLDPATH") - -If Len(Path) = 0 Then - colUsrEnvVars("OLDPATH") = colUsrEnvVars("PATH") - 'objShell.ExpandEnvironmentStrings("%PATH%") - WScript.Echo colUsrEnvVars("OLDPATH") - Path = colUsrEnvVars("OLDPATH") -End If - -NewPath = (QtPath & ";" & Path) -colUsrEnvVars("PATH") = (NewPath) - -colUsrEnvVars("QTDIR") = "C:\dev6411\install\Qt" - -colUsrEnvVars("ZLIB_LIB_DIR") = "C:\dev6411\install\zlib\lib" -colUsrEnvVars("ZLIB_INC_DIR") = "C:\dev6411\install\zlib\include" \ No newline at end of file diff --git a/build_scripts/windows6411/ctp.bat b/build_scripts/windows6411/ctp.bat deleted file mode 100755 index 91a0c79f24b73e6376335d2f36eaed9fcc54925e..0000000000000000000000000000000000000000 --- a/build_scripts/windows6411/ctp.bat +++ /dev/null @@ -1,2 +0,0 @@ -set PATH=c:\Program Files (x86)\Microsoft Visual C++ Compiler Nov 2012 CTP\bin;%PATH% -set INCLUDE=c:\Program Files (x86)\Microsoft Visual C++ Compiler Nov 2012 CTP\include;%INCLUDE% \ No newline at end of file diff --git a/build_scripts/windows6411/env.bat b/build_scripts/windows6411/env.bat deleted file mode 100755 index 1f68351a4eef31741a2d368106ccba1c9935f0fc..0000000000000000000000000000000000000000 --- a/build_scripts/windows6411/env.bat +++ /dev/null @@ -1,20 +0,0 @@ -@echo off -rem isn't batch awesome, no else statments... -IF [%1]==[] ( - set DIR "." -) -IF NOT [%1]==[] ( -set DIR=%1 -) -IF [%2]==[] ( - set MACHINE X86 -) -IF NOT [%2]==[] ( -set MACHINE=%2 -) - -IF NOT [%INITVS%]==[%MACHINE%] ( - echo "Setting %MACHINE% bit compiler variables" - set INITVS=%MACHINE% - call "c:\Program Files (x86)\Microsoft Visual Studio 11.0\VC\vcvarsall.bat" %MACHINE% -) \ No newline at end of file diff --git a/build_scripts/windows6411/launchcmdvs.bat b/build_scripts/windows6411/launchcmdvs.bat deleted file mode 100755 index dcf238c305831059bae7579e016cee5a9c4c5350..0000000000000000000000000000000000000000 --- a/build_scripts/windows6411/launchcmdvs.bat +++ /dev/null @@ -1,3 +0,0 @@ -call c7env64.bat -call env.bat "c:\dev\git\caret7_source" x64 -cmd.exe diff --git a/build_scripts/windows6411/launchqtcreatorvs.bat b/build_scripts/windows6411/launchqtcreatorvs.bat deleted file mode 100755 index c74912741c416a99919c9be03d137aadcb25a457..0000000000000000000000000000000000000000 --- a/build_scripts/windows6411/launchqtcreatorvs.bat +++ /dev/null @@ -1,3 +0,0 @@ -call c7env32.bat -call env.bat "c:\dev7\windows32" x64 -qtcreator.exe diff --git a/build_scripts/windows6411/launchvs.bat b/build_scripts/windows6411/launchvs.bat deleted file mode 100755 index b948bf0f8d6f92e7147e7f8e2e1ffe27dce66d45..0000000000000000000000000000000000000000 --- a/build_scripts/windows6411/launchvs.bat +++ /dev/null @@ -1,3 +0,0 @@ -call c7env64.bat -call env.bat "c:\dev7\windows64" x64 -devenv.exe diff --git a/build_scripts/windows6411/setup_env.bat b/build_scripts/windows6411/setup_env.bat deleted file mode 100755 index 7c2061860088def3a101665329772b1d274fae1a..0000000000000000000000000000000000000000 --- a/build_scripts/windows6411/setup_env.bat +++ /dev/null @@ -1,2 +0,0 @@ -call c7env64.bat -call env.bat "c:\dev7\windows64" AMD64 \ No newline at end of file diff --git a/build_scripts/windows6412/README.txt b/build_scripts/windows6412/README.txt deleted file mode 100755 index 6d8910cad16e879025ce69dc7cb95a89ac76a068..0000000000000000000000000000000000000000 --- a/build_scripts/windows6412/README.txt +++ /dev/null @@ -1,3 +0,0 @@ -setup_env.bat -sets up the build environment -build.bat -sets up environment, runs cmake, and builds the release version of caret -c7env64.bat - sets up environment diff --git a/build_scripts/windows6412/build.bat b/build_scripts/windows6412/build.bat deleted file mode 100755 index f0d100316a40efe44e339904e16fcebfc7f7cc20..0000000000000000000000000000000000000000 --- a/build_scripts/windows6412/build.bat +++ /dev/null @@ -1,11 +0,0 @@ -@echo off -if [%MACHINE%]==[] call setup_env.bat - -set OLD_DIR=%CD% -REM rd /q/s %DIR%\caret7_source\build -md %DIR%\caret7_source\build -cd /d %DIR%\caret7_source\build -cmake -G "NMake Makefiles JOM" -DCMAKE_BUILD_TYPE=Release ../src -jom -j8 -cd /d %OLD_DIR% -set OLD_DIR= \ No newline at end of file diff --git a/build_scripts/windows6412/c7env64.bat b/build_scripts/windows6412/c7env64.bat deleted file mode 100755 index aafc2c3f37817ecb4f1ef43725085f0839f4809f..0000000000000000000000000000000000000000 --- a/build_scripts/windows6412/c7env64.bat +++ /dev/null @@ -1,4 +0,0 @@ -set PATH=C:\dev6412\install\Qt486\bin;C:\Qt\qtcreator-2.4.1\bin;%PATH% -set QTDIR=C:\dev6412\install\Qt486 -set ZLIB_LIB_DIR=C:\dev6412\install\zlib\lib -set ZLIB_INC_DIR=C:\dev6412\install\zlib\include \ No newline at end of file diff --git a/build_scripts/windows6412/c7env64.vbs b/build_scripts/windows6412/c7env64.vbs deleted file mode 100755 index 9f22dfbf5181fb0835f9081f39049b9c84c0b54f..0000000000000000000000000000000000000000 --- a/build_scripts/windows6412/c7env64.vbs +++ /dev/null @@ -1,21 +0,0 @@ -Set objShell = WScript.CreateObject("WScript.Shell") -Set colUsrEnvVars = objShell.Environment("USER") - -QtPath = "C:\dev6411\install\Qt\bin" - -Path = colUsrEnvVars("OLDPATH") - -If Len(Path) = 0 Then - colUsrEnvVars("OLDPATH") = colUsrEnvVars("PATH") - 'objShell.ExpandEnvironmentStrings("%PATH%") - WScript.Echo colUsrEnvVars("OLDPATH") - Path = colUsrEnvVars("OLDPATH") -End If - -NewPath = (QtPath & ";" & Path) -colUsrEnvVars("PATH") = (NewPath) - -colUsrEnvVars("QTDIR") = "C:\dev6411\install\Qt" - -colUsrEnvVars("ZLIB_LIB_DIR") = "C:\dev6411\install\zlib\lib" -colUsrEnvVars("ZLIB_INC_DIR") = "C:\dev6411\install\zlib\include" \ No newline at end of file diff --git a/build_scripts/windows6412/ctp.bat b/build_scripts/windows6412/ctp.bat deleted file mode 100755 index 91a0c79f24b73e6376335d2f36eaed9fcc54925e..0000000000000000000000000000000000000000 --- a/build_scripts/windows6412/ctp.bat +++ /dev/null @@ -1,2 +0,0 @@ -set PATH=c:\Program Files (x86)\Microsoft Visual C++ Compiler Nov 2012 CTP\bin;%PATH% -set INCLUDE=c:\Program Files (x86)\Microsoft Visual C++ Compiler Nov 2012 CTP\include;%INCLUDE% \ No newline at end of file diff --git a/build_scripts/windows6412/env.bat b/build_scripts/windows6412/env.bat deleted file mode 100755 index 234241d256292034632a9ab8500fca166a8ec0cf..0000000000000000000000000000000000000000 --- a/build_scripts/windows6412/env.bat +++ /dev/null @@ -1,20 +0,0 @@ -@echo off -rem isn't batch awesome, no else statments... -IF [%1]==[] ( - set DIR "." -) -IF NOT [%1]==[] ( -set DIR=%1 -) -IF [%2]==[] ( - set MACHINE X86 -) -IF NOT [%2]==[] ( -set MACHINE=%2 -) - -IF NOT [%INITVS%]==[%MACHINE%] ( - echo "Setting %MACHINE% bit compiler variables" - set INITVS=%MACHINE% - call "c:\Program Files (x86)\Microsoft Visual Studio 12.0\VC\vcvarsall.bat" %MACHINE% -) \ No newline at end of file diff --git a/build_scripts/windows6412/launchcmdvs.bat b/build_scripts/windows6412/launchcmdvs.bat deleted file mode 100755 index dcf238c305831059bae7579e016cee5a9c4c5350..0000000000000000000000000000000000000000 --- a/build_scripts/windows6412/launchcmdvs.bat +++ /dev/null @@ -1,3 +0,0 @@ -call c7env64.bat -call env.bat "c:\dev\git\caret7_source" x64 -cmd.exe diff --git a/build_scripts/windows6412/launchqtcreatorvs.bat b/build_scripts/windows6412/launchqtcreatorvs.bat deleted file mode 100755 index c74912741c416a99919c9be03d137aadcb25a457..0000000000000000000000000000000000000000 --- a/build_scripts/windows6412/launchqtcreatorvs.bat +++ /dev/null @@ -1,3 +0,0 @@ -call c7env32.bat -call env.bat "c:\dev7\windows32" x64 -qtcreator.exe diff --git a/build_scripts/windows6412/launchvs.bat b/build_scripts/windows6412/launchvs.bat deleted file mode 100755 index b948bf0f8d6f92e7147e7f8e2e1ffe27dce66d45..0000000000000000000000000000000000000000 --- a/build_scripts/windows6412/launchvs.bat +++ /dev/null @@ -1,3 +0,0 @@ -call c7env64.bat -call env.bat "c:\dev7\windows64" x64 -devenv.exe diff --git a/build_scripts/windows6412/setup_env.bat b/build_scripts/windows6412/setup_env.bat deleted file mode 100755 index 7c2061860088def3a101665329772b1d274fae1a..0000000000000000000000000000000000000000 --- a/build_scripts/windows6412/setup_env.bat +++ /dev/null @@ -1,2 +0,0 @@ -call c7env64.bat -call env.bat "c:\dev7\windows64" AMD64 \ No newline at end of file diff --git a/build_scripts/windows64_debug.bat b/build_scripts/windows64_debug.bat deleted file mode 100755 index c7227d92f10b9b5351b87bb1d29b47517afd8582..0000000000000000000000000000000000000000 --- a/build_scripts/windows64_debug.bat +++ /dev/null @@ -1,35 +0,0 @@ -@echo=off -set CMD=c:\Windows\system32\cmd.exe -REM -REM Go to correct directory -REM -set BUILD_DIR=c:\dev7\windows64 -cd %BUILD_DIR% - -REM -REM Go into source directory -REM -cd caret7_source - -echo "Caret7 Windows64 DEBUG Build Result" - -REM -REM Grab the latest Sources -REM -c:\cygwin\bin\git.exe reset --hard HEAD -c:\cygwin\bin\git.exe fetch -c:\cygwin\bin\git.exe reset --hard origin/master - -REM -REM Build caret -REM -cd build_scripts/windows64 -%CMD% /c build_debug.bat -cd .. -cd .. -set DIST_DIR=caret@myelin1:/mainpool/storage/distribution/caret7_distribution/workbench/bin_windows64 - -REM echo "Copying Files" -REM c:\cygwin\bin\scp build/Desktop/workbench.exe %DIST_DIR% -REM c:\cygwin\bin\scp build/CommandLine/wb_command.exe %DIST_DIR% -REM echo "Finished Copying Files" diff --git a/build_scripts/windows64_remote.sh b/build_scripts/windows64_remote.sh deleted file mode 100755 index 528109a2338ec8e17872f498252ce92d3e4227cb..0000000000000000000000000000000000000000 --- a/build_scripts/windows64_remote.sh +++ /dev/null @@ -1,4 +0,0 @@ -#!/bin/sh - -ssh -v caret@windowsbuild 'c:\dev7\windows64\caret7_source\build_scripts\windows64.bat' > remote_launch_windows64.txt 2>&1 -cat remote_launch_windows64.txt | mailx -s 'Caret7 Windows 64 Build Result' john@brainvis.wustl.edu tsc5yc@mst.edu diff --git a/build_scripts/windows64_remote_debug.sh b/build_scripts/windows64_remote_debug.sh deleted file mode 100755 index 32036e65e7edd6d4c5db714f51911cb4f1b4a387..0000000000000000000000000000000000000000 --- a/build_scripts/windows64_remote_debug.sh +++ /dev/null @@ -1,4 +0,0 @@ -#!/bin/sh - -ssh -v caret@windowsbuild 'c:\dev7\windows64\caret7_source\build_scripts\windows64_debug.bat' > remote_launch_windows64_debug.txt 2>&1 -cat remote_launch_windows64_debug.txt | mailx -s 'Caret7 Windows 64 DEBUG Build Result' john@brainvis.wustl.edu tsc5yc@mst.edu diff --git a/src/Algorithms/AlgorithmCiftiCorrelation.cxx b/src/Algorithms/AlgorithmCiftiCorrelation.cxx index e1d8808467a745db49cf4f42132973decb269554..7f4c27e5b97808eb43f8b27ca99211d4eb821efc 100644 --- a/src/Algorithms/AlgorithmCiftiCorrelation.cxx +++ b/src/Algorithms/AlgorithmCiftiCorrelation.cxx @@ -527,7 +527,7 @@ float AlgorithmCiftiCorrelation::correlate(const float* row1, const float& rrs1, if (m_weightedMode) { int numWeights = (int)m_weightIndexes.size();//because we compacted the data in the row to not include any zero weights - double accum = sddot(row1, row2, numWeights);//these have already had the weighted row means subtracted out, and weights applied + double accum = dsdot(row1, row2, numWeights);//these have already had the weighted row means subtracted out, and weights applied if (m_covariance) { if (m_binaryWeights) @@ -540,7 +540,7 @@ float AlgorithmCiftiCorrelation::correlate(const float* row1, const float& rrs1, r = accum / (rrs1 * rrs2);//as do these } } else { - double accum = sddot(row1, row2, m_numCols);//these have already had the row means subtracted out + double accum = dsdot(row1, row2, m_numCols);//these have already had the row means subtracted out if (m_covariance) { r = accum / m_numCols; diff --git a/src/Algorithms/AlgorithmCiftiCorrelationGradient.cxx b/src/Algorithms/AlgorithmCiftiCorrelationGradient.cxx index 9e471435f4a8b3546edbfbf243590234e21cf9d8..ec0401d7fc346ffa776f06d7e4bcf8892a8a4a24 100644 --- a/src/Algorithms/AlgorithmCiftiCorrelationGradient.cxx +++ b/src/Algorithms/AlgorithmCiftiCorrelationGradient.cxx @@ -895,7 +895,7 @@ float AlgorithmCiftiCorrelationGradient::correlate(const float* row1, const floa { r = 1.0;//short circuit for same row } else { - double accum = sddot(row1, row2, m_numCols);//these have already had the row means subtracted out + double accum = dsdot(row1, row2, m_numCols);//these have already had the row means subtracted out if (m_covariance) { r = accum / m_numCols; diff --git a/src/Algorithms/AlgorithmCiftiCreateLabel.cxx b/src/Algorithms/AlgorithmCiftiCreateLabel.cxx index 2bb09ff74a9eed61bae150b0ea2f3b18fe479be2..46545811582b7f8a7b251a3e5480567af5c412d2 100644 --- a/src/Algorithms/AlgorithmCiftiCreateLabel.cxx +++ b/src/Algorithms/AlgorithmCiftiCreateLabel.cxx @@ -245,6 +245,10 @@ AlgorithmCiftiCreateLabel::AlgorithmCiftiCreateLabel(ProgressObject* myProgObj, } } } + if (count == 0) + { + throw AlgorithmException("volume label file does not contain any structure names"); + } voxelLists.resize(count); vector mydims; myVolLabel->getDimensions(mydims); @@ -273,7 +277,14 @@ AlgorithmCiftiCreateLabel::AlgorithmCiftiCreateLabel(ProgressObject* myProgObj, myXML.setVolumeDimsAndSForm(ciftiVolDims, myVol->getSform()); for (map::iterator myiter = componentMap.begin(); myiter != componentMap.end(); ++myiter) { - myXML.addVolumeModelToColumns(voxelLists[myiter->second], myiter->first); + if (!voxelLists[myiter->second].empty()) + { + myXML.addVolumeModelToColumns(voxelLists[myiter->second], myiter->first); + } + } + if (myXML.getNumberOfRows() == 0) + { + throw AlgorithmException("volume label file does not label any voxels as any structure"); } } if (numMaps == -1) diff --git a/src/Algorithms/AlgorithmCiftiCrossCorrelation.cxx b/src/Algorithms/AlgorithmCiftiCrossCorrelation.cxx index b03aeb0bfe311bf691f50282d5dc649a451e4d86..a9a2dfb3c4d96324375970c27ae4a3c2c58ebbc9 100644 --- a/src/Algorithms/AlgorithmCiftiCrossCorrelation.cxx +++ b/src/Algorithms/AlgorithmCiftiCrossCorrelation.cxx @@ -248,10 +248,10 @@ float AlgorithmCiftiCrossCorrelation::correlate(const float* row1, const float& if (m_weightedMode) { int numWeights = (int)m_weightIndexes.size();//because we compacted the data in the row to not include any zero weights - double accum = sddot(row1, row2, numWeights);//these have already had the weighted row means subtracted out, and weights applied + double accum = dsdot(row1, row2, numWeights);//these have already had the weighted row means subtracted out, and weights applied r = accum / (rrs1 * rrs2);//as do these } else { - double accum = sddot(row1, row2, m_numCols);//these have already had the row means subtracted out + double accum = dsdot(row1, row2, m_numCols);//these have already had the row means subtracted out r = accum / (rrs1 * rrs2); } if (fisherZ) diff --git a/src/Algorithms/AlgorithmCiftiLabelProbability.cxx b/src/Algorithms/AlgorithmCiftiLabelProbability.cxx new file mode 100644 index 0000000000000000000000000000000000000000..29918120c086e40b650d9b4d74f50e96827bd149 --- /dev/null +++ b/src/Algorithms/AlgorithmCiftiLabelProbability.cxx @@ -0,0 +1,154 @@ +/*LICENSE_START*/ +/* + * Copyright (C) 2016 Washington University School of Medicine + * + * 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 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ +/*LICENSE_END*/ + +#include "AlgorithmCiftiLabelProbability.h" +#include "AlgorithmException.h" + +#include "CiftiFile.h" + +#include +#include +#include + +using namespace caret; +using namespace std; + +AString AlgorithmCiftiLabelProbability::getCommandSwitch() +{ + return "-cifti-label-probability"; +} + +AString AlgorithmCiftiLabelProbability::getShortDescription() +{ + return "FIND FREQUENCY OF CIFTI LABELS"; +} + +OperationParameters* AlgorithmCiftiLabelProbability::getParameters() +{ + OperationParameters* ret = new OperationParameters(); + + ret->addCiftiParameter(1, "label-maps", "cifti dlabel file containing individual label maps from many subjects"); + + ret->addCiftiOutputParameter(2, "probability-dscalar-out", "the relative frequencies of each label at each vertex/voxel"); + + ret->createOptionalParameter(3, "-exclude-unlabeled", "don't make a probability map of the unlabeled key"); + + ret->setHelpText( + AString("This command outputs a set of soft ROIs, one for each label in the input, ") + + "where the value is how many of the input maps had that label at that vertex/voxel, divided by the number of input maps." + ); + return ret; +} + +void AlgorithmCiftiLabelProbability::useParameters(OperationParameters* myParams, ProgressObject* myProgObj) +{ + const CiftiFile* inputLabel = myParams->getCifti(1); + CiftiFile* outputCifti = myParams->getOutputCifti(2); + bool excludeUnlabeled = myParams->getOptionalParameter(3)->m_present; + AlgorithmCiftiLabelProbability(myProgObj, inputLabel, outputCifti, excludeUnlabeled); +} + +AlgorithmCiftiLabelProbability::AlgorithmCiftiLabelProbability(ProgressObject* myProgObj, const CiftiFile* inputLabel, CiftiFile* outputCifti, const bool& excludeUnlabeled) : AbstractAlgorithm(myProgObj) +{ + LevelProgress myProgress(myProgObj); + const CiftiXML& inputXML = inputLabel->getCiftiXML(); + if (inputXML.getNumberOfDimensions() != 2 || + inputXML.getMappingType(CiftiXML::ALONG_COLUMN) != CiftiMappingType::BRAIN_MODELS || + inputXML.getMappingType(CiftiXML::ALONG_ROW) != CiftiMappingType::LABELS) + { + throw AlgorithmException("input cifti file does not have the mapping types of a dlabel file"); + } + const CiftiLabelsMap& inputLabelMap = inputXML.getLabelsMap(CiftiXML::ALONG_ROW); + int64_t numInMaps = inputLabelMap.getLength(); + vector > keyToOutMapLookup(numInMaps);//we match labels by name, not by key - since cifti files are more memory efficient than volume files, do it the fast way + map nameToOutMap; + for (int64_t m = 0; m < numInMaps; ++m) + { + const GiftiLabelTable* mapTable = inputLabelMap.getMapLabelTable(m); + set mapKeys = mapTable->getKeys(); + int32_t unlabeledKey = -1;//don't request it from the table if we aren't going to skip it, because requesting it can add it to the table + if (excludeUnlabeled) + { + unlabeledKey = mapTable->getUnassignedLabelKey(); + } + for (set::iterator iter = mapKeys.begin(); iter != mapKeys.end(); ++iter)//order by key value + { + if (excludeUnlabeled && *iter == unlabeledKey) continue;//skip to next key + const AString thisName = mapTable->getLabelName(*iter); + map::iterator search = nameToOutMap.find(thisName); + int outMap = -1; + if (search == nameToOutMap.end()) + { + outMap = nameToOutMap.size();//sequential integers starting from 0 + nameToOutMap[thisName] = outMap; + } else { + outMap = search->second; + } + keyToOutMapLookup[m][*iter] = outMap; + } + }//for simplicity, do the actual processing in a second loop + int64_t numOutMaps = nameToOutMap.size(), colSize = inputXML.getDimensionLength(CiftiXML::ALONG_COLUMN); + vector > counts(numOutMaps, vector(colSize, 0)); + for (int64_t row = 0; row < colSize; ++row) + { + vector scratchRow(numInMaps); + inputLabel->getRow(scratchRow.data(), row); + for (int64_t m = 0; m < numInMaps; ++m) + { + int thiskey = (int)floor(scratchRow[m] + 0.5f); + map::iterator search = keyToOutMapLookup[m].find(thiskey); + if (search != keyToOutMapLookup[m].end()) + { + ++counts[search->second][row]; + } + } + } + CiftiXML outXML; + outXML.setNumberOfDimensions(2); + outXML.setMap(CiftiXML::ALONG_COLUMN, inputXML.getBrainModelsMap(CiftiXML::ALONG_COLUMN)); + CiftiScalarsMap outRowMap; + outRowMap.setLength(numOutMaps); + for (map::iterator iter = nameToOutMap.begin(); iter != nameToOutMap.end(); ++iter) + { + outRowMap.setMapName(iter->second, iter->first); + } + outXML.setMap(CiftiXML::ALONG_ROW, outRowMap); + outputCifti->setCiftiXML(outXML); + for (int64_t row = 0; row < colSize; ++row) + { + vector scratchRow(numOutMaps); + for (int64_t m = 0; m < numOutMaps; ++m) + { + scratchRow[m] = ((float)counts[m][row]) / numInMaps; + } + outputCifti->setRow(scratchRow.data(), row); + } +} + +float AlgorithmCiftiLabelProbability::getAlgorithmInternalWeight() +{ + return 1.0f;//override this if needed, if the progress bar isn't smooth +} + +float AlgorithmCiftiLabelProbability::getSubAlgorithmWeight() +{ + //return AlgorithmInsertNameHere::getAlgorithmWeight();//if you use a subalgorithm + return 0.0f; +} diff --git a/src/Algorithms/AlgorithmCiftiLabelProbability.h b/src/Algorithms/AlgorithmCiftiLabelProbability.h new file mode 100644 index 0000000000000000000000000000000000000000..6e47c1bb07d355c620e2552e794831ab3f37c224 --- /dev/null +++ b/src/Algorithms/AlgorithmCiftiLabelProbability.h @@ -0,0 +1,46 @@ +#ifndef __ALGORITHM_CIFTI_LABEL_PROBABILITY_H__ +#define __ALGORITHM_CIFTI_LABEL_PROBABILITY_H__ + +/*LICENSE_START*/ +/* + * Copyright (C) 2016 Washington University School of Medicine + * + * 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 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ +/*LICENSE_END*/ + +#include "AbstractAlgorithm.h" + +namespace caret { + + class AlgorithmCiftiLabelProbability : public AbstractAlgorithm + { + AlgorithmCiftiLabelProbability(); + protected: + static float getSubAlgorithmWeight(); + static float getAlgorithmInternalWeight(); + public: + AlgorithmCiftiLabelProbability(ProgressObject* myProgObj, const CiftiFile* inputLabel, CiftiFile* outputCifti, const bool& excludeUnlabeled = false); + static OperationParameters* getParameters(); + static void useParameters(OperationParameters* myParams, ProgressObject* myProgObj); + static AString getCommandSwitch(); + static AString getShortDescription(); + }; + + typedef TemplateAutoOperation AutoAlgorithmCiftiLabelProbability; + +} + +#endif //__ALGORITHM_CIFTI_LABEL_PROBABILITY_H__ diff --git a/src/Algorithms/AlgorithmCiftiParcellate.cxx b/src/Algorithms/AlgorithmCiftiParcellate.cxx index b07198c99fdb5d89c2141f8cc647c79f2886d715..66c2eedfe73fcb98b1ab69d5cd4d5c050c004295 100644 --- a/src/Algorithms/AlgorithmCiftiParcellate.cxx +++ b/src/Algorithms/AlgorithmCiftiParcellate.cxx @@ -82,9 +82,16 @@ OperationParameters* AlgorithmCiftiParcellate::getParameters() ret->createOptionalParameter(9, "-only-numeric", "exclude non-numeric values"); + OptionalParameter* emptyOpt = ret->createOptionalParameter(10, "-include-empty", "create parcels for labels that have no vertices or voxels"); + OptionalParameter* emptyValOpt = emptyOpt->createOptionalParameter(1, "-fill-value", "specify value to use in empty parcels (default 0)"); + emptyValOpt->addDoubleParameter(1, "value", "the value to fill empty parcels with"); + OptionalParameter* emptyRoiOpt = emptyOpt->createOptionalParameter(2, "-nonempty-mask-out", "output a matching pscalar file that has 0s in empty parcels, and 1s elsewhere"); + emptyRoiOpt->addCiftiOutputParameter(1, "mask-out", "the output mask file"); + ret->setHelpText( - AString("Each label in the cifti label file will be treated as a parcel, and all rows or columns within the parcel are averaged together to form the output ") + + AString("Each non-empty label (other than the unlabeled key) in the cifti label file will be treated as a parcel, and all rows or columns within the parcel are averaged together to form the output ") + "row or column. " + + "If -include-empty is specified, empty labels will be treated as parcels with no elements, and filled with a constant value. " + CiftiXML::directionFromStringExplanation() + " " + "For dtseries or dscalar, use COLUMN. " + "If you are parcellating a dconn in both directions, parcellating by ROW first will use much less memory.\n\n" + @@ -131,6 +138,23 @@ void AlgorithmCiftiParcellate::useParameters(OperationParameters* myParams, Prog excludeHigh = (float)excludeOpt->getDouble(2); if (!(excludeLow > 0.0f && excludeHigh > 0.0f)) throw AlgorithmException("exclusion sigmas must be positive"); } + OptionalParameter* emptyOpt = myParams->getOptionalParameter(10); + bool includeEmpty = emptyOpt->m_present; + float emptyFillValue = 0.0f; + CiftiFile* emptyMaskOut = NULL; + if (includeEmpty) + { + OptionalParameter* emptyValOpt = emptyOpt->getOptionalParameter(1); + if (emptyValOpt->m_present) + { + emptyFillValue = emptyValOpt->getDouble(1); + } + OptionalParameter* emptyRoiOpt = emptyOpt->getOptionalParameter(2); + if (emptyRoiOpt->m_present) + { + emptyMaskOut = emptyRoiOpt->getOutputCifti(1); + } + } OptionalParameter* spatialWeightOpt = myParams->getOptionalParameter(5); OptionalParameter* ciftiWeightOpt = myParams->getOptionalParameter(6); if (spatialWeightOpt->m_present && ciftiWeightOpt->m_present) @@ -170,7 +194,7 @@ void AlgorithmCiftiParcellate::useParameters(OperationParameters* myParams, Prog thisStore = &rightStore; thisWeights = &rightWeights; thisSurfOpt = rightSurfOpt; - thisMetricOpt = cerebMetricOpt; + thisMetricOpt = rightMetricOpt; break; case 2: thisStore = &cerebStore; @@ -181,7 +205,7 @@ void AlgorithmCiftiParcellate::useParameters(OperationParameters* myParams, Prog } if (thisMetricOpt->m_present) { - *thisWeights = leftMetricOpt->getMetric(1); + *thisWeights = thisMetricOpt->getMetric(1); } if (thisSurfOpt->m_present) { @@ -194,19 +218,28 @@ void AlgorithmCiftiParcellate::useParameters(OperationParameters* myParams, Prog *thisWeights = thisStore; } } - AlgorithmCiftiParcellate(myProgObj, myCiftiIn, myCiftiLabel, direction, myCiftiOut, leftWeights, rightWeights, cerebWeights, method, excludeLow, excludeHigh, onlyNumeric); + AlgorithmCiftiParcellate(myProgObj, myCiftiIn, myCiftiLabel, direction, myCiftiOut, + leftWeights, rightWeights, cerebWeights, + method, excludeLow, excludeHigh, onlyNumeric, + includeEmpty, emptyFillValue, emptyMaskOut); return; } if (ciftiWeightOpt->m_present) { - AlgorithmCiftiParcellate(myProgObj, myCiftiIn, myCiftiLabel, direction, myCiftiOut, ciftiWeightOpt->getCifti(1), method, excludeLow, excludeHigh, onlyNumeric); + AlgorithmCiftiParcellate(myProgObj, myCiftiIn, myCiftiLabel, direction, myCiftiOut, + ciftiWeightOpt->getCifti(1), + method, excludeLow, excludeHigh, onlyNumeric, + includeEmpty, emptyFillValue, emptyMaskOut); return; } - AlgorithmCiftiParcellate(myProgObj, myCiftiIn, myCiftiLabel, direction, myCiftiOut, method, excludeLow, excludeHigh, onlyNumeric); + AlgorithmCiftiParcellate(myProgObj, myCiftiIn, myCiftiLabel, direction, myCiftiOut, + method, excludeLow, excludeHigh, onlyNumeric, + includeEmpty, emptyFillValue, emptyMaskOut); } AlgorithmCiftiParcellate::AlgorithmCiftiParcellate(ProgressObject* myProgObj, const CiftiFile* myCiftiIn, const CiftiFile* myCiftiLabel, const int& direction, CiftiFile* myCiftiOut, - const ReductionEnum::Enum& method, const float& excludeLow, const float& excludeHigh, const bool& onlyNumeric) : AbstractAlgorithm(myProgObj) + const ReductionEnum::Enum& method, const float& excludeLow, const float& excludeHigh, const bool& onlyNumeric, + const bool& includeEmpty, const float& emptyFillVal, CiftiFile* emptyMaskOut) : AbstractAlgorithm(myProgObj) { LevelProgress myProgress(myProgObj); CaretAssert(direction >= 0); @@ -235,7 +268,7 @@ AlgorithmCiftiParcellate::AlgorithmCiftiParcellate(ProgressObject* myProgObj, co } vector indexToParcel; CiftiXML myOutXML = myInputXML; - CiftiParcelsMap outParcelMap = parcellateMapping(myCiftiLabel, inputDense, indexToParcel); + CiftiParcelsMap outParcelMap = parcellateMapping(myCiftiLabel, inputDense, indexToParcel, includeEmpty); int numParcels = outParcelMap.getLength(); if (numParcels < 1) { @@ -255,6 +288,26 @@ AlgorithmCiftiParcellate::AlgorithmCiftiParcellate(ProgressObject* myProgObj, co ++parcelCounts[parcel]; } } + if (emptyMaskOut != NULL) + { + CiftiXML maskOutXML; + maskOutXML.setNumberOfDimensions(2); + maskOutXML.setMap(CiftiXML::ALONG_COLUMN, outParcelMap); + CiftiScalarsMap maskNameMap; + maskNameMap.setLength(1); + maskNameMap.setMapName(0, "parcel not empty"); + maskOutXML.setMap(CiftiXML::ALONG_ROW, maskNameMap); + emptyMaskOut->setCiftiXML(maskOutXML); + vector emptyMaskData(numParcels, 1.0f); + for (int i = 0; i < numParcels; ++i) + { + if (parcelCounts[i] == 0) + { + emptyMaskData[i] = 0.0f; + } + } + emptyMaskOut->setColumn(emptyMaskData.data(), 0); + } bool isLabel = false; int labelDir = -1; for (int i = 0; i < (int)dims.size(); ++i) @@ -319,7 +372,7 @@ AlgorithmCiftiParcellate::AlgorithmCiftiParcellate(ProgressObject* myProgObj, co { scratchOutRow[j] = myOutXML.getLabelsMap(labelDir).getMapLabelTable((*iter)[labelDir - 1])->getUnassignedLabelKey(); } else { - scratchOutRow[j] = 0.0f; + scratchOutRow[j] = emptyFillVal;//odd corner case, but probably fine: with nonzero empty fill value and SAMPSTDEV, parcels with only one element get the fill value, but aren't technically empty } } } @@ -411,7 +464,7 @@ AlgorithmCiftiParcellate::AlgorithmCiftiParcellate(ProgressObject* myProgObj, co scratchOutRow[j] = myOutXML.getLabelsMap(labelDir).getMapLabelTable(indices[labelDir - 1])->getUnassignedLabelKey(); } } else { - scratchOutRow[j] = 0.0f; + scratchOutRow[j] = emptyFillVal; } } } @@ -424,7 +477,8 @@ AlgorithmCiftiParcellate::AlgorithmCiftiParcellate(ProgressObject* myProgObj, co namespace { void doWeightedParcellation(const CiftiFile* myCiftiIn, const int& direction, CiftiFile* myCiftiOut, const vector& indexToParcel, - const vector >& parcelWeights, const ReductionEnum::Enum& method, const float& excludeLow, const float& excludeHigh, const bool& onlyNumeric) + const vector >& parcelWeights, const ReductionEnum::Enum& method, const float& excludeLow, const float& excludeHigh, const bool& onlyNumeric, + const float& emptyFillVal, CiftiFile* emptyMaskOut) { const CiftiXML& myInputXML = myCiftiIn->getCiftiXML(); const CiftiXML& myOutXML = myCiftiOut->getCiftiXML(); @@ -445,7 +499,27 @@ namespace { CaretLogWarning(ReductionEnum::toName(method) + " reduction requested while parcellating label data"); } - int numParcels = myCiftiOut->getCiftiXML().getDimensionLength(direction); + int numParcels = myOutXML.getDimensionLength(direction); + if (emptyMaskOut != NULL) + { + CiftiXML maskOutXML; + maskOutXML.setNumberOfDimensions(2); + maskOutXML.setMap(CiftiXML::ALONG_COLUMN, myOutXML.getParcelsMap(direction)); + CiftiScalarsMap maskNameMap; + maskNameMap.setLength(1); + maskNameMap.setMapName(0, "parcel not empty"); + maskOutXML.setMap(CiftiXML::ALONG_ROW, maskNameMap); + emptyMaskOut->setCiftiXML(maskOutXML); + vector emptyMaskData(numParcels, 1.0f); + for (int i = 0; i < numParcels; ++i) + { + if (parcelWeights[i].size() == 0) + { + emptyMaskData[i] = 0.0f; + } + } + emptyMaskOut->setColumn(emptyMaskData.data(), 0); + } int64_t numCols = myInputXML.getDimensionLength(CiftiXML::ALONG_ROW); vector scratchRow(numCols); if (direction == CiftiXML::ALONG_ROW) @@ -497,7 +571,7 @@ namespace { scratchOutRow[j] = myOutXML.getLabelsMap(labelDir).getMapLabelTable((*iter)[labelDir - 1])->getUnassignedLabelKey(); } else { - scratchOutRow[j] = 0.0f; + scratchOutRow[j] = emptyFillVal; } } } @@ -589,7 +663,7 @@ namespace scratchOutRow[j] = myOutXML.getLabelsMap(labelDir).getMapLabelTable(indices[labelDir - 1])->getUnassignedLabelKey(); } } else { - scratchOutRow[j] = 0.0f; + scratchOutRow[j] = emptyFillVal; } } } @@ -602,7 +676,8 @@ namespace AlgorithmCiftiParcellate::AlgorithmCiftiParcellate(ProgressObject* myProgObj, const CiftiFile* myCiftiIn, const CiftiFile* myCiftiLabel, const int& direction, CiftiFile* myCiftiOut, const MetricFile* leftWeights, const MetricFile* rightWeights, const MetricFile* cerebWeights, const ReductionEnum::Enum& method, - const float& excludeLow, const float& excludeHigh, const bool& onlyNumeric): AbstractAlgorithm(myProgObj) + const float& excludeLow, const float& excludeHigh, const bool& onlyNumeric, + const bool& includeEmpty, const float& emptyFillVal, CiftiFile* emptyMaskOut): AbstractAlgorithm(myProgObj) { LevelProgress myProgress(myProgObj); CaretAssert(direction >= 0); @@ -660,7 +735,7 @@ AlgorithmCiftiParcellate::AlgorithmCiftiParcellate(ProgressObject* myProgObj, co } vector indexToParcel; CiftiXML myOutXML = myInputXML; - CiftiParcelsMap outParcelMap = parcellateMapping(myCiftiLabel, inputDense, indexToParcel); + CiftiParcelsMap outParcelMap = parcellateMapping(myCiftiLabel, inputDense, indexToParcel, includeEmpty); int numParcels = outParcelMap.getLength(); if (numParcels < 1) { @@ -698,11 +773,12 @@ AlgorithmCiftiParcellate::AlgorithmCiftiParcellate(ProgressObject* myProgObj, co } } } - doWeightedParcellation(myCiftiIn, direction, myCiftiOut, indexToParcel, parcelWeights, method, excludeLow, excludeHigh, onlyNumeric); + doWeightedParcellation(myCiftiIn, direction, myCiftiOut, indexToParcel, parcelWeights, method, excludeLow, excludeHigh, onlyNumeric, emptyFillVal, emptyMaskOut); } AlgorithmCiftiParcellate::AlgorithmCiftiParcellate(ProgressObject* myProgObj, const CiftiFile* myCiftiIn, const CiftiFile* myCiftiLabel, const int& direction, CiftiFile* myCiftiOut, - const CiftiFile* ciftiWeights, const ReductionEnum::Enum& method, const float& excludeLow, const float& excludeHigh, const bool& onlyNumeric): AbstractAlgorithm(myProgObj) + const CiftiFile* ciftiWeights, const ReductionEnum::Enum& method, const float& excludeLow, const float& excludeHigh, const bool& onlyNumeric, + const bool& includeEmpty, const float& emptyFillVal, CiftiFile* emptyMaskOut): AbstractAlgorithm(myProgObj) { LevelProgress myProgress(myProgObj); CaretAssert(direction >= 0); @@ -740,7 +816,7 @@ AlgorithmCiftiParcellate::AlgorithmCiftiParcellate(ProgressObject* myProgObj, co } vector indexToParcel; CiftiXML myOutXML = myInputXML; - CiftiParcelsMap outParcelMap = parcellateMapping(myCiftiLabel, inputDense, indexToParcel); + CiftiParcelsMap outParcelMap = parcellateMapping(myCiftiLabel, inputDense, indexToParcel, includeEmpty); int numParcels = outParcelMap.getLength(); if (numParcels < 1) { @@ -759,10 +835,10 @@ AlgorithmCiftiParcellate::AlgorithmCiftiParcellate(ProgressObject* myProgObj, co parcelWeights[parcel].push_back(weightCol[j]);//we already tested that the dense mappings matched } } - doWeightedParcellation(myCiftiIn, direction, myCiftiOut, indexToParcel, parcelWeights, method, excludeLow, excludeHigh, onlyNumeric); + doWeightedParcellation(myCiftiIn, direction, myCiftiOut, indexToParcel, parcelWeights, method, excludeLow, excludeHigh, onlyNumeric, emptyFillVal, emptyMaskOut); } -CiftiParcelsMap AlgorithmCiftiParcellate::parcellateMapping(const CiftiFile* myCiftiLabel, const CiftiBrainModelsMap& toParcellate, vector& indexToParcelOut) +CiftiParcelsMap AlgorithmCiftiParcellate::parcellateMapping(const CiftiFile* myCiftiLabel, const CiftiBrainModelsMap& toParcellate, vector& indexToParcelOut, const bool& includeEmpty) { const CiftiXML& myLabelXML = myCiftiLabel->getCiftiXML(); if (myLabelXML.getNumberOfDimensions() != 2 || @@ -772,11 +848,11 @@ CiftiParcelsMap AlgorithmCiftiParcellate::parcellateMapping(const CiftiFile* myC throw AlgorithmException("AlgorithmCiftiParcellate::parcellateMapping requires a cifti dlabel file as input"); } const CiftiLabelsMap& myLabelsMap = myLabelXML.getLabelsMap(CiftiXML::ALONG_ROW); - const CiftiBrainModelsMap& myDenseMap = myLabelXML.getBrainModelsMap(CiftiXML::ALONG_COLUMN); + const CiftiBrainModelsMap& labelDenseMap = myLabelXML.getBrainModelsMap(CiftiXML::ALONG_COLUMN); CiftiParcelsMap ret; - if (toParcellate.hasVolumeData() && myDenseMap.hasVolumeData()) + if (toParcellate.hasVolumeData() && labelDenseMap.hasVolumeData()) { - if(!toParcellate.getVolumeSpace().matches(myDenseMap.getVolumeSpace())) + if(!toParcellate.getVolumeSpace().matches(labelDenseMap.getVolumeSpace())) { throw AlgorithmException("AlgorithmCiftiParcellate::parcellateMapping requires matching volume space between dlabel and dense mapping to parcellate"); } @@ -786,102 +862,167 @@ CiftiParcelsMap AlgorithmCiftiParcellate::parcellateMapping(const CiftiFile* myC vector labelData(myLabelXML.getDimensionLength(CiftiXML::ALONG_COLUMN)); int unusedKey = myLabelTable->getUnassignedLabelKey(); myCiftiLabel->getColumn(labelData.data(), 0); - map > usedKeys;//the keys from the label table that actually overlap with data in the input file indexToParcelOut.clear(); indexToParcelOut.resize(toParcellate.getLength(), -1); vector surfList = toParcellate.getSurfaceStructureList(); - vector volList = toParcellate.getVolumeStructureList(); - for (int i = 0; i < (int)surfList.size(); ++i) - { - StructureEnum::Enum myStruct = surfList[i]; - if (myDenseMap.hasSurfaceData(myStruct) && toParcellate.hasSurfaceData(myStruct)) + if (includeEmpty) + {//if we include empty, then the dlabel file by itself determines the entire parcel map, ignoring the data map + const vector labelSurfList = labelDenseMap.getSurfaceStructureList(); + const set allLabelKeys = myLabelTable->getKeys(); + map keyToParcel; + int32_t count = 0; + vector parcelList; + for (set::const_iterator iter = allLabelKeys.begin(); iter != allLabelKeys.end(); ++iter) { - if (myDenseMap.getSurfaceNumberOfNodes(myStruct) != toParcellate.getSurfaceNumberOfNodes(myStruct)) + if (*iter != unusedKey) { - throw AlgorithmException("mismatch in number of surface vertices between input and dlabel for structure " + StructureEnum::toName(myStruct)); + keyToParcel[*iter] = count; + parcelList.push_back(CiftiParcelsMap::Parcel()); + parcelList.back().m_name = myLabelTable->getLabelName(*iter); + ++count; + } + } + for (int i = 0; i < (int)labelSurfList.size(); ++i) + { + StructureEnum::Enum myStruct = labelSurfList[i]; + if (labelDenseMap.hasSurfaceData(myStruct) && toParcellate.hasSurfaceData(myStruct)) + {//if they don't match in vertex count but one is empty, don't error? + if (labelDenseMap.getSurfaceNumberOfNodes(myStruct) != toParcellate.getSurfaceNumberOfNodes(myStruct)) + { + throw AlgorithmException("mismatch in number of surface vertices between input and dlabel for structure " + StructureEnum::toName(myStruct)); + } } - ret.addSurface(toParcellate.getSurfaceNumberOfNodes(myStruct), myStruct); - vector surfMap = toParcellate.getSurfaceMap(myStruct); - int64_t mapSize = (int64_t)surfMap.size(); - for (int64_t j = 0; j < mapSize; ++j) + ret.addSurface(labelDenseMap.getSurfaceNumberOfNodes(myStruct), myStruct); + vector labelSurfMap = labelDenseMap.getSurfaceMap(myStruct); + for (int64_t j = 0; j < (int64_t)labelSurfMap.size(); ++j) { - int64_t labelIndex = myDenseMap.getIndexForNode(surfMap[j].m_surfaceNode, myStruct); - if (labelIndex != -1) + int labelKey = (int)floor(labelData[labelSurfMap[j].m_ciftiIndex] + 0.5f); + map::iterator found = keyToParcel.find(labelKey);//could be unlabeled, or wild key value + if (found != keyToParcel.end()) { - int labelKey = (int)floor(labelData[labelIndex] + 0.5f); - if (labelKey != unusedKey) + int32_t whichParcel = found->second; + parcelList[whichParcel].m_surfaceNodes[myStruct].insert(labelSurfMap[j].m_surfaceNode); + int64_t dataIndex = toParcellate.getIndexForNode(labelSurfMap[j].m_surfaceNode, myStruct); + if (dataIndex != -1) { - int tempVal = -1; - map >::iterator iter = usedKeys.find(labelKey); - if (iter == usedKeys.end()) + indexToParcelOut[dataIndex] = whichParcel; + } + } + } + } + const vector labelVolMap = labelDenseMap.getFullVolumeMap(); + for (int64_t i = 0; i < (int64_t)labelVolMap.size(); ++i) + { + int labelKey = (int)floor(labelData[labelVolMap[i].m_ciftiIndex] + 0.5f); + map::iterator found = keyToParcel.find(labelKey);//could be unlabeled, or wild key value + if (found != keyToParcel.end()) + { + int32_t whichParcel = found->second; + parcelList[whichParcel].m_voxelIndices.insert(labelVolMap[i].m_ijk); + int64_t dataIndex = toParcellate.getIndexForVoxel(labelVolMap[i].m_ijk); + if (dataIndex != -1) + { + indexToParcelOut[dataIndex] = whichParcel; + } + } + } + for (int i = 0; i < (int)parcelList.size(); ++i) + { + ret.addParcel(parcelList[i]); + } + } else { + map > usedKeys;//the keys from the label table that actually overlap with data in the input file + for (int i = 0; i < (int)surfList.size(); ++i) + { + StructureEnum::Enum myStruct = surfList[i]; + if (labelDenseMap.hasSurfaceData(myStruct) && toParcellate.hasSurfaceData(myStruct)) + { + if (labelDenseMap.getSurfaceNumberOfNodes(myStruct) != toParcellate.getSurfaceNumberOfNodes(myStruct)) + { + throw AlgorithmException("mismatch in number of surface vertices between input and dlabel for structure " + StructureEnum::toName(myStruct)); + } + ret.addSurface(toParcellate.getSurfaceNumberOfNodes(myStruct), myStruct); + vector surfMap = toParcellate.getSurfaceMap(myStruct); + int64_t mapSize = (int64_t)surfMap.size(); + for (int64_t j = 0; j < mapSize; ++j) + { + int64_t labelIndex = labelDenseMap.getIndexForNode(surfMap[j].m_surfaceNode, myStruct); + if (labelIndex != -1) + { + int labelKey = (int)floor(labelData[labelIndex] + 0.5f); + if (labelKey != unusedKey) { - const GiftiLabel* myLabel = myLabelTable->getLabel(labelKey); - if (myLabel != NULL)//ignore values that aren't in the label table + int tempVal = -1; + map >::iterator iter = usedKeys.find(labelKey); + if (iter == usedKeys.end()) { - tempVal = usedKeys.size(); - CiftiParcelsMap::Parcel tempParcel; - tempParcel.m_name = myLabel->getName(); + const GiftiLabel* myLabel = myLabelTable->getLabel(labelKey); + if (myLabel != NULL)//ignore values that aren't in the label table + { + tempVal = usedKeys.size(); + CiftiParcelsMap::Parcel tempParcel; + tempParcel.m_name = myLabel->getName(); + tempParcel.m_surfaceNodes[myStruct].insert(surfMap[j].m_surfaceNode); + usedKeys[labelKey] = pair(tempParcel, tempVal); + } + } else { + tempVal = iter->second.second; + CiftiParcelsMap::Parcel& tempParcel = iter->second.first; tempParcel.m_surfaceNodes[myStruct].insert(surfMap[j].m_surfaceNode); - usedKeys[labelKey] = pair(tempParcel, tempVal); } - } else { - tempVal = iter->second.second; - CiftiParcelsMap::Parcel& tempParcel = iter->second.first; - tempParcel.m_surfaceNodes[myStruct].insert(surfMap[j].m_surfaceNode); + indexToParcelOut[surfMap[j].m_ciftiIndex] = tempVal;//we will remap these to be in order of label keys later } - indexToParcelOut[surfMap[j].m_ciftiIndex] = tempVal;//we will remap these to be in order of label keys later } } } } - } - vector volMap = toParcellate.getFullVolumeMap(); - int64_t mapSize = (int64_t)volMap.size(); - for (int64_t i = 0; i < mapSize; ++i) - { - int64_t labelIndex = myDenseMap.getIndexForVoxel(volMap[i].m_ijk); - if (labelIndex != -1) + vector volMap = toParcellate.getFullVolumeMap(); + for (int64_t i = 0; i < (int64_t)volMap.size(); ++i) { - int labelKey = (int)floor(labelData[labelIndex] + 0.5f); - if (labelKey != unusedKey) + int64_t labelIndex = labelDenseMap.getIndexForVoxel(volMap[i].m_ijk); + if (labelIndex != -1) { - int tempVal = -1; - map >::iterator iter = usedKeys.find(labelKey); - if (iter == usedKeys.end()) + int labelKey = (int)floor(labelData[labelIndex] + 0.5f); + if (labelKey != unusedKey) { - const GiftiLabel* myLabel = myLabelTable->getLabel(labelKey); - if (myLabel != NULL)//ignore values that aren't in the label table + int tempVal = -1; + map >::iterator iter = usedKeys.find(labelKey); + if (iter == usedKeys.end()) { - tempVal = usedKeys.size(); - CiftiParcelsMap::Parcel tempParcel; - tempParcel.m_name = myLabel->getName(); + const GiftiLabel* myLabel = myLabelTable->getLabel(labelKey); + if (myLabel != NULL)//ignore values that aren't in the label table + { + tempVal = usedKeys.size(); + CiftiParcelsMap::Parcel tempParcel; + tempParcel.m_name = myLabel->getName(); + tempParcel.m_voxelIndices.insert(VoxelIJK(volMap[i].m_ijk)); + usedKeys[labelKey] = pair(tempParcel, tempVal); + } + } else { + tempVal = iter->second.second; + CiftiParcelsMap::Parcel& tempParcel = iter->second.first; tempParcel.m_voxelIndices.insert(VoxelIJK(volMap[i].m_ijk)); - usedKeys[labelKey] = pair(tempParcel, tempVal); } - } else { - tempVal = iter->second.second; - CiftiParcelsMap::Parcel& tempParcel = iter->second.first; - tempParcel.m_voxelIndices.insert(VoxelIJK(volMap[i].m_ijk)); + indexToParcelOut[volMap[i].m_ciftiIndex] = tempVal;//we will remap these to be in order of label keys later } - indexToParcelOut[volMap[i].m_ciftiIndex] = tempVal;//we will remap these to be in order of label keys later } } - } - int numParcels = (int)usedKeys.size(); - vector valRemap(numParcels, -1); - int count = 0; - for (map >::const_iterator iter = usedKeys.begin(); iter != usedKeys.end(); ++iter) - { - valRemap[iter->second.second] = count;//build a lookup from temp values to label key rank - ret.addParcel(iter->second.first); - ++count; - } - int64_t lookupSize = (int64_t)indexToParcelOut.size(); - for (int64_t i = 0; i < lookupSize; ++i)//finally, remap the temporary values to the key order of the labels - { - if (indexToParcelOut[i] != -1) + int numParcels = (int)usedKeys.size(); + vector valRemap(numParcels, -1); + int count = 0; + for (map >::const_iterator iter = usedKeys.begin(); iter != usedKeys.end(); ++iter) { - indexToParcelOut[i] = valRemap[indexToParcelOut[i]]; + valRemap[iter->second.second] = count;//build a lookup from temp values to label key rank + ret.addParcel(iter->second.first); + ++count; + } + int64_t lookupSize = (int64_t)indexToParcelOut.size(); + for (int64_t i = 0; i < lookupSize; ++i)//finally, remap the temporary values to the key order of the labels + { + if (indexToParcelOut[i] != -1) + { + indexToParcelOut[i] = valRemap[indexToParcelOut[i]]; + } } } return ret; diff --git a/src/Algorithms/AlgorithmCiftiParcellate.h b/src/Algorithms/AlgorithmCiftiParcellate.h index 3d8d5e83d69c468fbbbca414904bfb0024bbf54a..5d2771ae6c3a0d075758452c21fbafeb26279224 100644 --- a/src/Algorithms/AlgorithmCiftiParcellate.h +++ b/src/Algorithms/AlgorithmCiftiParcellate.h @@ -38,15 +38,18 @@ namespace caret { public: AlgorithmCiftiParcellate(ProgressObject* myProgObj, const CiftiFile* myCiftiIn, const CiftiFile* myCiftiLabel, const int& direction, CiftiFile* myCiftiOut, const ReductionEnum::Enum& method = ReductionEnum::MEAN, - const float& excludeLow = -1.0f, const float& excludeHigh = -1.0f, const bool& onlyNumeric = false); + const float& excludeLow = -1.0f, const float& excludeHigh = -1.0f, const bool& onlyNumeric = false, + const bool& includeEmpty = false, const float& emptyFillVal = 0.0f, CiftiFile* emptyMaskOut = NULL); AlgorithmCiftiParcellate(ProgressObject* myProgObj, const CiftiFile* myCiftiIn, const CiftiFile* myCiftiLabel, const int& direction, CiftiFile* myCiftiOut, const MetricFile* leftWeights, const MetricFile* rightWeights = NULL, const MetricFile* cerebWeights = NULL, const ReductionEnum::Enum& method = ReductionEnum::MEAN, - const float& excludeLow = -1.0f, const float& excludeHigh = -1.0f, const bool& onlyNumeric = false); + const float& excludeLow = -1.0f, const float& excludeHigh = -1.0f, const bool& onlyNumeric = false, + const bool& includeEmpty = false, const float& emptyFillVal = 0.0f, CiftiFile* emptyMaskOut = NULL); AlgorithmCiftiParcellate(ProgressObject* myProgObj, const CiftiFile* myCiftiIn, const CiftiFile* myCiftiLabel, const int& direction, CiftiFile* myCiftiOut, const CiftiFile* ciftiWeights, const ReductionEnum::Enum& method = ReductionEnum::MEAN, - const float& excludeLow = -1.0f, const float& excludeHigh = -1.0f, const bool& onlyNumeric = false); - static CiftiParcelsMap parcellateMapping(const CiftiFile* myCiftiLabel, const CiftiBrainModelsMap& toParcellate, std::vector& indexToParcelOut); + const float& excludeLow = -1.0f, const float& excludeHigh = -1.0f, const bool& onlyNumeric = false, + const bool& includeEmpty = false, const float& emptyFillVal = 0.0f, CiftiFile* emptyMaskOut = NULL); + static CiftiParcelsMap parcellateMapping(const CiftiFile* myCiftiLabel, const CiftiBrainModelsMap& toParcellate, std::vector& indexToParcelOut, const bool& includeEmpty = false); static OperationParameters* getParameters(); static void useParameters(OperationParameters* myParams, ProgressObject* myProgObj); static AString getCommandSwitch(); diff --git a/src/Algorithms/AlgorithmLabelProbability.cxx b/src/Algorithms/AlgorithmLabelProbability.cxx new file mode 100644 index 0000000000000000000000000000000000000000..5780f4d8afda48318cd64a1f423caebdcc54013f --- /dev/null +++ b/src/Algorithms/AlgorithmLabelProbability.cxx @@ -0,0 +1,127 @@ +/*LICENSE_START*/ +/* + * Copyright (C) 2016 Washington University School of Medicine + * + * 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 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ +/*LICENSE_END*/ + +#include "AlgorithmLabelProbability.h" +#include "AlgorithmException.h" + +#include "GiftiLabelTable.h" +#include "LabelFile.h" +#include "MetricFile.h" + +#include +#include + +using namespace caret; +using namespace std; + +AString AlgorithmLabelProbability::getCommandSwitch() +{ + return "-label-probability"; +} + +AString AlgorithmLabelProbability::getShortDescription() +{ + return "FIND FREQUENCY OF SURFACE LABELS"; +} + +OperationParameters* AlgorithmLabelProbability::getParameters() +{ + OperationParameters* ret = new OperationParameters(); + + ret->addLabelParameter(1, "label-maps", "label file containing individual label maps from many subjects"); + + ret->addMetricOutputParameter(2, "probability-metric-out", "the relative frequencies of each label at each vertex"); + + ret->createOptionalParameter(3, "-exclude-unlabeled", "don't make a probability map of the unlabeled key"); + + ret->setHelpText( + AString("This command outputs a set of soft ROIs, one for each label in the input, ") + + "where the value is how many of the input maps had that label at that vertex, divided by the number of input maps." + ); + return ret; +} + +void AlgorithmLabelProbability::useParameters(OperationParameters* myParams, ProgressObject* myProgObj) +{ + const LabelFile* inputLabel = myParams->getLabel(1); + MetricFile* outputMetric = myParams->getOutputMetric(2); + bool excludeUnlabeled = myParams->getOptionalParameter(3)->m_present; + AlgorithmLabelProbability(myProgObj, inputLabel, outputMetric, excludeUnlabeled); +} + +AlgorithmLabelProbability::AlgorithmLabelProbability(ProgressObject* myProgObj, const LabelFile* inputLabel, MetricFile* outputMetric, const bool& excludeUnlabeled) : AbstractAlgorithm(myProgObj) +{ + LevelProgress myProgress(myProgObj); + int numNodes = inputLabel->getNumberOfNodes(); + int numInMaps = inputLabel->getNumberOfMaps();//note: label files have only one label table that covers the entire file, and should never have duplicate names + map keyToOutMapLookup; + vector outMapNames; + const GiftiLabelTable* fileTable = inputLabel->getLabelTable(); + set keys = fileTable->getKeys(); + int32_t unlabeledKey = -1;//don't request it from label table if we aren't going to exclude it, as that could create the unassigned key + if (excludeUnlabeled) + { + unlabeledKey = fileTable->getUnassignedLabelKey(); + } + for (set::iterator iter = keys.begin(); iter != keys.end(); ++iter) + { + if (excludeUnlabeled && *iter == unlabeledKey) continue; + int newOutMap = (int)keyToOutMapLookup.size();//get size before [] allocates a new member + keyToOutMapLookup[*iter] = newOutMap; + outMapNames.push_back(fileTable->getLabelName(*iter)); + } + int numOutMaps = keyToOutMapLookup.size(); + vector > counts(numOutMaps, vector(numNodes, 0)); + for (int m = 0; m < numInMaps; ++m) + { + const int32_t* data = inputLabel->getLabelKeyPointerForColumn(m); + for (int i = 0; i < numNodes; ++i) + { + map::iterator search = keyToOutMapLookup.find(data[i]); + if (search != keyToOutMapLookup.end()) + { + ++counts[search->second][i]; + } + } + } + vector scratch(numNodes); + outputMetric->setNumberOfNodesAndColumns(numNodes, numOutMaps); + outputMetric->setStructure(inputLabel->getStructure()); + for (int m = 0; m < numOutMaps; ++m) + { + outputMetric->setMapName(m, outMapNames[m]); + for (int i = 0; i < numNodes; ++i) + { + scratch[i] = ((float)counts[m][i]) / numInMaps; + } + outputMetric->setValuesForColumn(m, scratch.data()); + } +} + +float AlgorithmLabelProbability::getAlgorithmInternalWeight() +{ + return 1.0f;//override this if needed, if the progress bar isn't smooth +} + +float AlgorithmLabelProbability::getSubAlgorithmWeight() +{ + //return AlgorithmInsertNameHere::getAlgorithmWeight();//if you use a subalgorithm + return 0.0f; +} diff --git a/src/Algorithms/AlgorithmLabelProbability.h b/src/Algorithms/AlgorithmLabelProbability.h new file mode 100644 index 0000000000000000000000000000000000000000..8eb479a52b8455e3aee250b2ca56e7104ddb67bd --- /dev/null +++ b/src/Algorithms/AlgorithmLabelProbability.h @@ -0,0 +1,46 @@ +#ifndef __ALGORITHM_LABEL_PROBABILITY_H__ +#define __ALGORITHM_LABEL_PROBABILITY_H__ + +/*LICENSE_START*/ +/* + * Copyright (C) 2016 Washington University School of Medicine + * + * 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 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ +/*LICENSE_END*/ + +#include "AbstractAlgorithm.h" + +namespace caret { + + class AlgorithmLabelProbability : public AbstractAlgorithm + { + AlgorithmLabelProbability(); + protected: + static float getSubAlgorithmWeight(); + static float getAlgorithmInternalWeight(); + public: + AlgorithmLabelProbability(ProgressObject* myProgObj, const LabelFile* inputLabel, MetricFile* outputMetric, const bool& excludeUnlabeled = false); + static OperationParameters* getParameters(); + static void useParameters(OperationParameters* myParams, ProgressObject* myProgObj); + static AString getCommandSwitch(); + static AString getShortDescription(); + }; + + typedef TemplateAutoOperation AutoAlgorithmLabelProbability; + +} + +#endif //__ALGORITHM_LABEL_PROBABILITY_H__ diff --git a/src/Algorithms/AlgorithmLabelToVolumeMapping.cxx b/src/Algorithms/AlgorithmLabelToVolumeMapping.cxx index 6310921dda295c859aa7ebb69d9b97c793a20f77..e3f7aa348d78dc5098f9d4b3cb37f503c6c04079 100644 --- a/src/Algorithms/AlgorithmLabelToVolumeMapping.cxx +++ b/src/Algorithms/AlgorithmLabelToVolumeMapping.cxx @@ -63,12 +63,14 @@ OperationParameters* AlgorithmLabelToVolumeMapping::getParameters() ribbonOpt->addSurfaceParameter(2, "outer-surf", "the outer surface of the ribbon"); OptionalParameter* ribbonSubdivOpt = ribbonOpt->createOptionalParameter(3, "-voxel-subdiv", "voxel divisions while estimating voxel weights"); ribbonSubdivOpt->addIntegerParameter(1, "subdiv-num", "number of subdivisions, default 3"); + ribbonOpt->createOptionalParameter(4, "-greedy", "also put labels in voxels with less than 50% partial volume (legacy behavior)"); + ribbonOpt->createOptionalParameter(5, "-thick-columns", "use overlapping columns (legacy method)"); ret->setHelpText( AString("Maps labels from a gifti label file into a volume file. ") + "You must specify exactly one mapping method option. " + "The -nearest-vertex method uses the label from the vertex closest to the voxel center. " + - "The -ribbon-constrained method uses the same method as in -volume-to-surface-mapping, then uses the weights in reverse, with popularity logic to decide on a label to use. " + "The -ribbon-constrained method uses the same method as in -volume-to-surface-mapping, then uses the weights in reverse, with popularity logic to decide on a label to use." ); return ret; } @@ -101,6 +103,7 @@ void AlgorithmLabelToVolumeMapping::useParameters(OperationParameters* myParams, } SurfaceFile* innerSurf = NULL, *outerSurf = NULL; int subDivs = 3; + bool greedy = false, thick = false; OptionalParameter* ribbonOpt = myParams->getOptionalParameter(6); if (ribbonOpt->m_present) { @@ -121,6 +124,8 @@ void AlgorithmLabelToVolumeMapping::useParameters(OperationParameters* myParams, throw AlgorithmException("invalid number of subdivisions specified"); } } + greedy = ribbonOpt->getOptionalParameter(4)->m_present; + thick = ribbonOpt->getOptionalParameter(5)->m_present; } if (!haveMethod) { @@ -132,7 +137,7 @@ void AlgorithmLabelToVolumeMapping::useParameters(OperationParameters* myParams, AlgorithmLabelToVolumeMapping(myProgObj, myLabel, mySurf, myTemplateVol->getVolumeSpace(), myVolOut, nearDist); break; case RIBBON: - AlgorithmLabelToVolumeMapping(myProgObj, myLabel, mySurf, myTemplateVol->getVolumeSpace(), myVolOut, innerSurf, outerSurf, subDivs); + AlgorithmLabelToVolumeMapping(myProgObj, myLabel, mySurf, myTemplateVol->getVolumeSpace(), myVolOut, innerSurf, outerSurf, subDivs, greedy, thick); break; case INVALID: CaretAssert(0); @@ -190,7 +195,8 @@ AlgorithmLabelToVolumeMapping::AlgorithmLabelToVolumeMapping(ProgressObject* myP } AlgorithmLabelToVolumeMapping::AlgorithmLabelToVolumeMapping(ProgressObject* myProgObj, const LabelFile* myLabel, const SurfaceFile* mySurf, const VolumeSpace& myVolSpace, - VolumeFile* myVolOut, const SurfaceFile* innerSurf, const SurfaceFile* outerSurf, const int& subDivs) : AbstractAlgorithm(myProgObj) + VolumeFile* myVolOut, const SurfaceFile* innerSurf, const SurfaceFile* outerSurf, const int& subDivs, + const bool& greedy, const bool& thickColumn) : AbstractAlgorithm(myProgObj) { int numNodes = mySurf->getNumberOfNodes(); if (myLabel->getNumberOfNodes() != numNodes) @@ -211,7 +217,7 @@ AlgorithmLabelToVolumeMapping::AlgorithmLabelToVolumeMapping(ProgressObject* myP int numCols = myLabel->getNumberOfColumns(); myVolOut->reinitialize(myVolSpace, numCols, 1, SubvolumeAttributes::LABEL); vector > forwardWeights; - RibbonMappingHelper::computeWeightsRibbon(forwardWeights, myVolSpace, innerSurf, outerSurf, NULL, subDivs); + RibbonMappingHelper::computeWeightsRibbon(forwardWeights, myVolSpace, innerSurf, outerSurf, NULL, subDivs, !thickColumn); map > > reverseWeights; for (int i = 0; i < numNodes; ++i) { @@ -229,10 +235,12 @@ AlgorithmLabelToVolumeMapping::AlgorithmLabelToVolumeMapping(ProgressObject* myP const int32_t* colData = myLabel->getLabelKeyPointerForColumn(m); for (map > >::const_iterator iter = reverseWeights.begin(); iter != reverseWeights.end(); ++iter) { + double totalWeight = 0.0; map totals; const vector >& vertWeightsRef = iter->second; for (int i = 0; i < (int)vertWeightsRef.size(); ++i) { + totalWeight += vertWeightsRef[i].second; map::iterator iter2 = totals.find(colData[vertWeightsRef[i].first]); if (iter2 == totals.end()) {//because floats don't initialize to 0 @@ -243,12 +251,25 @@ AlgorithmLabelToVolumeMapping::AlgorithmLabelToVolumeMapping(ProgressObject* myP } float bestWeight = -1.0f; int32_t bestLabel = unlabeledVal; - for (map::iterator iter2 = totals.begin(); iter2 != totals.end(); ++iter2) + bool skipLoop = false; + if (!greedy) { - if (iter2->second > bestWeight) + if (thickColumn) { - bestWeight = iter2->second; - bestLabel = iter2->first; + skipLoop = (totalWeight < 1.5);//slight hack: the thick column method basically counts every triangle three times + } else { + skipLoop = (totalWeight < 0.5); + } + } + if (!skipLoop) + { + for (map::iterator iter2 = totals.begin(); iter2 != totals.end(); ++iter2) + { + if (iter2->second > bestWeight) + { + bestWeight = iter2->second; + bestLabel = iter2->first; + } } } scratchFrame[myVolSpace.getIndex(iter->first.m_ijk)] = bestLabel; diff --git a/src/Algorithms/AlgorithmLabelToVolumeMapping.h b/src/Algorithms/AlgorithmLabelToVolumeMapping.h index 651a492208354b2a8704ccfb508450f63731c85a..e4770e566ea460ba54dc649e682bee5e65614bfe 100644 --- a/src/Algorithms/AlgorithmLabelToVolumeMapping.h +++ b/src/Algorithms/AlgorithmLabelToVolumeMapping.h @@ -37,7 +37,8 @@ namespace caret { AlgorithmLabelToVolumeMapping(ProgressObject* myProgObj, const LabelFile* myLabel, const SurfaceFile* mySurf, const VolumeSpace& myVolSpace, VolumeFile* myVolOut, const float& nearDist); AlgorithmLabelToVolumeMapping(ProgressObject* myProgObj, const LabelFile* myLabel, const SurfaceFile* mySurf, const VolumeSpace& myVolSpace, - VolumeFile* myVolOut, const SurfaceFile* innerSurf, const SurfaceFile* outerSurf, const int& subDivs = 3); + VolumeFile* myVolOut, const SurfaceFile* innerSurf, const SurfaceFile* outerSurf, + const int& subDivs = 3, const bool& greedy = false, const bool& thick = false); static OperationParameters* getParameters(); static void useParameters(OperationParameters* myParams, ProgressObject* myProgObj); static AString getCommandSwitch(); diff --git a/src/Algorithms/AlgorithmMetricToVolumeMapping.cxx b/src/Algorithms/AlgorithmMetricToVolumeMapping.cxx index 309739ff8904adde16407cf3eab55b2f69b99c9e..4edd1925a3c636142ce3bb2fcdb5d015796bbbf0 100644 --- a/src/Algorithms/AlgorithmMetricToVolumeMapping.cxx +++ b/src/Algorithms/AlgorithmMetricToVolumeMapping.cxx @@ -65,12 +65,15 @@ OperationParameters* AlgorithmMetricToVolumeMapping::getParameters() ribbonOpt->addSurfaceParameter(2, "outer-surf", "the outer surface of the ribbon"); OptionalParameter* ribbonSubdivOpt = ribbonOpt->createOptionalParameter(3, "-voxel-subdiv", "voxel divisions while estimating voxel weights"); ribbonSubdivOpt->addIntegerParameter(1, "subdiv-num", "number of subdivisions, default 3"); + ribbonOpt->createOptionalParameter(4, "-greedy", "instead of antialiasing partial-volumed voxels, put full metric values (legacy behavior)"); + ribbonOpt->createOptionalParameter(5, "-thick-columns", "use overlapping columns (legacy method)"); ret->setHelpText( AString("Maps values from a metric file into a volume file. ") + "You must specify exactly one mapping method option. " + "The -nearest-vertex method uses the value from the vertex closest to the voxel center (useful for integer values). " + - "The -ribbon-constrained method uses the same method as in -volume-to-surface-mapping, then uses the weights in reverse. " + "The -ribbon-constrained method uses the same method as in -volume-to-surface-mapping, then uses the weights in reverse. " + + "Mapping to lower resolutions than the mesh may require a larger -voxel-subdiv value in order to have all of the surface data participate." ); return ret; } @@ -103,6 +106,7 @@ void AlgorithmMetricToVolumeMapping::useParameters(OperationParameters* myParams } SurfaceFile* innerSurf = NULL, *outerSurf = NULL; int subDivs = 3; + bool greedy = false, thick = false; OptionalParameter* ribbonOpt = myParams->getOptionalParameter(6); if (ribbonOpt->m_present) { @@ -123,6 +127,8 @@ void AlgorithmMetricToVolumeMapping::useParameters(OperationParameters* myParams throw AlgorithmException("invalid number of subdivisions specified"); } } + greedy = ribbonOpt->getOptionalParameter(4)->m_present; + thick = ribbonOpt->getOptionalParameter(5)->m_present; } if (!haveMethod) { @@ -134,7 +140,7 @@ void AlgorithmMetricToVolumeMapping::useParameters(OperationParameters* myParams AlgorithmMetricToVolumeMapping(myProgObj, myMetric, mySurf, myTemplateVol->getVolumeSpace(), myVolOut, nearDist); break; case RIBBON: - AlgorithmMetricToVolumeMapping(myProgObj, myMetric, mySurf, myTemplateVol->getVolumeSpace(), myVolOut, innerSurf, outerSurf, subDivs); + AlgorithmMetricToVolumeMapping(myProgObj, myMetric, mySurf, myTemplateVol->getVolumeSpace(), myVolOut, innerSurf, outerSurf, subDivs, greedy, thick); break; case INVALID: CaretAssert(0); @@ -191,7 +197,8 @@ AlgorithmMetricToVolumeMapping::AlgorithmMetricToVolumeMapping(ProgressObject* m } AlgorithmMetricToVolumeMapping::AlgorithmMetricToVolumeMapping(ProgressObject* myProgObj, const MetricFile* myMetric, const SurfaceFile* mySurf, const VolumeSpace& myVolSpace, - VolumeFile* myVolOut, const SurfaceFile* innerSurf, const SurfaceFile* outerSurf, const int& subDivs) : AbstractAlgorithm(myProgObj) + VolumeFile* myVolOut, const SurfaceFile* innerSurf, const SurfaceFile* outerSurf, const int& subDivs, + const bool& greedy, const bool& thickColumn) : AbstractAlgorithm(myProgObj) { int numNodes = mySurf->getNumberOfNodes(); if (myMetric->getNumberOfNodes() != numNodes) @@ -212,7 +219,7 @@ AlgorithmMetricToVolumeMapping::AlgorithmMetricToVolumeMapping(ProgressObject* m int numCols = myMetric->getNumberOfColumns(); myVolOut->reinitialize(myVolSpace, numCols); vector > forwardWeights; - RibbonMappingHelper::computeWeightsRibbon(forwardWeights, myVolSpace, innerSurf, outerSurf, NULL, subDivs); + RibbonMappingHelper::computeWeightsRibbon(forwardWeights, myVolSpace, innerSurf, outerSurf, NULL, subDivs, !thickColumn); map > > reverseWeights; for (int i = 0; i < numNodes; ++i) { @@ -236,7 +243,18 @@ AlgorithmMetricToVolumeMapping::AlgorithmMetricToVolumeMapping(ProgressObject* m accum += colData[iter->second[i].first] * iter->second[i].second; } CaretAssert(totalWeight > 0.0);//ribbon mapping should never add weights of 0 to lists - scratchFrame[myVolSpace.getIndex(iter->first.m_ijk)] = accum / totalWeight; + if (greedy) + { + scratchFrame[myVolSpace.getIndex(iter->first.m_ijk)] = accum / totalWeight; + } else { + float denom = 1.0f;//thin weights are intended to be space filling without overlapping + if (thickColumn) denom = 3.0f;//a bit of a hack - ideally, in thick mode every fully covered voxel should have weights sum to 3 + if (totalWeight > denom) + { + denom = totalWeight;//however, the surface contours occasionally occasionally turn the polyhedrons partly inside out, so voxels can sum to more than they theoretically should + } + scratchFrame[myVolSpace.getIndex(iter->first.m_ijk)] = accum / denom; + } } myVolOut->setFrame(scratchFrame.data(), m); myVolOut->setMapName(m, myMetric->getMapName(m)); diff --git a/src/Algorithms/AlgorithmMetricToVolumeMapping.h b/src/Algorithms/AlgorithmMetricToVolumeMapping.h index d1c90971656f2dd078d44bafb21332b4ec0182b5..35bd3d3434373afb0455ecf6f7fb325e6f0955f8 100644 --- a/src/Algorithms/AlgorithmMetricToVolumeMapping.h +++ b/src/Algorithms/AlgorithmMetricToVolumeMapping.h @@ -39,7 +39,8 @@ namespace caret { VolumeFile* myVolOut, const float& nearDist); ///ribbon constrained AlgorithmMetricToVolumeMapping(ProgressObject* myProgObj, const MetricFile* myMetric, const SurfaceFile* mySurf, const VolumeSpace& myVolSpace, - VolumeFile* myVolOut, const SurfaceFile* innerSurf, const SurfaceFile* outerSurf, const int& subDivs = 3); + VolumeFile* myVolOut, const SurfaceFile* innerSurf, const SurfaceFile* outerSurf, + const int& subDivs = 3, const bool& greedy = false, const bool& thick = false); static OperationParameters* getParameters(); static void useParameters(OperationParameters* myParams, ProgressObject* myProgObj); static AString getCommandSwitch(); diff --git a/src/Algorithms/AlgorithmNodesInsideBorder.cxx b/src/Algorithms/AlgorithmNodesInsideBorder.cxx index 3c6ba35c6ceb5ca8f867ea5868bef7faf33d50a6..397fb39e4de1026c4ca3cc1f391105e9c2e59a47 100644 --- a/src/Algorithms/AlgorithmNodesInsideBorder.cxx +++ b/src/Algorithms/AlgorithmNodesInsideBorder.cxx @@ -763,9 +763,9 @@ AlgorithmNodesInsideBorder::findNodeFurthestFromConnectedPathCenterOfGravity(con if (sumCount >= 1.0) { const float cog[3] = { - sumX / sumCount, - sumY / sumCount, - sumZ / sumCount + (float)(sumX / sumCount), + (float)(sumY / sumCount), + (float)(sumZ / sumCount) }; /* diff --git a/src/Algorithms/AlgorithmSurfaceCortexLayer.cxx b/src/Algorithms/AlgorithmSurfaceCortexLayer.cxx index 71ad7612a4d1739adcacd17e488a42bf187d2e74..66142b1757f6d694be496d2c59ed33ea2f1b133c 100644 --- a/src/Algorithms/AlgorithmSurfaceCortexLayer.cxx +++ b/src/Algorithms/AlgorithmSurfaceCortexLayer.cxx @@ -55,7 +55,7 @@ OperationParameters* AlgorithmSurfaceCortexLayer::getParameters() OptionalParameter* metricOpt = ret->createOptionalParameter(5, "-placement-out", "output the placement as a distance fraction from pial to white"); metricOpt->addMetricOutputParameter(1, "placement-metric", "output metric"); - ret->createOptionalParameter(6, "-untwist", "temporary option for comparing methods, specify to use old method"); + //ret->createOptionalParameter(6, "-untwist", "temporary option for comparing methods, specify to use old method"); ret->setHelpText( AString("The input surfaces must have vertex correspondence. ") + @@ -78,7 +78,7 @@ void AlgorithmSurfaceCortexLayer::useParameters(OperationParameters* myParams, P { myMetricOut = metricOpt->getOutputMetric(1); } - bool untwistMode = myParams->getOptionalParameter(6)->m_present; + bool untwistMode = false;//myParams->getOptionalParameter(6)->m_present; AlgorithmSurfaceCortexLayer(myProgObj, myWhiteSurf, myPialSurf, myVolFrac, myOutSurf, myMetricOut, untwistMode); } diff --git a/src/Algorithms/AlgorithmSurfaceDistortion.cxx b/src/Algorithms/AlgorithmSurfaceDistortion.cxx index ad8dbb4c93f35d6edd7c96cc256235f0956231cb..28140294d8acd444d3cd88cbaaeb688011c156d1 100644 --- a/src/Algorithms/AlgorithmSurfaceDistortion.cxx +++ b/src/Algorithms/AlgorithmSurfaceDistortion.cxx @@ -22,6 +22,7 @@ #include "AlgorithmException.h" #include "AlgorithmMetricSmoothing.h" +#include "FloatMatrix.h" #include "MathFunctions.h" #include "MetricFile.h" #include "SurfaceFile.h" @@ -60,14 +61,18 @@ OperationParameters* AlgorithmSurfaceDistortion::getParameters() ret->createOptionalParameter(6, "-edge-method", "calculate distortion of edge lengths rather than areas"); + ret->createOptionalParameter(7, "-local-affine-method", "calculate distortion by the local affines between triangles"); + ret->setHelpText( - AString("This command, when not using -caret5-method or -edge-method, is equivalent to using -surface-vertex-areas on each surface, ") + + AString("This command, when not using -caret5-method, -edge-method, or -local-affine-method, is equivalent to using -surface-vertex-areas on each surface, ") + "smoothing both output metrics with the GEO_GAUSS_EQUAL method on the surface they came from if -smooth is specified, and then using the formula " + "'ln(distorted/reference)/ln(2)' on the smoothed results.\n\n" + "When using -caret5-method, it uses the surface distortion method from caret5, which takes the base 2 log of the ratio of tile areas, " + "then averages those results at each vertex, and then smooths the result on the reference surface.\n\n" + "When using -edge-method, the -smooth option is ignored, and the output at each vertex is the average of 'abs(ln(refEdge/distortEdge)/ln(2))' over all edges " + - "connected to the vertex." + "connected to the vertex.\n\n" + + "When using -local-affine-method, the -smooth option is ignored. The output is two columns, the first is the area distortion ratio, and the second is anisotropic strain. " + + "These are calculated by an affine transform between matching triangles, and then averaged across the triangles of a vertex." ); return ret; } @@ -84,20 +89,29 @@ void AlgorithmSurfaceDistortion::useParameters(OperationParameters* myParams, Pr smooth = (float)smoothOpt->getDouble(1); if (smooth <= 0.0f) throw AlgorithmException("smoothing kernel must be positive if specified"); } + int methodCount = 0; bool caret5method = myParams->getOptionalParameter(5)->m_present; + if (caret5method) ++methodCount; bool edgeMethod = myParams->getOptionalParameter(6)->m_present; - if (caret5method && edgeMethod) throw AlgorithmException("you may not specify both -caret5-method and -edge-method"); - AlgorithmSurfaceDistortion(myProgObj, referenceSurf, distortedSurf, myMetricOut, smooth, caret5method, edgeMethod); + if (edgeMethod) ++methodCount; + bool strainMethod = myParams->getOptionalParameter(7)->m_present; + if (strainMethod) ++methodCount; + if (methodCount > 1) throw AlgorithmException("you may not specify more than one of -caret5-method, -edge-method, or -local-affine-method"); + AlgorithmSurfaceDistortion(myProgObj, referenceSurf, distortedSurf, myMetricOut, smooth, caret5method, edgeMethod, strainMethod); } AlgorithmSurfaceDistortion::AlgorithmSurfaceDistortion(ProgressObject* myProgObj, const SurfaceFile* referenceSurf, const SurfaceFile* distortedSurf, - MetricFile* myMetricOut, const float& smooth, const bool& caret5method, const bool& edgeMethod) : AbstractAlgorithm(myProgObj) + MetricFile* myMetricOut, const float& smooth, const bool& caret5method, const bool& edgeMethod, const bool& strainMethod) : AbstractAlgorithm(myProgObj) { - if (caret5method && edgeMethod) throw AlgorithmException("you may not use both caret5 and edge method flags"); - ProgressObject* smoothRef = NULL, *smoothDistort = NULL, *caret5Smooth = NULL;//uncomment these if you use another algorithm inside here + int methodCount = 0; + if (caret5method) ++methodCount; + if (edgeMethod) ++methodCount; + if (strainMethod) ++methodCount; + if (methodCount > 1) throw AlgorithmException("you may not use multiple method flags"); + ProgressObject* smoothRef = NULL, *smoothDistort = NULL, *caret5Smooth = NULL; if (myProgObj != NULL) { - if (smooth > 0.0f && !edgeMethod) + if (smooth > 0.0f && !edgeMethod && !strainMethod) { if (caret5method) { @@ -111,7 +125,9 @@ AlgorithmSurfaceDistortion::AlgorithmSurfaceDistortion(ProgressObject* myProgObj LevelProgress myProgress(myProgObj); if (!referenceSurf->hasNodeCorrespondence(*distortedSurf)) throw AlgorithmException("input surfaces must have node correspondence"); int numNodes = referenceSurf->getNumberOfNodes(); - myMetricOut->setNumberOfNodesAndColumns(numNodes, 1); + int numOutCols = 1; + if (strainMethod) numOutCols = 2; + myMetricOut->setNumberOfNodesAndColumns(numNodes, numOutCols); myMetricOut->setStructure(referenceSurf->getStructure()); if (caret5method) { @@ -198,6 +214,73 @@ AlgorithmSurfaceDistortion::AlgorithmSurfaceDistortion(ProgressObject* myProgObj } myMetricOut->setValue(i, 0, accum / numNeigh); } + } else if (strainMethod) { + myMetricOut->setColumnName(0, "area ratio (J)"); + myMetricOut->setColumnName(1, "elongation ratio (R)"); + int numTris = referenceSurf->getNumberOfTriangles(); + vector affines(numTris); + for (int i = 0; i < numTris; ++i) + { + const int32_t* thisTri = referenceSurf->getTriangle(i); + Vector3D allCoords[2][3]; + for (int j = 0; j < 3; ++j) + { + allCoords[0][j] = referenceSurf->getCoordinate(thisTri[j]); + allCoords[1][j] = distortedSurf->getCoordinate(thisTri[j]); + } + float allCoords2D[2][3][2];//ref/dist, vert, x/y + for (int k = 0; k < 2; ++k) + { + Vector3D iHat = (allCoords[k][1] - allCoords[k][0]).normal();//we don't need the triangle normal, so make the 2D basis without cross products + Vector3D jHat = allCoords[k][2] - allCoords[k][0]; + jHat = (jHat - iHat * iHat.dot(jHat)).normal(); + for (int j = 0; j < 3; ++j) + { + Vector3D tempCoord = allCoords[k][j] - allCoords[k][0];//use a triangle vertex as the origin to make the numbers smaller, less rounding error + allCoords2D[k][j][0] = iHat.dot(tempCoord); + allCoords2D[k][j][1] = jHat.dot(tempCoord); + } + } + affines[i].resize(2, 2, true); + FloatMatrix solver(3, 4);//3 equations, 3 unknowns + for (int k = 0; k < 2; ++k) + { + for (int j = 0; j < 3; ++j) + { + solver[j][0] = allCoords2D[0][j][0];//x + solver[j][1] = allCoords2D[0][j][1];//y + solver[j][2] = 1; + solver[j][3] = allCoords2D[1][j][k];//x' or y' + } + FloatMatrix solved = solver.reducedRowEchelon(); + affines[i][k][0] = solved[0][3];//x coef + affines[i][k][1] = solved[1][3];//y coef + } + } + int numNodes = referenceSurf->getNumberOfNodes(); + CaretPointer myTopoHelp = referenceSurf->getTopologyHelper(); + for (int i = 0; i < numNodes; ++i) + { + const vector& myTiles = myTopoHelp->getNodeTiles(i); + double accumJ = 0.0, accumR = 0.0; + for (int j = 0; j < (int)myTiles.size(); ++j) + { + FloatMatrix tempMat = affines[myTiles[j]] * affines[myTiles[j]].transpose(); + float thisJ = sqrt(tempMat[0][0] * tempMat[1][1] - tempMat[0][1] * tempMat[1][0]);//written-out determinant for 2x2, because FloatMatrix doesn't currently have a generic determinant function + float I1_st = (tempMat[0][0] + tempMat[1][1]) / thisJ;//2D formula + float thisR; + if (I1_st <= 2) + { + thisR = 1.0; + } else { + thisR = 0.5 * (I1_st + sqrt(I1_st * I1_st - 4));//convert I1* to (major strain) / (minor strain) + } + accumJ += thisJ;//not sure which areas to weight by, so for now do a straight average, relying on smoothness inherent in the measures + accumR += thisR;//could maybe do some fancy interpolation of the affines in 3D sphere space + } + myMetricOut->setValue(i, 0, accumJ / myTiles.size()); + myMetricOut->setValue(i, 1, accumR / myTiles.size()); + } } else { myMetricOut->setColumnName(0, "area distortion"); MetricFile refAreas, distortAreas; diff --git a/src/Algorithms/AlgorithmSurfaceDistortion.h b/src/Algorithms/AlgorithmSurfaceDistortion.h index b50b5a604dc9fdd698008ce1183d1dffaabce669..e8c340a1ec13dfb4d47317fbb64632f1704bf828 100644 --- a/src/Algorithms/AlgorithmSurfaceDistortion.h +++ b/src/Algorithms/AlgorithmSurfaceDistortion.h @@ -33,7 +33,8 @@ namespace caret { static float getAlgorithmInternalWeight(); public: AlgorithmSurfaceDistortion(ProgressObject* myProgObj, const SurfaceFile* referenceSurf, const SurfaceFile* distortedSurf, - MetricFile* myMetricOut, const float& smooth = -1.0f, const bool& caret5method = false, const bool& edgeMethod = false); + MetricFile* myMetricOut, const float& smooth = -1.0f, + const bool& caret5method = false, const bool& edgeMethod = false, const bool& strainMethod = false); static OperationParameters* getParameters(); static void useParameters(OperationParameters* myParams, ProgressObject* myProgObj); static AString getCommandSwitch(); diff --git a/src/Algorithms/AlgorithmVolumeAffineResample.cxx b/src/Algorithms/AlgorithmVolumeAffineResample.cxx index 6beb6e996fee9866bfa54242cbf6111139673ce9..bd23d04346a6b3b5bdececf5fd397d90c34399a9 100644 --- a/src/Algorithms/AlgorithmVolumeAffineResample.cxx +++ b/src/Algorithms/AlgorithmVolumeAffineResample.cxx @@ -95,7 +95,9 @@ void AlgorithmVolumeAffineResample::useParameters(OperationParameters* myParams, FloatMatrix affMat = FloatMatrix(myAffine.getMatrix()); NiftiIO refSpaceIO; refSpaceIO.openRead(refSpaceName); - AlgorithmVolumeAffineResample(myProgObj, inVol, affMat, refSpaceIO.getDimensions().data(), refSpaceIO.getHeader().getSForm(), myMethod, outVol); + vector refDims = refSpaceIO.getDimensions(); + if (refDims.size() < 3) refDims.resize(3, 1); + AlgorithmVolumeAffineResample(myProgObj, inVol, affMat, refDims.data(), refSpaceIO.getHeader().getSForm(), myMethod, outVol); } AlgorithmVolumeAffineResample::AlgorithmVolumeAffineResample(ProgressObject* myProgObj, const VolumeFile* inVol, const FloatMatrix& myAffine, @@ -135,6 +137,10 @@ AlgorithmVolumeAffineResample::AlgorithmVolumeAffineResample(ProgressObject* myP *(outVol->getMapLabelTable(i)) = *(inVol->getMapLabelTable(i)); } } + for (int64_t i = 0; i < numMaps; ++i) + { + outVol->setMapName(i, inVol->getMapName(i)); + } for (int64_t c = 0; c < numComponents; ++c) { for (int64_t b = 0; b < numMaps; ++b) diff --git a/src/Algorithms/AlgorithmVolumeDistortion.cxx b/src/Algorithms/AlgorithmVolumeDistortion.cxx new file mode 100644 index 0000000000000000000000000000000000000000..7b1e5673d108a6b89e1278cf5e3640f85f940657 --- /dev/null +++ b/src/Algorithms/AlgorithmVolumeDistortion.cxx @@ -0,0 +1,191 @@ +/*LICENSE_START*/ +/* + * Copyright (C) 2016 Washington University School of Medicine + * + * 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 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ +/*LICENSE_END*/ + +#include "AlgorithmVolumeDistortion.h" +#include "AlgorithmException.h" + +#include "AlgorithmVolumeGradient.h" +#include "CaretOMP.h" +#include "FloatMatrix.h" +#include "VolumeFile.h" +#include "WarpfieldFile.h" + +#include +#include + +using namespace caret; +using namespace std; + +AString AlgorithmVolumeDistortion::getCommandSwitch() +{ + return "-volume-distortion"; +} + +AString AlgorithmVolumeDistortion::getShortDescription() +{ + return "CALCULATE VOLUME WARPFIELD DISTORTION"; +} + +OperationParameters* AlgorithmVolumeDistortion::getParameters() +{ + OperationParameters* ret = new OperationParameters(); + + ret->addStringParameter(1, "warpfield", "the warpfield to compute the distortion of"); + + ret->addVolumeOutputParameter(2, "volume-out", "the output distortion measures"); + + OptionalParameter* fnirtOpt = ret->createOptionalParameter(3, "-fnirt", "MUST be used if using a fnirt warpfield"); + fnirtOpt->addStringParameter(1, "source-volume", "the source volume used when generating the warpfield"); + + ret->createOptionalParameter(4, "-circular", "use the circle-based formula for the anisotropic measure"); + + ret->setHelpText( + AString("Calculates isotropic and anisotropic distortions in the volume warpfield. ") + + "At each voxel, the gradient of the absolute warpfield is computed to obtain the local affine transforms for each voxel (jacobian matrices), and strain tensors are derived from them. " + + "The isotropic component (volumetric expansion ratio) is the product of the three principal strains. " + + "The default measure ('elongation') for the anisotropic component is the largest principal strain divided by the smallest.\n\n" + + "The -circular option instead calculates the anisotropic component by transforming the principal strains into log space, " + + "considering them as x-values of points on a circle 120 degrees apart, finds the circle's diameter, and transforms that back to a ratio." + ); + return ret; +} + +void AlgorithmVolumeDistortion::useParameters(OperationParameters* myParams, ProgressObject* myProgObj) +{ + AString warpName = myParams->getString(1); + VolumeFile* distortionOut = myParams->getOutputVolume(2); + OptionalParameter* fnirtOpt = myParams->getOptionalParameter(3); + Method myMethod = ELONGATION; + if (myParams->getOptionalParameter(4)->m_present) myMethod = CIRCULAR; + WarpfieldFile myWarp; + if (fnirtOpt->m_present) + { + myWarp.readFnirt(warpName, fnirtOpt->getString(1)); + } else { + myWarp.readWorld(warpName); + } + AlgorithmVolumeDistortion(myProgObj, myWarp, distortionOut, myMethod); +} + +AlgorithmVolumeDistortion::AlgorithmVolumeDistortion(ProgressObject* myProgObj, const WarpfieldFile& myWarp, VolumeFile* distortionOut, Method myMethod) : AbstractAlgorithm(myProgObj) +{ + LevelProgress myProgress(myProgObj); + switch (myMethod) + { + case ELONGATION: + case CIRCULAR: + break; + default: + throw AlgorithmException("invalid method value passed to AlgorithmVolumeDistortion"); + } + CaretPointer absWarp = myWarp.generateAbsolute(); + VolumeFile gradMag, gradVec;//don't need gradMag, but it is a mandatory argument to algorithm + AlgorithmVolumeGradient(NULL, absWarp, &gradMag, -1, NULL, &gradVec);//vector order is dx'/dx, dx'/dy, dx'/dz, dy'/dx, etc - these are the local affines, we don't need the translation + vector dims = gradVec.getDimensions(); + CaretAssert(dims[3] == 9);//make sure it has the expected components for a 3D tranform + dims.resize(4);//drop components so we can use it to initialize output + dims[3] = 2;//set it to 2 maps + distortionOut->reinitialize(dims, absWarp->getSform()); + distortionOut->setMapName(0, "isotropic (expansion)"); + switch (myMethod) + { + case ELONGATION: + distortionOut->setMapName(1, "anisotropic (elongation)"); + break; + case CIRCULAR: + distortionOut->setMapName(1, "anisotropic (circular)"); + break; + } +#pragma omp CARET_PARFOR schedule(dynamic) + for (int64_t k = 0; k < dims[2]; ++k) + { + for (int64_t j = 0; j < dims[1]; ++j) + { + for (int64_t i = 0; i < dims[0]; ++i) + { + FloatMatrix localAff(3, 3); + for (int dim1 = 0; dim1 < 3; ++dim1) + { + for (int dim2 = 0; dim2 < 3; ++dim2) + { + localAff[dim1][dim2] = gradVec.getValue(i, j, k, dim1 * 3 + dim2); + } + } + FloatMatrix squaredtensor = localAff * localAff.transpose();//this cancels the inner rotations, aligning the principal strains resulting in squaring them, allowing us to use tensor simplifications + float isotropic = sqrt(squaredtensor.determinant());//square root of i3 for squared tensor + float i1 = (squaredtensor[0][0] + squaredtensor[1][1] + squaredtensor[2][2]);//sum of squares of principal strains + float i2 = squaredtensor[0][0] * squaredtensor[1][1] + squaredtensor[0][0] * squaredtensor[2][2] + squaredtensor[1][1] * squaredtensor[2][2] - + squaredtensor[0][1] * squaredtensor[0][1] - squaredtensor[0][2] * squaredtensor[0][2] - squaredtensor[1][2] * squaredtensor[1][2]; + float q = (3 * i2 - i1 * i1) / 9.0f;//now we need to solve the cubic for the three principal strains, so we can make whatever formula we want + if (q > 0) + { + q = 0.0f;//just in case, avoid pow/sqrt giving NaN for tiny positive q, we should always have 3 solutions + } + float r = (i1 * (2 * i1 * i1 - 9 * i2) + 27 * isotropic * isotropic) / 54.0f;//remember, the tensor is squared + const float PI = 3.141592654f;//trivially avoid the _USE_MATH_DEFINES that isn't actually required by the standard - float has only ~7 decimal digits of precision + float triratio = r / pow(-q, 3.0f / 2.0f);//actually part of the cubic root equation, but we need to sanity check it + if (triratio > 1.0f) triratio = 1.0f;//because we don't want acos to give a nan + if (triratio < -1.0f) triratio = -1.0f; + float theta = acos(triratio); + float shapes[3];//these will be ordinary, not squared, strains + float isoroot3 = pow(isotropic, 1.0f / 3.0f); + for (int a = 0; a < 3; ++a) + { + shapes[a] = sqrt(2 * sqrt(-q) * cos((theta + a * 2 * PI) / 3.0f) + i1 / 3.0f) / isoroot3;//square root because squared tensor, and also divide out isotropic component + } + sort(shapes, shapes + 3); + float anisotropic = -1.0f;//initialize with obviously-wrong value to silence compiler and make bugs obvious + switch (myMethod) + { + case ELONGATION: + anisotropic = shapes[2] / shapes[0];//deceptively simple: largest strain divided by smallest strain + break; + case CIRCULAR: + { + const float ASPHER_THRESH = 1.00001f; + if (shapes[2] > ASPHER_THRESH) + {//asphericity formula is somewhat unstable when near-spherical (shapes[0] will always be near 1 when shapes[2] is, so a reciprocal reformulation won't help much) + anisotropic = exp(-2.0f * log(shapes[0]) / cos(atan(sqrt(3.0f) * (-log(shapes[0]) / log(shapes[2]) - 1) / (log(shapes[0]) / log(shapes[2]) - 1)) + PI / 6.0f));//deceptively complicated + } else {//when unstable, reshape the elongation ratio to approximate it + anisotropic = pow(shapes[2] / shapes[0], 1.24f); + }//explanation time: per the help info, put the shape ratios into log space, so that geometry can be useful + //now consider an equilateral triangle centered at the origin, and these log-space shape values as the signed distance from the x=0 line + //now, find the distance from the origin to any vertex, and multiply by 2 to get the circumscribing circle's diameter + //this solution relied on the the most-horizontal edge of the triangle being split by x=0 at the same ratio as the magnitudes of the two extreme log-shape values (don't forget the negative sign) + break; + } + } + distortionOut->setValue(isotropic, i, j, k, 0); + distortionOut->setValue(anisotropic, i, j, k, 1); + } + } + } +} + +float AlgorithmVolumeDistortion::getAlgorithmInternalWeight() +{ + return 1.0f;//override this if needed, if the progress bar isn't smooth +} + +float AlgorithmVolumeDistortion::getSubAlgorithmWeight() +{ + //return AlgorithmInsertNameHere::getAlgorithmWeight();//if you use a subalgorithm + return 0.0f; +} diff --git a/src/Algorithms/AlgorithmVolumeDistortion.h b/src/Algorithms/AlgorithmVolumeDistortion.h new file mode 100644 index 0000000000000000000000000000000000000000..4dbb75e9358bf7119a7bdf74b56e7a324fcc4146 --- /dev/null +++ b/src/Algorithms/AlgorithmVolumeDistortion.h @@ -0,0 +1,53 @@ +#ifndef __ALGORITHM_VOLUME_DISTORTION_H__ +#define __ALGORITHM_VOLUME_DISTORTION_H__ + +/*LICENSE_START*/ +/* + * Copyright (C) 2016 Washington University School of Medicine + * + * 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 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ +/*LICENSE_END*/ + +#include "AbstractAlgorithm.h" + +namespace caret { + + class WarpfieldFile; + + class AlgorithmVolumeDistortion : public AbstractAlgorithm + { + AlgorithmVolumeDistortion(); + protected: + static float getSubAlgorithmWeight(); + static float getAlgorithmInternalWeight(); + public: + enum Method + { + ELONGATION, + CIRCULAR + }; + AlgorithmVolumeDistortion(ProgressObject* myProgObj, const WarpfieldFile& myWarp, VolumeFile* distortionOut, Method myMethod = ELONGATION); + static OperationParameters* getParameters(); + static void useParameters(OperationParameters* myParams, ProgressObject* myProgObj); + static AString getCommandSwitch(); + static AString getShortDescription(); + }; + + typedef TemplateAutoOperation AutoAlgorithmVolumeDistortion; + +} + +#endif //__ALGORITHM_VOLUME_DISTORTION_H__ diff --git a/src/Algorithms/AlgorithmVolumeLabelProbability.cxx b/src/Algorithms/AlgorithmVolumeLabelProbability.cxx index 6a2206c5a110f857e9d9eadebdfe45be1e039cbd..459d86403a7c1344bd1402384c53a92d2eca720d 100644 --- a/src/Algorithms/AlgorithmVolumeLabelProbability.cxx +++ b/src/Algorithms/AlgorithmVolumeLabelProbability.cxx @@ -38,7 +38,7 @@ AString AlgorithmVolumeLabelProbability::getCommandSwitch() AString AlgorithmVolumeLabelProbability::getShortDescription() { - return "FIND FREQUENCY OF LABELS"; + return "FIND FREQUENCY OF VOLUME LABELS"; } OperationParameters* AlgorithmVolumeLabelProbability::getParameters() diff --git a/src/Algorithms/AlgorithmVolumeLabelToSurfaceMapping.cxx b/src/Algorithms/AlgorithmVolumeLabelToSurfaceMapping.cxx index bbe644186fafc2122d8f214a254d6e9cf7522bd4..30b7c8aade3ab150b1fdf7a6f8426643fa019a68 100644 --- a/src/Algorithms/AlgorithmVolumeLabelToSurfaceMapping.cxx +++ b/src/Algorithms/AlgorithmVolumeLabelToSurfaceMapping.cxx @@ -59,12 +59,21 @@ OperationParameters* AlgorithmVolumeLabelToSurfaceMapping::getParameters() roiVol->addVolumeParameter(1, "roi-volume", "the volume file"); OptionalParameter* ribbonSubdiv = ribbonOpt->createOptionalParameter(4, "-voxel-subdiv", "voxel divisions while estimating voxel weights"); ribbonSubdiv->addIntegerParameter(1, "subdiv-num", "number of subdivisions, default 3"); + ribbonOpt->createOptionalParameter(5, "-thin-columns", "use non-overlapping polyhedra"); OptionalParameter* subvolumeSelect = ret->createOptionalParameter(5, "-subvol-select", "select a single subvolume to map"); subvolumeSelect->addStringParameter(1, "subvol", "the subvolume number or name"); ret->setHelpText( - AString("Map label volume data to a surface. If -ribbon-constrained is not specified, uses the enclosing voxel method.") + AString("Map label volume data to a surface. If -ribbon-constrained is not specified, uses the enclosing voxel method. ") + + "The ribbon mapping method constructs a polyhedron from the vertex's neighbors on each " + + "surface, and estimates the amount of this polyhedron's volume that falls inside any nearby voxels, to use as the weights for a popularity comparison. " + + "If -thin-columns is specified, the polyhedron uses the edge midpoints and triangle centroids, so that neighboring vertices do not have overlapping polyhedra. " + + "This may require increasing -voxel-subdiv to get enough samples in each voxel to reliably land inside these smaller polyhedra. " + "The volume ROI is useful to exclude partial volume effects of voxels the surfaces pass through, and will cause the mapping to ignore " + + "voxels that don't have a positive value in the mask. The subdivision number specifies how it approximates the amount of the volume the polyhedron " + + "intersects, by splitting each voxel into NxNxN pieces, and checking whether the center of each piece is inside the polyhedron. If you have very large " + + "voxels, consider increasing this if you get unexpected unlabeled vertices in your output." ); return ret; } @@ -105,7 +114,8 @@ void AlgorithmVolumeLabelToSurfaceMapping::useParameters(OperationParameters* my throw AlgorithmException("invalid number of subdivisions specified"); } } - AlgorithmVolumeLabelToSurfaceMapping(myProgObj, myVolume, mySurface, myLabelOut, innerSurf, outerSurf, myRoiVol, subdivisions, mySubVol); + bool thinColumns = ribbonOpt->getOptionalParameter(5)->m_present; + AlgorithmVolumeLabelToSurfaceMapping(myProgObj, myVolume, mySurface, myLabelOut, innerSurf, outerSurf, myRoiVol, subdivisions, thinColumns, mySubVol); } else { AlgorithmVolumeLabelToSurfaceMapping(myProgObj, myVolume, mySurface, myLabelOut, mySubVol); } @@ -119,6 +129,7 @@ AlgorithmVolumeLabelToSurfaceMapping::AlgorithmVolumeLabelToSurfaceMapping(Progr { throw AlgorithmException("input volume must be a label volume"); } + if (myVolume->getNumberOfComponents() != 1) throw AlgorithmException("label volumes must not have multiple components per map"); vector myVolDims; myVolume->getDimensions(myVolDims); if (mySubVol >= myVolDims[3] || mySubVol < -1) @@ -128,9 +139,9 @@ AlgorithmVolumeLabelToSurfaceMapping::AlgorithmVolumeLabelToSurfaceMapping(Progr int64_t numColumns; if (mySubVol == -1) { - numColumns = myVolDims[3] * myVolDims[4]; + numColumns = myVolDims[3]; } else { - numColumns = myVolDims[4]; + numColumns = 1; } int64_t numNodes = mySurface->getNumberOfNodes(); myLabelOut->setNumberOfNodesAndColumns(numNodes, numColumns); @@ -141,7 +152,7 @@ AlgorithmVolumeLabelToSurfaceMapping::AlgorithmVolumeLabelToSurfaceMapping(Progr GiftiLabelTable cumulativeTable = *(myVolume->getMapLabelTable(0));//so we don't run into append issues with the "???" label that gets made by GiftiLabelTable's constructor for (int64_t i = 0; i < myVolDims[3]; ++i) { - map frameRemap; + map frameRemap; if (i > 0) { const GiftiLabelTable* tempTable = myVolume->getMapLabelTable(i); @@ -151,29 +162,21 @@ AlgorithmVolumeLabelToSurfaceMapping::AlgorithmVolumeLabelToSurfaceMapping(Progr } frameRemap = cumulativeTable.append(*tempTable); } - for (int64_t j = 0; j < myVolDims[4]; ++j) + AString mapName = myVolume->getMapName(i); + myLabelOut->setColumnName(i, mapName); +#pragma omp CARET_PARFOR + for (int64_t node = 0; node < numNodes; ++node) { - AString mapName = myVolume->getMapName(i); - if (myVolDims[4] != 1) + int32_t tempKey = (int32_t)floor(myVolume->interpolateValue(mySurface->getCoordinate(node), VolumeFile::ENCLOSING_VOXEL, NULL, i) + 0.5f); + map::iterator iter = frameRemap.find(tempKey); + if (iter != frameRemap.end()) { - mapName += " component " + AString::number(j); + myArray[node] = iter->second; + } else { + myArray[node] = tempKey;//for simplicity, assume all values in the volume file are in the label table } - int64_t thisCol = i * myVolDims[4] + j; - myLabelOut->setColumnName(thisCol, mapName); -#pragma omp CARET_PARFOR - for (int64_t node = 0; node < numNodes; ++node) - { - int32_t tempKey = (int32_t)floor(myVolume->interpolateValue(mySurface->getCoordinate(node), VolumeFile::ENCLOSING_VOXEL, NULL, i, j) + 0.5f); - map::iterator iter = frameRemap.find(tempKey); - if (iter != frameRemap.end()) - { - myArray[node] = iter->second; - } else { - myArray[node] = tempKey;//for simplicity, assume all values in the volume file are in the label table - } - } - myLabelOut->setLabelKeysForColumn(thisCol, myArray.data()); } + myLabelOut->setLabelKeysForColumn(i, myArray.data()); } *(myLabelOut->getLabelTable()) = cumulativeTable; } else { @@ -183,27 +186,19 @@ AlgorithmVolumeLabelToSurfaceMapping::AlgorithmVolumeLabelToSurfaceMapping(Progr throw AlgorithmException("specified subvolume is missing a label table"); } *(myLabelOut->getLabelTable()) = *tempTable; - for (int64_t j = 0; j < myVolDims[4]; ++j) - { - AString mapName = myVolume->getMapName(mySubVol); - if (myVolDims[4] != 1) - { - mapName += " component " + AString::number(j); - } - int64_t thisCol = j; - myLabelOut->setColumnName(thisCol, mapName); + myLabelOut->setColumnName(0, myVolume->getMapName(mySubVol)); #pragma omp CARET_PARFOR - for (int64_t node = 0; node < numNodes; ++node) - {//for simplicity, assume all values in the volume file are in the label table - myArray[node] = (int32_t)floor(myVolume->interpolateValue(mySurface->getCoordinate(node), VolumeFile::ENCLOSING_VOXEL, NULL, mySubVol, j) + 0.5f); - } - myLabelOut->setLabelKeysForColumn(thisCol, myArray.data()); + for (int64_t node = 0; node < numNodes; ++node) + {//for simplicity, assume all values in the volume file are in the label table + myArray[node] = (int32_t)floor(myVolume->interpolateValue(mySurface->getCoordinate(node), VolumeFile::ENCLOSING_VOXEL, NULL, mySubVol) + 0.5f); } + myLabelOut->setLabelKeysForColumn(0, myArray.data()); } } AlgorithmVolumeLabelToSurfaceMapping::AlgorithmVolumeLabelToSurfaceMapping(ProgressObject* myProgObj, const VolumeFile* myVolume, const SurfaceFile* mySurface, LabelFile* myLabelOut, - const SurfaceFile* innerSurf, const SurfaceFile* outerSurf, const VolumeFile* myRoiVol, const int32_t& subdivisions, + const SurfaceFile* innerSurf, const SurfaceFile* outerSurf, + const VolumeFile* myRoiVol, const int32_t& subdivisions, const bool& thinColumns, const int64_t& mySubVol): AbstractAlgorithm(myProgObj) { LevelProgress myProgress(myProgObj); @@ -211,6 +206,7 @@ AlgorithmVolumeLabelToSurfaceMapping::AlgorithmVolumeLabelToSurfaceMapping(Progr { throw AlgorithmException("input volume must be a label volume"); } + if (myVolume->getNumberOfComponents() != 1) throw AlgorithmException("label volumes must not have multiple components per map"); vector myVolDims; myVolume->getDimensions(myVolDims); if (mySubVol >= myVolDims[3] || mySubVol < -1) @@ -220,9 +216,9 @@ AlgorithmVolumeLabelToSurfaceMapping::AlgorithmVolumeLabelToSurfaceMapping(Progr int64_t numColumns; if (mySubVol == -1) { - numColumns = myVolDims[3] * myVolDims[4]; + numColumns = myVolDims[3]; } else { - numColumns = myVolDims[4]; + numColumns = 1; } int64_t numNodes = mySurface->getNumberOfNodes(); myLabelOut->setNumberOfNodesAndColumns(numNodes, numColumns); @@ -231,14 +227,14 @@ AlgorithmVolumeLabelToSurfaceMapping::AlgorithmVolumeLabelToSurfaceMapping(Progr vector > myWeights; const float* roiFrame = NULL; if (myRoiVol != NULL) roiFrame = myRoiVol->getFrame(); - RibbonMappingHelper::computeWeightsRibbon(myWeights, myVolume->getVolumeSpace(), innerSurf, outerSurf, roiFrame, subdivisions); + RibbonMappingHelper::computeWeightsRibbon(myWeights, myVolume->getVolumeSpace(), innerSurf, outerSurf, roiFrame, subdivisions, thinColumns); if (mySubVol == -1) { GiftiLabelTable cumulativeTable = *(myVolume->getMapLabelTable(0));//so we don't run into append issues with the "???" label that gets made by GiftiLabelTable's constructor int32_t unlabeledKey = cumulativeTable.getUnassignedLabelKey();//but, assume the first table has one, because we may need it for (int64_t i = 0; i < myVolDims[3]; ++i) { - map frameRemap; + map frameRemap; if (i > 0) { const GiftiLabelTable* tempTable = myVolume->getMapLabelTable(i); @@ -248,76 +244,82 @@ AlgorithmVolumeLabelToSurfaceMapping::AlgorithmVolumeLabelToSurfaceMapping(Progr } frameRemap = cumulativeTable.append(*tempTable); } - for (int64_t j = 0; j < myVolDims[4]; ++j) + myLabelOut->setColumnName(i, myVolume->getMapName(i)); +#pragma omp CARET_PARFOR + for (int64_t node = 0; node < numNodes; ++node) { - AString mapName = myVolume->getMapName(i); - if (myVolDims[4] != 1) + const vector& weightRef = myWeights[node]; + map totals; + for (int v = 0; v < (int)weightRef.size(); ++v) { - mapName += " component " + AString::number(j); + int32_t voxKey = (int32_t)floor(myVolume->getValue(weightRef[v].ijk, i) + 0.5f); + map::iterator iter = totals.find(voxKey); + if (iter == totals.end()) + {//floats don't initialize to 0 + totals[voxKey] = weightRef[v].weight; + } else { + totals[voxKey] += weightRef[v].weight; + } } - int64_t thisCol = i * myVolDims[4] + j; - myLabelOut->setColumnName(thisCol, mapName); -#pragma omp CARET_PARFOR - for (int64_t node = 0; node < numNodes; ++node) + int32_t tempKey = unlabeledKey; + float bestSum = -1.0f; + for (map::iterator iter = totals.begin(); iter != totals.end(); ++iter) { - const vector& weightRef = myWeights[node]; - map totals; - for (int v = 0; v < (int)weightRef.size(); ++v) - { - int32_t voxKey = (int32_t)floor(myVolume->getValue(weightRef[v].ijk, i, j) + 0.5f); - map::iterator iter = totals.find(voxKey); - if (iter == totals.end()) - {//floats don't initialize to 0 - totals[voxKey] = weightRef[v].weight; - } else { - totals[voxKey] += weightRef[v].weight; - } - } - int32_t tempKey = unlabeledKey; - float bestSum = -1.0f; - for (map::iterator iter = totals.begin(); iter != totals.end(); ++iter) - { - if (iter->second > bestSum) - { - tempKey = iter->first; - bestSum = iter->second; - } - } - map::iterator iter = frameRemap.find(tempKey); - if (iter != frameRemap.end()) + if (iter->second > bestSum) { - myArray[node] = iter->second; - } else { - myArray[node] = tempKey;//for simplicity, assume all values in the frame are in the label table + tempKey = iter->first; + bestSum = iter->second; } } - myLabelOut->setLabelKeysForColumn(thisCol, myArray.data()); + map::iterator iter = frameRemap.find(tempKey); + if (iter != frameRemap.end()) + { + myArray[node] = iter->second; + } else { + myArray[node] = tempKey;//for simplicity, assume all values in the frame are in the label table + } } + myLabelOut->setLabelKeysForColumn(i, myArray.data()); } *(myLabelOut->getLabelTable()) = cumulativeTable; } else { const GiftiLabelTable* tempTable = myVolume->getMapLabelTable(mySubVol); + int unlabeledKey = tempTable->getUnassignedLabelKey(); if (tempTable == NULL) { throw AlgorithmException("specified subvolume is missing a label table"); } *(myLabelOut->getLabelTable()) = *tempTable; - for (int64_t j = 0; j < myVolDims[4]; ++j) - { - AString mapName = myVolume->getMapName(mySubVol); - if (myVolDims[4] != 1) + myLabelOut->setColumnName(0, myVolume->getMapName(mySubVol)); +#pragma omp CARET_PARFOR + for (int64_t node = 0; node < numNodes; ++node) + {//for simplicity, assume all values in the volume file are in the label table + const vector& weightRef = myWeights[node]; + map totals; + for (int v = 0; v < (int)weightRef.size(); ++v) { - mapName += " component " + AString::number(j); + int32_t voxKey = (int32_t)floor(myVolume->getValue(weightRef[v].ijk, mySubVol) + 0.5f); + map::iterator iter = totals.find(voxKey); + if (iter == totals.end()) + {//floats don't initialize to 0 + totals[voxKey] = weightRef[v].weight; + } else { + totals[voxKey] += weightRef[v].weight; + } } - int64_t thisCol = j; - myLabelOut->setColumnName(thisCol, mapName); -#pragma omp CARET_PARFOR - for (int64_t node = 0; node < numNodes; ++node) - {//for simplicity, assume all values in the volume file are in the label table - myArray[node] = 0;//TODO + int32_t tempKey = unlabeledKey; + float bestSum = -1.0f; + for (map::iterator iter = totals.begin(); iter != totals.end(); ++iter) + { + if (iter->second > bestSum) + { + tempKey = iter->first; + bestSum = iter->second; + } } - myLabelOut->setLabelKeysForColumn(thisCol, myArray.data()); + myArray[node] = tempKey;//for simplicity, assume all values in the frame are in the label table } + myLabelOut->setLabelKeysForColumn(0, myArray.data()); } } diff --git a/src/Algorithms/AlgorithmVolumeLabelToSurfaceMapping.h b/src/Algorithms/AlgorithmVolumeLabelToSurfaceMapping.h index 425c5ba8cb98e52b65e4f3c4ec6b7d4bb4838784..17a79a7e7fdcd629127e6ff566b2a570256fc3c5 100644 --- a/src/Algorithms/AlgorithmVolumeLabelToSurfaceMapping.h +++ b/src/Algorithms/AlgorithmVolumeLabelToSurfaceMapping.h @@ -35,7 +35,8 @@ namespace caret { AlgorithmVolumeLabelToSurfaceMapping(ProgressObject* myProgObj, const VolumeFile* myVolume, const SurfaceFile* mySurface, LabelFile* myLabelOut, const int64_t& mySubVol = -1); AlgorithmVolumeLabelToSurfaceMapping(ProgressObject* myProgObj, const VolumeFile* myVolume, const SurfaceFile* mySurface, LabelFile* myLabelOut, - const SurfaceFile* innerSurf, const SurfaceFile* outerSurf, const VolumeFile* myRoiVol = NULL, const int32_t& subdivisions = 3, + const SurfaceFile* innerSurf, const SurfaceFile* outerSurf, + const VolumeFile* myRoiVol = NULL, const int32_t& subdivisions = 3, const bool& thinColumns = false, const int64_t& mySubVol = -1); static OperationParameters* getParameters(); static void useParameters(OperationParameters* myParams, ProgressObject* myProgObj); diff --git a/src/Algorithms/AlgorithmVolumeSmoothing.cxx b/src/Algorithms/AlgorithmVolumeSmoothing.cxx index fd84fbad18682397e7f60aef16e70fddf0b067fe..d3572aa69083ca075ff2c89d84f57cd0eb4033b0 100644 --- a/src/Algorithms/AlgorithmVolumeSmoothing.cxx +++ b/src/Algorithms/AlgorithmVolumeSmoothing.cxx @@ -328,6 +328,8 @@ void AlgorithmVolumeSmoothing::smoothFrame(const float* inFrame, vector } } } +//somehow this loop set is helped by collapsing the two outers, while the other two are not helped by it +//however, we need to upgrade our windows compiler before we can use "collapse" #pragma omp CARET_PARFOR schedule(dynamic) for (int j = 0; j < myDims[1]; ++j)//and finally k { diff --git a/src/Algorithms/AlgorithmVolumeToSurfaceMapping.cxx b/src/Algorithms/AlgorithmVolumeToSurfaceMapping.cxx index de9fd9a60c3e0544e0444ca65daa0379066bf41e..455ee41d7e3960890963e457ee098c7f29a002c2 100644 --- a/src/Algorithms/AlgorithmVolumeToSurfaceMapping.cxx +++ b/src/Algorithms/AlgorithmVolumeToSurfaceMapping.cxx @@ -30,6 +30,9 @@ #include "Vector3D.h" #include "VolumeFile.h" +#include "AlgorithmSurfaceToSurface3dDistance.h" +#include "AlgorithmCreateSignedDistanceVolume.h" + #include #include @@ -68,6 +71,9 @@ OperationParameters* AlgorithmVolumeToSurfaceMapping::getParameters() roiVol->addVolumeParameter(1, "roi-volume", "the volume file"); OptionalParameter* ribbonSubdiv = ribbonOpt->createOptionalParameter(4, "-voxel-subdiv", "voxel divisions while estimating voxel weights"); ribbonSubdiv->addIntegerParameter(1, "subdiv-num", "number of subdivisions, default 3"); + ribbonOpt->createOptionalParameter(7, "-thin-columns", "use non-overlapping polyhedra"); + OptionalParameter* gaussianOpt = ribbonOpt->createOptionalParameter(8, "-gaussian", "reduce weight to voxels that aren't near "); + gaussianOpt->addDoubleParameter(1, "scale", "value to multiply the local thickness by, to get the gaussian sigma"); OptionalParameter* ribbonWeights = ribbonOpt->createOptionalParameter(5, "-output-weights", "write the voxel weights for a vertex to a volume file"); ribbonWeights->addIntegerParameter(1, "vertex", "the vertex number to get the voxel weights for, 0-based"); ribbonWeights->addVolumeOutputParameter(2, "weights-out", "volume to write the weights to"); @@ -78,6 +84,7 @@ OperationParameters* AlgorithmVolumeToSurfaceMapping::getParameters() myelinStyleOpt->addVolumeParameter(1, "ribbon-roi", "an roi volume of the cortical ribbon for this hemisphere"); myelinStyleOpt->addMetricParameter(2, "thickness", "a metric file of cortical thickness"); myelinStyleOpt->addDoubleParameter(3, "sigma", "gaussian kernel in mm for weighting voxels within range"); + myelinStyleOpt->createOptionalParameter(4, "-legacy-bug", "emulate old v1.2.3 and earlier code that didn't follow a cylinder cutoff"); OptionalParameter* subvolumeSelect = ret->createOptionalParameter(7, "-subvol-select", "select a single subvolume to map"); subvolumeSelect->addStringParameter(1, "subvol", "the subvolume number or name"); @@ -87,13 +94,18 @@ OperationParameters* AlgorithmVolumeToSurfaceMapping::getParameters() "linear interpolation based on the voxels immediately on each side of the vertex's position.\n\n" + "The ribbon mapping method constructs a polyhedron from the vertex's neighbors on each " + "surface, and estimates the amount of this polyhedron's volume that falls inside any nearby voxels, to use as the weights for sampling. " + + "If -thin-columns is specified, the polyhedron uses the edge midpoints and triangle centroids, so that neighboring vertices do not have overlapping polyhedra. " + + "This may require increasing -voxel-subdiv to get enough samples in each voxel to reliably land inside these smaller polyhedra. " + "The volume ROI is useful to exclude partial volume effects of voxels the surfaces pass through, and will cause the mapping to ignore " + "voxels that don't have a positive value in the mask. The subdivision number specifies how it approximates the amount of the volume the polyhedron " + "intersects, by splitting each voxel into NxNxN pieces, and checking whether the center of each piece is inside the polyhedron. If you have very large " + - "voxels, consider increasing this if you get zeros in your output.\n\n" + - "The myelin style method uses part of the caret5 myelin mapping command to do the mapping: for each surface vertex, take all voxels closer than the thickness at the vertex " + - "that are within the ribbon ROI, and less than half the thickness value away from the vertex along the direction of the surface normal, and apply a gaussian kernel " + - "with the specified sigma to them to get the weights to use." + "voxels, consider increasing this if you get zeros in your output. " + + "The -gaussian option makes it act more like the myelin method, where the distance of a voxel from is used to downweight the voxel.\n\n" + + "The myelin style method uses part of the caret5 myelin mapping command to do the mapping: for each surface vertex, take all voxels that are in a cylinder " + + "with width and height equal to cortical thickness, centered on the vertex and aligned with the surface normal, and that are also within the ribbon ROI, " + + "and apply a gaussian kernel with the specified sigma to them to get the weights to use. " + + "The -legacy-bug flag reverts to the unintended behavior present from the initial implementation up to and including v1.2.3, which had only the tangential cutoff " + + "and a bounding box intended to be larger than where the cylinder cutoff should have been." ); return ret; } @@ -196,6 +208,14 @@ void AlgorithmVolumeToSurfaceMapping::useParameters(OperationParameters* myParam throw AlgorithmException("invalid number of subdivisions specified"); } } + bool thinColumns = ribbonOpt->getOptionalParameter(7)->m_present; + float gaussScale = -1.0f; + OptionalParameter* gaussianOpt = ribbonOpt->getOptionalParameter(8); + if (gaussianOpt->m_present) + { + gaussScale = (float)gaussianOpt->getDouble(1); + if (!(gaussScale > 0.0f)) throw AlgorithmException("gaussian scale must be positive"); + } int weightsOutVertex = -1; VolumeFile* weightsOut = NULL; OptionalParameter* ribbonWeights = ribbonOpt->getOptionalParameter(5); @@ -204,7 +224,8 @@ void AlgorithmVolumeToSurfaceMapping::useParameters(OperationParameters* myParam weightsOutVertex = (int)ribbonWeights->getInteger(1); weightsOut = ribbonWeights->getOutputVolume(2); } - AlgorithmVolumeToSurfaceMapping(myProgObj, myVolume, mySurface, myMetricOut, innerSurf, outerSurf, myRoiVol, subdivisions, mySubVol, weightsOutVertex, weightsOut); + AlgorithmVolumeToSurfaceMapping(myProgObj, myVolume, mySurface, myMetricOut, innerSurf, outerSurf, myRoiVol, subdivisions, thinColumns, + mySubVol, gaussScale, weightsOutVertex, weightsOut); OptionalParameter* ribbonWeightsText = ribbonOpt->getOptionalParameter(6); if (ribbonWeightsText->m_present) {//do this after the algorithm, to let it do the error condition checking @@ -213,7 +234,7 @@ void AlgorithmVolumeToSurfaceMapping::useParameters(OperationParameters* myParam vector > myWeights; const float* roiFrame = NULL; if (myRoiVol != NULL) roiFrame = myRoiVol->getFrame(); - RibbonMappingHelper::computeWeightsRibbon(myWeights, myVolume->getVolumeSpace(), innerSurf, outerSurf, roiFrame, subdivisions); + AlgorithmVolumeToSurfaceMapping::precomputeWeightsRibbon(myWeights, myVolume->getVolumeSpace(), innerSurf, outerSurf, roiFrame, subdivisions, thinColumns, mySurface, gaussScale); for (int i = 0; i < (int)myWeights.size(); ++i) { outFile << i << ", " << myWeights[i].size(); @@ -235,7 +256,8 @@ void AlgorithmVolumeToSurfaceMapping::useParameters(OperationParameters* myParam VolumeFile* roi = myelinStyleOpt->getVolume(1); MetricFile* thickness = myelinStyleOpt->getMetric(2); float sigma = (float)myelinStyleOpt->getDouble(3); - AlgorithmVolumeToSurfaceMapping(myProgObj, myVolume, mySurface, myMetricOut, roi, thickness, sigma, mySubVol); + bool oldCutoffBug = myelinStyleOpt->getOptionalParameter(4)->m_present; + AlgorithmVolumeToSurfaceMapping(myProgObj, myVolume, mySurface, myMetricOut, roi, thickness, sigma, mySubVol, oldCutoffBug); break; } default: @@ -340,7 +362,8 @@ AlgorithmVolumeToSurfaceMapping::AlgorithmVolumeToSurfaceMapping(ProgressObject* //ribbon mapping AlgorithmVolumeToSurfaceMapping::AlgorithmVolumeToSurfaceMapping(ProgressObject* myProgObj, const VolumeFile* myVolume, const SurfaceFile* mySurface, MetricFile* myMetricOut, const SurfaceFile* innerSurf, const SurfaceFile* outerSurf, const VolumeFile* roiVol, - const int32_t& subdivisions, const int64_t& mySubVol, const int& weightsOutVertex, VolumeFile* weightsOut) : AbstractAlgorithm(myProgObj) + const int32_t& subdivisions, const bool& thinColumns, const int64_t& mySubVol, const float& gaussScale, + const int& weightsOutVertex, VolumeFile* weightsOut) : AbstractAlgorithm(myProgObj) { LevelProgress myProgress(myProgObj); vector myVolDims; @@ -377,7 +400,7 @@ AlgorithmVolumeToSurfaceMapping::AlgorithmVolumeToSurfaceMapping(ProgressObject* vector > myWeights; const float* roiFrame = NULL; if (roiVol != NULL) roiFrame = roiVol->getFrame(); - RibbonMappingHelper::computeWeightsRibbon(myWeights, myVolume->getVolumeSpace(), innerSurf, outerSurf, roiFrame, subdivisions); + precomputeWeightsRibbon(myWeights, myVolume->getVolumeSpace(), innerSurf, outerSurf, roiFrame, subdivisions, thinColumns, mySurface, gaussScale); if (weightsOut != NULL) { weightsOut->setValueAllVoxels(0.0f); @@ -461,9 +484,40 @@ AlgorithmVolumeToSurfaceMapping::AlgorithmVolumeToSurfaceMapping(ProgressObject* } } +void AlgorithmVolumeToSurfaceMapping::precomputeWeightsRibbon(vector >& myWeights, const VolumeSpace& volSpace, + const SurfaceFile* innerSurf, const SurfaceFile* outerSurf, const float* roiFrame, + const int& subdivisions, const bool& thinColumns, const SurfaceFile* gaussSurf, const float& gaussScale) +{ + RibbonMappingHelper::computeWeightsRibbon(myWeights, volSpace, innerSurf, outerSurf, roiFrame, subdivisions, thinColumns); + if (gaussScale > 0.0f) + { + VolumeFile signedDistVol; + MetricFile thickness; + AlgorithmSurfaceToSurface3dDistance(NULL, innerSurf, outerSurf, &thickness); + int numNodes = innerSurf->getNumberOfNodes(); + float maxThick = thickness.getValue(0, 0); + for (int i = 1; i < numNodes; ++i) + { + float thisThick = thickness.getValue(i, 0); + if (thisThick > maxThick) maxThick = thisThick; + } + signedDistVol.reinitialize(volSpace); + AlgorithmCreateSignedDistanceVolume(NULL, gaussSurf, &signedDistVol, NULL, maxThick * gaussScale * 3.0f, maxThick * gaussScale * 3.0f);//if we somehow have a voxel really far away compared to thickness, treat it as at least 3 sigma + for (int i = 0; i < numNodes; ++i) + {//modify the weights from the ribbon helper + vector& nodeWeights = myWeights[i]; + for (int j = 0; j < (int)nodeWeights.size(); ++j) + { + float toSquare = signedDistVol.getValue(nodeWeights[j].ijk) / (thickness.getValue(i, 0) * gaussScale);//negatives are fine, we are going to square it + nodeWeights[j].weight *= exp(-toSquare * toSquare / 2); + } + } + } +} + //myelin style mapping AlgorithmVolumeToSurfaceMapping::AlgorithmVolumeToSurfaceMapping(ProgressObject* myProgObj, const VolumeFile* myVolume, const SurfaceFile* mySurface, MetricFile* myMetricOut, - const VolumeFile* roiVol, const MetricFile* thickness, const float& sigma, const int64_t& mySubVol): AbstractAlgorithm(myProgObj) + const VolumeFile* roiVol, const MetricFile* thickness, const float& sigma, const int64_t& mySubVol, const bool& oldCutoffBug): AbstractAlgorithm(myProgObj) { LevelProgress myProgress(myProgObj); vector myVolDims; @@ -487,7 +541,7 @@ AlgorithmVolumeToSurfaceMapping::AlgorithmVolumeToSurfaceMapping(ProgressObject* myMetricOut->setNumberOfNodesAndColumns(numNodes, numColumns); myMetricOut->setStructure(mySurface->getStructure()); vector > myWeights; - precomputeWeightsMyelin(myWeights, mySurface, roiVol, thickness, sigma); + precomputeWeightsMyelin(myWeights, mySurface, roiVol, thickness, sigma, oldCutoffBug); vector myScratch(numNodes); if (mySubVol == -1) { @@ -501,7 +555,7 @@ AlgorithmVolumeToSurfaceMapping::AlgorithmVolumeToSurfaceMapping(ProgressObject* { metricLabel += " component " + AString::number(j); } - metricLabel += " ribbon constrained"; + metricLabel += " myelin style"; myMetricOut->setColumnName(thisCol, metricLabel); #pragma omp CARET_PARFOR schedule(dynamic) for (int64_t node = 0; node < numNodes; ++node) @@ -525,7 +579,7 @@ AlgorithmVolumeToSurfaceMapping::AlgorithmVolumeToSurfaceMapping(ProgressObject* { metricLabel += " component " + AString::number(j); } - metricLabel += " ribbon constrained"; + metricLabel += " myelin style"; int64_t thisCol = j; myMetricOut->setColumnName(thisCol, metricLabel); #pragma omp CARET_PARFOR schedule(dynamic) @@ -545,7 +599,7 @@ AlgorithmVolumeToSurfaceMapping::AlgorithmVolumeToSurfaceMapping(ProgressObject* } void AlgorithmVolumeToSurfaceMapping::precomputeWeightsMyelin(vector >& myWeights, const SurfaceFile* mySurface, const VolumeFile* roiVol, - const MetricFile* thickness, const float& sigma) + const MetricFile* thickness, const float& sigma, const bool& oldCutoffBug) { int64_t numNodes = mySurface->getNumberOfNodes(); myWeights.clear(); @@ -573,12 +627,17 @@ void AlgorithmVolumeToSurfaceMapping::precomputeWeightsMyelin(vectorgetCoordinate(node), nodeIndices, nodenormal = normals + (node * 3); roiVol->spaceToIndex(nodeCoord, nodeIndices); float nodeThick = thicknessData[node]; + float cylinderCorner = nodeThick * sqrt(5.0f) / 2.0f; + if (oldCutoffBug) + { + cylinderCorner = nodeThick;//old code didn't use a large enough box to always cover the cylinder corner + } int64_t min[3], max[3]; for (int i = 0; i < 3; ++i) { - min[i] = (int)ceil(nodeIndices[i] - range[i] * nodeThick); + min[i] = (int)ceil(nodeIndices[i] - range[i] * cylinderCorner); if (min[i] < 0) min[i] = 0; - max[i] = (int)floor(nodeIndices[i] + range[i] * nodeThick) + 1; + max[i] = (int)floor(nodeIndices[i] + range[i] * cylinderCorner) + 1; if (max[i] > myDims[i]) max[i] = myDims[i]; } double weightTotal = 0.0; @@ -592,8 +651,8 @@ void AlgorithmVolumeToSurfaceMapping::precomputeWeightsMyelin(vectorindexToSpace(ijk, voxelCoord); Vector3D delta = voxelCoord - nodeCoord; - if (abs(nodenormal.dot(delta)) < 0.5f * nodeThick && roiVol->getValue(ijk) > 0.0f) - { + if (abs(nodenormal.dot(delta)) < 0.5f * nodeThick && (oldCutoffBug || nodenormal.cross(delta).length() < nodeThick) && roiVol->getValue(ijk) > 0.0f) + {//old code ignored the tangential distance, only using the box edge float weight = exp(delta.lengthsquared() * invnegsigmasquaredx2); myWeights[node].push_back(VoxelWeight(weight, ijk)); weightTotal += weight; diff --git a/src/Algorithms/AlgorithmVolumeToSurfaceMapping.h b/src/Algorithms/AlgorithmVolumeToSurfaceMapping.h index bb4a6c1d816a778d722f160a91f00e14f0a8839e..902f316ccaee49f55b04e424f465c1cb57b02de6 100644 --- a/src/Algorithms/AlgorithmVolumeToSurfaceMapping.h +++ b/src/Algorithms/AlgorithmVolumeToSurfaceMapping.h @@ -34,7 +34,10 @@ namespace caret { class AlgorithmVolumeToSurfaceMapping : public AbstractAlgorithm { AlgorithmVolumeToSurfaceMapping(); - void precomputeWeightsMyelin(std::vector >& myWeights, const SurfaceFile* mySurface, const VolumeFile* roiVol, const MetricFile* thickness, const float& sigma); + static void precomputeWeightsMyelin(std::vector >& myWeights, const SurfaceFile* mySurface, const VolumeFile* roiVol, + const MetricFile* thickness, const float& sigma, const bool& oldCutoffBug); + static void precomputeWeightsRibbon(std::vector >& myWeights, const VolumeSpace& volSpace, const SurfaceFile* innerSurf, const SurfaceFile* outerSurf, + const float* roiFrame, const int& subdivisions, const bool& thinColumns, const SurfaceFile* gaussSurf, const float& gaussScale); enum Method { TRILINEAR, @@ -50,10 +53,12 @@ namespace caret { AlgorithmVolumeToSurfaceMapping(ProgressObject* myProgObj, const VolumeFile* myVolume, const SurfaceFile* mySurface, MetricFile* myMetricOut, const VolumeFile::InterpType& myMethod, const int64_t& mySubVol = -1); AlgorithmVolumeToSurfaceMapping(ProgressObject* myProgObj, const VolumeFile* myVolume, const SurfaceFile* mySurface, MetricFile* myMetricOut, - const SurfaceFile* innerSurf, const SurfaceFile* outerSurf, const VolumeFile* roiVol = NULL, const int32_t& subdivisions = 3, - const int64_t& mySubVol = -1, const int& weightsOutVertex = -1, VolumeFile* weightsOut = NULL); + const SurfaceFile* innerSurf, const SurfaceFile* outerSurf, + const VolumeFile* roiVol = NULL, const int32_t& subdivisions = 3, const bool& thinColumns = false, + const int64_t& mySubVol = -1, const float& gaussScale = -1.0f, + const int& weightsOutVertex = -1, VolumeFile* weightsOut = NULL); AlgorithmVolumeToSurfaceMapping(ProgressObject* myProgObj, const VolumeFile* myVolume, const SurfaceFile* mySurface, MetricFile* myMetricOut, - const VolumeFile* roiVol, const MetricFile* thickness, const float& sigma, const int64_t& mySubVol = -1); + const VolumeFile* roiVol, const MetricFile* thickness, const float& sigma, const int64_t& mySubVol = -1, const bool& oldCutoffBug = false); static OperationParameters* getParameters(); static void useParameters(OperationParameters* myParams, ProgressObject* myProgObj); static AString getCommandSwitch(); diff --git a/src/Algorithms/AlgorithmVolumeWarpfieldAffineRegression.cxx b/src/Algorithms/AlgorithmVolumeWarpfieldAffineRegression.cxx new file mode 100644 index 0000000000000000000000000000000000000000..4bd4d5dc7997b68119bb1eaa8eeb853a512c765a --- /dev/null +++ b/src/Algorithms/AlgorithmVolumeWarpfieldAffineRegression.cxx @@ -0,0 +1,167 @@ +/*LICENSE_START*/ +/* + * Copyright (C) 2016 Washington University School of Medicine + * + * 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 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ +/*LICENSE_END*/ + +#include "AlgorithmVolumeWarpfieldAffineRegression.h" +#include "AlgorithmException.h" + +#include "AffineFile.h" +#include "MatrixFunctions.h" +#include "Vector3D.h" +#include "VolumeFile.h" +#include "WarpfieldFile.h" + +using namespace caret; +using namespace std; + +AString AlgorithmVolumeWarpfieldAffineRegression::getCommandSwitch() +{ + return "-volume-warpfield-affine-regression"; +} + +AString AlgorithmVolumeWarpfieldAffineRegression::getShortDescription() +{ + return "REGRESS AFFINE FROM WARPFIELD"; +} + +OperationParameters* AlgorithmVolumeWarpfieldAffineRegression::getParameters() +{ + OperationParameters* ret = new OperationParameters(); + + ret->addStringParameter(1, "warpfield", "the input warpfield"); + + ret->addStringParameter(2, "affine-out", "output - the output affine file");//HACK: fake the output help formatting + + OptionalParameter* roiOpt = ret->createOptionalParameter(3, "-roi", "only consider voxels within a mask (e.g., a brain mask)"); + roiOpt->addVolumeParameter(1, "roi-vol", "the mask volume"); + + OptionalParameter* fnirtInOpt = ret->createOptionalParameter(4, "-fnirt", "input is a fnirt warpfield"); + fnirtInOpt->addStringParameter(1, "source-volume", "the source volume used when generating the fnirt warpfield"); + + OptionalParameter* flirtOutOpt = ret->createOptionalParameter(5, "-flirt-out", "write output as a flirt matrix rather than a world coordinate transform"); + flirtOutOpt->addStringParameter(1, "source-volume", "the volume you want to apply the transform to"); + flirtOutOpt->addStringParameter(2, "target-volume", "the target space you want the transformed volume to match"); + + ret->setHelpText( + AString("For all voxels in the warpfield, do a regression that predicts the post-warp coordinate from the source coordinate. ") + + "When -roi is specified, only consider voxels with a value greater than 0 in .\n\n" + + "The default is to expect the warpfield to be in relative world coordinates (mm space), and to write the output as a world affine (mm space to mm space). " + + "If you are using FSL-created files and utilities, specify -fnirt and -flirt as needed, as their coordinate conventions are different." + ); + return ret; +} + +void AlgorithmVolumeWarpfieldAffineRegression::useParameters(OperationParameters* myParams, ProgressObject* myProgObj) +{ + AString warpfieldName = myParams->getString(1); + AString affineOutName = myParams->getString(2); + VolumeFile* myRoi = NULL; + OptionalParameter* roiOpt = myParams->getOptionalParameter(3); + if (roiOpt->m_present) + { + myRoi = roiOpt->getVolume(1); + } + WarpfieldFile myWarpfield; + OptionalParameter* fnirtInOpt = myParams->getOptionalParameter(4); + if (fnirtInOpt->m_present) + { + myWarpfield.readFnirt(warpfieldName, fnirtInOpt->getString(1)); + } else { + myWarpfield.readWorld(warpfieldName); + } + FloatMatrix affineMatOut; + AlgorithmVolumeWarpfieldAffineRegression(myProgObj, myWarpfield.getWarpfield(), affineMatOut, myRoi); + AffineFile affineOut; + affineOut.setMatrix(affineMatOut); + OptionalParameter* flirtOutOpt = myParams->getOptionalParameter(5); + if (flirtOutOpt->m_present) + { + affineOut.writeFlirt(affineOutName, flirtOutOpt->getString(1), flirtOutOpt->getString(2)); + } else { + affineOut.writeWorld(affineOutName); + } +} + +AlgorithmVolumeWarpfieldAffineRegression::AlgorithmVolumeWarpfieldAffineRegression(ProgressObject* myProgObj, const VolumeFile* warpVol, FloatMatrix& affineMatOut, const VolumeFile* myRoi) : AbstractAlgorithm(myProgObj) +{ + LevelProgress myProgress(myProgObj); + if (myRoi != NULL && !warpVol->matchesVolumeSpace(myRoi)) + { + throw AlgorithmException("roi volume does not match the volume space of the warpfield"); + } + vector voldims = warpVol->getDimensions(); + if (voldims[3] != 3) + { + throw AlgorithmException("warpfield volume does not have 3 subvolumes"); + } + //three regressions with the same predictors and different target values + vector > indep(4, vector(4, 0.0)), dep(4, vector(3, 0.0)); + for (int64_t k = 0; k < voldims[2]; ++k) + { + for (int64_t j = 0; j < voldims[1]; ++j) + { + for (int64_t i = 0; i < voldims[0]; ++i) + { + if (myRoi == NULL || myRoi->getValue(i, j, k) > 0.0f) + { + Vector3D outCoord, displacement, inCoord;//outCoord is the post-warp coordinate + warpVol->indexToSpace(i, j, k, outCoord); + displacement[0] = warpVol->getValue(i, j, k, 0); + displacement[1] = warpVol->getValue(i, j, k, 1); + displacement[2] = warpVol->getValue(i, j, k, 2); + inCoord = outCoord + displacement; + for (int dim1 = 0; dim1 < 3; ++dim1)//compute X' * X and X' * Y + { + for (int dim2 = 0; dim2 < 3; ++dim2) + { + indep[dim1][dim2] += inCoord[dim1] * inCoord[dim2]; + dep[dim2][dim1] += outCoord[dim1] * inCoord[dim2]; + } + indep[dim1][3] += inCoord[dim1]; + indep[3][dim1] += inCoord[dim1]; + dep[3][dim1] += outCoord[dim1]; + } + indep[3][3] += 1.0; + } + } + } + } + vector > rrefMat; + MatrixFunctions::horizCat(indep, dep, rrefMat); + MatrixFunctions::rref(rrefMat); + affineMatOut = FloatMatrix::identity(4); + for (int dim1 = 0; dim1 < 3; ++dim1) + { + for (int dim2 = 0; dim2 < 4; ++dim2) + { + affineMatOut[dim1][dim2] = rrefMat[dim2][4 + dim1]; + } + } +} + +float AlgorithmVolumeWarpfieldAffineRegression::getAlgorithmInternalWeight() +{ + return 1.0f;//override this if needed, if the progress bar isn't smooth +} + +float AlgorithmVolumeWarpfieldAffineRegression::getSubAlgorithmWeight() +{ + //return AlgorithmInsertNameHere::getAlgorithmWeight();//if you use a subalgorithm + return 0.0f; +} diff --git a/src/Algorithms/AlgorithmVolumeWarpfieldAffineRegression.h b/src/Algorithms/AlgorithmVolumeWarpfieldAffineRegression.h new file mode 100644 index 0000000000000000000000000000000000000000..bea406731dbfcae5ecf9b3e741f376a55d5fcf3f --- /dev/null +++ b/src/Algorithms/AlgorithmVolumeWarpfieldAffineRegression.h @@ -0,0 +1,48 @@ +#ifndef __ALGORITHM_VOLUME_WARPFIELD_AFFINE_REGRESSION_H__ +#define __ALGORITHM_VOLUME_WARPFIELD_AFFINE_REGRESSION_H__ + +/*LICENSE_START*/ +/* + * Copyright (C) 2016 Washington University School of Medicine + * + * 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 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ +/*LICENSE_END*/ + +#include "AbstractAlgorithm.h" + +#include "FloatMatrix.h" + +namespace caret { + + class AlgorithmVolumeWarpfieldAffineRegression : public AbstractAlgorithm + { + AlgorithmVolumeWarpfieldAffineRegression(); + protected: + static float getSubAlgorithmWeight(); + static float getAlgorithmInternalWeight(); + public: + AlgorithmVolumeWarpfieldAffineRegression(ProgressObject* myProgObj, const VolumeFile* warpVol, FloatMatrix& affineMatOut, const VolumeFile* myRoi = NULL); + static OperationParameters* getParameters(); + static void useParameters(OperationParameters* myParams, ProgressObject* myProgObj); + static AString getCommandSwitch(); + static AString getShortDescription(); + }; + + typedef TemplateAutoOperation AutoAlgorithmVolumeWarpfieldAffineRegression; + +} + +#endif //__ALGORITHM_VOLUME_WARPFIELD_AFFINE_REGRESSION_H__ diff --git a/src/Algorithms/AlgorithmVolumeWarpfieldResample.cxx b/src/Algorithms/AlgorithmVolumeWarpfieldResample.cxx index eb57e75636239c9c87381dc14ab6c5731a786807..e79e211c7a36701263e383ddde05de9486163471 100644 --- a/src/Algorithms/AlgorithmVolumeWarpfieldResample.cxx +++ b/src/Algorithms/AlgorithmVolumeWarpfieldResample.cxx @@ -93,7 +93,9 @@ void AlgorithmVolumeWarpfieldResample::useParameters(OperationParameters* myPara } NiftiIO refSpaceIO; refSpaceIO.openRead(refSpaceName); - AlgorithmVolumeWarpfieldResample(myProgObj, inVol, myWarpfield.getWarpfield(), refSpaceIO.getDimensions().data(), refSpaceIO.getHeader().getSForm(), myMethod, outVol); + vector refDims = refSpaceIO.getDimensions(); + if (refDims.size() < 3) refDims.resize(3, 1); + AlgorithmVolumeWarpfieldResample(myProgObj, inVol, myWarpfield.getWarpfield(), refDims.data(), refSpaceIO.getHeader().getSForm(), myMethod, outVol); } AlgorithmVolumeWarpfieldResample::AlgorithmVolumeWarpfieldResample(ProgressObject* myProgObj, const VolumeFile* inVol, const VolumeFile* warpfield, @@ -121,6 +123,10 @@ AlgorithmVolumeWarpfieldResample::AlgorithmVolumeWarpfieldResample(ProgressObjec *(outVol->getMapLabelTable(i)) = *(inVol->getMapLabelTable(i)); } } + for (int64_t i = 0; i < numMaps; ++i) + { + outVol->setMapName(i, inVol->getMapName(i)); + } for (int64_t c = 0; c < numComponents; ++c) { for (int64_t b = 0; b < numMaps; ++b) diff --git a/src/Algorithms/CMakeLists.txt b/src/Algorithms/CMakeLists.txt index a91f60a71135d115c55442cfc9f2f58827f4526e..e7ff9b755c6a79bdcffd76d4e1ba30aa0c30fa02 100644 --- a/src/Algorithms/CMakeLists.txt +++ b/src/Algorithms/CMakeLists.txt @@ -11,7 +11,12 @@ SET(QT_DONT_USE_QTGUI) # # Add QT for includes # -INCLUDE (${QT_USE_FILE}) +if(Qt5_FOUND) + include_directories(${Qt5Core_INCLUDE_DIRS}) +endif() +IF (QT4_FOUND) + INCLUDE(${QT_USE_FILE}) +ENDIF () # # Create the helper library @@ -37,6 +42,7 @@ AlgorithmCiftiFalseCorrelation.h AlgorithmCiftiFindClusters.h AlgorithmCiftiGradient.h AlgorithmCiftiLabelAdjacency.h +AlgorithmCiftiLabelProbability.h AlgorithmCiftiLabelToBorder.h AlgorithmCiftiLabelToROI.h AlgorithmCiftiMergeDense.h @@ -64,6 +70,7 @@ AlgorithmGiftiLabelToROI.h AlgorithmLabelDilate.h AlgorithmLabelErode.h AlgorithmLabelModifyKeys.h +AlgorithmLabelProbability.h AlgorithmLabelResample.h AlgorithmLabelToBorder.h AlgorithmLabelToVolumeMapping.h @@ -109,6 +116,7 @@ AlgorithmSurfaceWedgeVolume.h AlgorithmVolumeAffineResample.h AlgorithmVolumeAllLabelsToROIs.h AlgorithmVolumeDilate.h +AlgorithmVolumeDistortion.h AlgorithmVolumeErode.h AlgorithmVolumeEstimateFWHM.h AlgorithmVolumeExtrema.h @@ -128,6 +136,7 @@ AlgorithmVolumeSmoothing.h AlgorithmVolumeTFCE.h AlgorithmVolumeToSurfaceMapping.h AlgorithmVolumeVectorOperation.h +AlgorithmVolumeWarpfieldAffineRegression.h AlgorithmVolumeWarpfieldResample.h OverlapLogicEnum.h @@ -151,6 +160,7 @@ AlgorithmCiftiFalseCorrelation.cxx AlgorithmCiftiFindClusters.cxx AlgorithmCiftiGradient.cxx AlgorithmCiftiLabelAdjacency.cxx +AlgorithmCiftiLabelProbability.cxx AlgorithmCiftiLabelToBorder.cxx AlgorithmCiftiLabelToROI.cxx AlgorithmCiftiMergeDense.cxx @@ -178,6 +188,7 @@ AlgorithmGiftiLabelToROI.cxx AlgorithmLabelDilate.cxx AlgorithmLabelErode.cxx AlgorithmLabelModifyKeys.cxx +AlgorithmLabelProbability.cxx AlgorithmLabelResample.cxx AlgorithmLabelToBorder.cxx AlgorithmLabelToVolumeMapping.cxx @@ -223,6 +234,7 @@ AlgorithmSurfaceWedgeVolume.cxx AlgorithmVolumeAffineResample.cxx AlgorithmVolumeAllLabelsToROIs.cxx AlgorithmVolumeDilate.cxx +AlgorithmVolumeDistortion.cxx AlgorithmVolumeErode.cxx AlgorithmVolumeEstimateFWHM.cxx AlgorithmVolumeExtrema.cxx @@ -242,10 +254,13 @@ AlgorithmVolumeSmoothing.cxx AlgorithmVolumeTFCE.cxx AlgorithmVolumeToSurfaceMapping.cxx AlgorithmVolumeVectorOperation.cxx +AlgorithmVolumeWarpfieldAffineRegression.cxx AlgorithmVolumeWarpfieldResample.cxx OverlapLogicEnum.cxx ) +TARGET_LINK_LIBRARIES(Algorithms ${CARET_QT5_LINK}) + # # Find Headers # diff --git a/src/Annotations/Annotation.cxx b/src/Annotations/Annotation.cxx index 4d03e9aea83b4a8c640b70ca7c8f139c699367cb..144808393eae196d8b996e1825367d3c69ca69de 100644 --- a/src/Annotations/Annotation.cxx +++ b/src/Annotations/Annotation.cxx @@ -124,7 +124,12 @@ Annotation::copyHelperAnnotation(const Annotation& obj) m_coordinateSpace = obj.m_coordinateSpace; m_tabIndex = obj.m_tabIndex; m_windowIndex = obj.m_windowIndex; - m_lineWidth = obj.m_lineWidth; + m_viewportCoordinateSpaceViewport[0] = obj.m_viewportCoordinateSpaceViewport[0]; + m_viewportCoordinateSpaceViewport[1] = obj.m_viewportCoordinateSpaceViewport[1]; + m_viewportCoordinateSpaceViewport[2] = obj.m_viewportCoordinateSpaceViewport[2]; + m_viewportCoordinateSpaceViewport[3] = obj.m_viewportCoordinateSpaceViewport[3]; + m_lineWidthPixels = obj.m_lineWidthPixels; + m_lineWidthPercentage = obj.m_lineWidthPercentage; m_colorLine = obj.m_colorLine; m_colorBackground = obj.m_colorBackground; m_customColorBackground[0] = obj.m_customColorBackground[0]; @@ -136,6 +141,8 @@ Annotation::copyHelperAnnotation(const Annotation& obj) m_customColorLine[2] = obj.m_customColorLine[2]; m_customColorLine[3] = obj.m_customColorLine[3]; + m_properties = obj.m_properties; + *m_displayGroupAndTabItemHelper = *obj.m_displayGroupAndTabItemHelper; /* @@ -204,6 +211,13 @@ Annotation::clone() const switch (text->getFontSizeType()) { case AnnotationTextFontSizeTypeEnum::PERCENTAGE_OF_VIEWPORT_HEIGHT: + { + const AnnotationPercentSizeText* pctText = dynamic_cast(text); + CaretAssert(pctText); + myClone = new AnnotationPercentSizeText(*pctText); + } + break; + case AnnotationTextFontSizeTypeEnum::PERCENTAGE_OF_VIEWPORT_WIDTH: { const AnnotationPercentSizeText* pctText = dynamic_cast(text); CaretAssert(pctText); @@ -252,17 +266,20 @@ Annotation::replaceWithCopyOfAnnotation(const Annotation* annotation) } /** - * @return Is this annotation deletable? This method may be overridden - * by annotations (such as colorbars) that cannot be deleted. + * Set this instance modified. */ -bool -Annotation::isDeletable() const +void +Annotation::setModified() { - return true; + /* + * While this method does not need to be overridden, + * doing so enables debugging of invalid modification status. + */ + CaretObjectTracksModification::setModified(); } /** - * @return Is this annotation requiring that it be kept in a fixed + * @return Is this annotation requiring that it be kept in a fixed * aspect ratio? By default, this is false. This method may be * overridden by annotations that require a fixed aspect ratio * (such as an image annotaiton). @@ -299,7 +316,8 @@ Annotation::applyColoringFromOther(const Annotation* otherAnnotation) m_colorBackground = otherAnnotation->m_colorBackground; m_colorLine = otherAnnotation->m_colorLine; - m_lineWidth = otherAnnotation->m_lineWidth; + m_lineWidthPixels = otherAnnotation->m_lineWidthPixels; + m_lineWidthPercentage = otherAnnotation->m_lineWidthPercentage; for (int32_t i = 0; i < 4; i++) { m_customColorBackground[i] = otherAnnotation->m_customColorBackground[i]; @@ -366,6 +384,10 @@ Annotation::initializeAnnotationMembers() m_tabIndex = -1; m_windowIndex = -1; + m_viewportCoordinateSpaceViewport[0] = 0; + m_viewportCoordinateSpaceViewport[1] = 0; + m_viewportCoordinateSpaceViewport[2] = 0; + m_viewportCoordinateSpaceViewport[3] = 0; m_displayGroupAndTabItemHelper = new DisplayGroupAndTabItemHelper(); @@ -383,7 +405,8 @@ Annotation::initializeAnnotationMembers() switch (m_attributeDefaultType) { case AnnotationAttributesDefaultTypeEnum::NORMAL: - m_lineWidth = 3.0; + m_lineWidthPixels = 3.0; + m_lineWidthPercentage = 1.0; m_colorBackground = CaretColorEnum::NONE; m_colorLine = CaretColorEnum::WHITE; @@ -418,7 +441,8 @@ Annotation::initializeAnnotationMembers() break; case AnnotationAttributesDefaultTypeEnum::USER: { - m_lineWidth = s_userDefaultLineWidth; + m_lineWidthPixels = s_userDefaultLineWidthPixelsObsolete; + m_lineWidthPercentage = s_userDefaultLineWidthPercentage; m_colorBackground = s_userDefaultColorBackground; m_colorLine = s_userDefaultColorLine; @@ -510,6 +534,12 @@ Annotation::initializeAnnotationMembers() } } + initializeProperties(); + + if (m_coordinateSpace == AnnotationCoordinateSpaceEnum::VIEWPORT) { + setPropertiesForSpecializedUsage(PropertiesSpecializedUsage::VIEWPORT_ANNOTATION); + } + /* * Note: The 'const' members are not saved to the scene as they * are set by constructor. @@ -523,7 +553,7 @@ Annotation::initializeAnnotationMembers() m_sceneAssistant->add("m_displayGroupAndTabItemHelper", "DisplayGroupAndTabItemHelper", m_displayGroupAndTabItemHelper); - if (m_type == AnnotationTypeEnum::COLOR_BAR) { + if (testProperty(Property::SCENE_CONTAINS_ATTRIBUTES)) { m_sceneAssistant->add("m_coordinateSpace", &m_coordinateSpace); m_sceneAssistant->add("m_colorBackground", @@ -629,6 +659,8 @@ Annotation::setCoordinateSpace(const AnnotationCoordinateSpaceEnum::Enum coordin { if (m_coordinateSpace != coordinateSpace) { m_coordinateSpace = coordinateSpace; + CaretAssertMessage((m_coordinateSpace != AnnotationCoordinateSpaceEnum::VIEWPORT), + "Annotation coordinate space should never change to VIEWPORT space."); setModified(); } } @@ -679,6 +711,41 @@ Annotation::setWindowIndex(const int32_t windowIndex) } } +/** + * Get the viewport used by an annotation in viewport coordinate space. + * + * @param viewportOut + * Ouput with the viewport. + */ +void +Annotation::getViewportCoordinateSpaceViewport(int viewportOut[4]) const +{ + viewportOut[0] = m_viewportCoordinateSpaceViewport[0]; + viewportOut[1] = m_viewportCoordinateSpaceViewport[1]; + viewportOut[2] = m_viewportCoordinateSpaceViewport[2]; + viewportOut[3] = m_viewportCoordinateSpaceViewport[3]; +} + +/** + * Set the viewport used by an annotation in viewport coordinate space. + * + * @param viewport + * Input with the viewport. + */ +void +Annotation::setViewportCoordinateSpaceViewport(const int viewport[4]) +{ + if ((m_viewportCoordinateSpaceViewport[0] != viewport[0]) + || (m_viewportCoordinateSpaceViewport[1] != viewport[1]) + || (m_viewportCoordinateSpaceViewport[2] != viewport[2]) + || (m_viewportCoordinateSpaceViewport[3] != viewport[3])) { + m_viewportCoordinateSpaceViewport[0] = viewport[0]; + m_viewportCoordinateSpaceViewport[1] = viewport[1]; + m_viewportCoordinateSpaceViewport[2] = viewport[2]; + m_viewportCoordinateSpaceViewport[3] = viewport[3]; + } +} + /** * Get a description of this object's content. * @return String describing this object's content. @@ -700,54 +767,82 @@ Annotation::toString() const } /** - * @return The line width. + * Convert the annotation's obsolete line width that was in pixels to a percentage of viewport height. + * Prior to late July, 2017, a line was specified in pixels. + * + * First, the annotation's percentage width is examined. If it is valid (greater than zero), then + * no conversion is needed. Otherwise, use the viewport height to convert the pixel width to a + * percentage and set the annotation's percentage line width. + * + * Note that even though this method is 'const' it may cause the modification status to change + * to modified. + * + * @param viewportHeight + * The height of the viewport in pixels. + */ +void +Annotation::convertObsoleteLineWidthPixelsToPercentageWidth(const float viewportHeight) const +{ + if (m_lineWidthPercentage > 0.0f) { + return; + } + + float percentWidth = 1.0f; + if (viewportHeight > 0.0f) { + percentWidth = (m_lineWidthPixels / viewportHeight) * 100.0f; + } + + const_cast(this)->setLineWidthPercentage(percentWidth); +} + + +/** + * @return The line width in pixels. */ float -Annotation::getLineWidth() const +Annotation::getLineWidthPixelsObsolete() const { - return m_lineWidth; + return m_lineWidthPixels; } /** - * Set the line width. + * Set the line width in pixels. * * @param lineWidth * New value for line width. */ void -Annotation::setLineWidth(const float lineWidth) +Annotation::setLineWidthPixelsObsolete(const float lineWidth) { - if (lineWidth != m_lineWidth) { - m_lineWidth = lineWidth; + if (lineWidth != m_lineWidthPixels) { + m_lineWidthPixels = lineWidth; setModified(); } } /** - * @return Is line width supported? - * Most annotations support a line width. - * Annotations that do not support a line width - * must override this method and return a value of false. + * @return The line width percentage. */ -bool -Annotation::isLineWidthSupported() const +float +Annotation::getLineWidthPercentage() const { - return true; + return m_lineWidthPercentage; } /** - * @return Is background color supported? - * Most annotations support a background color. - * Annotations that do not support a background color - * must override this method and return a value of false. + * Set the line width percentage. + * + * @param lineWidthPercentage + * New value for line width percentage. */ -bool -Annotation::isBackgroundColorSupported() const +void +Annotation::setLineWidthPercentage(const float lineWidthPercentage) { - return true; + if (lineWidthPercentage != m_lineWidthPercentage) { + m_lineWidthPercentage = lineWidthPercentage; + } } - /** * @return The line color. */ @@ -808,7 +903,7 @@ Annotation::getLineColorRGBA(float rgbaOut[4]) const case CaretColorEnum::TEAL: case CaretColorEnum::WHITE: case CaretColorEnum::YELLOW: - CaretColorEnum::toRGBFloat(m_colorLine, + CaretColorEnum::toRGBAFloat(m_colorLine, rgbaOut); rgbaOut[3] = 1.0; break; @@ -894,7 +989,7 @@ Annotation::getBackgroundColorRGBA(float rgbaOut[4]) const case CaretColorEnum::TEAL: case CaretColorEnum::WHITE: case CaretColorEnum::YELLOW: - CaretColorEnum::toRGBFloat(m_colorBackground, + CaretColorEnum::toRGBAFloat(m_colorBackground, rgbaOut); rgbaOut[3] = 1.0; break; @@ -1052,6 +1147,242 @@ Annotation::setCustomBackgroundColor(const uint8_t rgba[4]) } } +/** + * Initialize properties. + */ +void +Annotation::initializeProperties() +{ + /* + * Initialize all properties on/supported + */ + CaretAssert(m_properties.size() >= static_cast::type>(Property::COUNT_FOR_BITSET)); + m_properties.set(); + + bool colorBarFlag = false; + bool fillColorFlag = true; + bool lineArrowsFlag = false; + bool textFlag = false; + switch (m_type) { + case AnnotationTypeEnum::BOX: + break; + case AnnotationTypeEnum::COLOR_BAR: + colorBarFlag = true; + break; + case AnnotationTypeEnum::IMAGE: + fillColorFlag = false; + break; + case AnnotationTypeEnum::LINE: + fillColorFlag = false; + lineArrowsFlag = true; + break; + case AnnotationTypeEnum::OVAL: + break; + case AnnotationTypeEnum::TEXT: + textFlag = true; + break; + } + + setProperty(Property::FILL_COLOR, fillColorFlag); + setProperty(Property::LINE_ARROWS, lineArrowsFlag); + setProperty(Property::TEXT_ALIGNMENT, textFlag); + setProperty(Property::TEXT_EDIT, textFlag); + setProperty(Property::TEXT_COLOR, textFlag); + setProperty(Property::TEXT_FONT_NAME, colorBarFlag | textFlag); + setProperty(Property::TEXT_FONT_SIZE, colorBarFlag | textFlag); + setProperty(Property::TEXT_FONT_STYLE, textFlag); + setProperty(Property::TEXT_ORIENTATION, textFlag); + + resetProperty(Property::SCENE_CONTAINS_ATTRIBUTES); + + /* + * These are not valid properties + */ + resetProperty(Property::INVALID); + resetProperty(Property::COUNT_FOR_BITSET); + + if (colorBarFlag) { + resetProperty(Property::ARRANGE); + resetProperty(Property::COPY_CUT_PASTE); + resetProperty(Property::DELETION); + resetProperty(Property::DISPLAY_GROUP); + resetProperty(Property::GROUP); + resetProperty(Property::LINE_COLOR); + resetProperty(Property::LINE_THICKNESS); + resetProperty(Property::TEXT_COLOR); + resetProperty(Property::TEXT_EDIT); + + setProperty(Property::SCENE_CONTAINS_ATTRIBUTES); + } +} + +/** + * Set the properties for specialized usage of annotations. This is typically used + * for annotations that are not stored in an annotation file but instead are a + * property of some entity (such as chart labels). + * + * @param specializedUsage + * The specialized usage. + */ +void +Annotation::setPropertiesForSpecializedUsage(const PropertiesSpecializedUsage specializedUsage) +{ + initializeProperties(); + + bool chartLabelTitleFlag = false; + bool viewportFlag = false; + switch (specializedUsage) { + case PropertiesSpecializedUsage::CHART_LABEL: + chartLabelTitleFlag = true; + break; + case PropertiesSpecializedUsage::CHART_TITLE: + chartLabelTitleFlag = true; + break; + case PropertiesSpecializedUsage::VIEWPORT_ANNOTATION: + viewportFlag = true; + break; + } + + if (chartLabelTitleFlag) { + resetProperty(Property::ARRANGE); + resetProperty(Property::COORDINATE); + resetProperty(Property::COPY_CUT_PASTE); + resetProperty(Property::DELETION); + resetProperty(Property::DISPLAY_GROUP); + resetProperty(Property::GROUP); + resetProperty(Property::LINE_COLOR); + resetProperty(Property::LINE_THICKNESS); + resetProperty(Property::ROTATION); + resetProperty(Property::TEXT_ALIGNMENT); + resetProperty(Property::TEXT_COLOR); + resetProperty(Property::TEXT_ORIENTATION); + + setProperty(Property::SCENE_CONTAINS_ATTRIBUTES); + } + + if (viewportFlag) { + resetProperty(Property::ARRANGE); + resetProperty(Property::COORDINATE); + resetProperty(Property::COPY_CUT_PASTE); + resetProperty(Property::DELETION); + resetProperty(Property::DISPLAY_GROUP); + resetProperty(Property::GROUP); + resetProperty(Property::ROTATION); + + setProperty(Property::SCENE_CONTAINS_ATTRIBUTES); + } +} + +/** + * Test a property. + * + * @param property + * Property for testing. + * @return + * True if property is on, else false. + */ +bool +Annotation::testProperty(const Property property) const +{ + /* + * Text connect to brainordiante property is valid only + * for a a text annotation in surface space + + */ + if (property == Property::TEXT_CONNECT_TO_BRAINORDINATE) { + if (m_type == AnnotationTypeEnum::TEXT) { + if (m_coordinateSpace == AnnotationCoordinateSpaceEnum::SURFACE) { + return true; + } + } + return false; + } + + const int32_t propertyIndex = static_cast::type>(property); + return m_properties.test(propertyIndex); + +} + +/** + * Test for any of the properties are set. + * + * @param propertyOne + * First property for testing. + * @param propertyTwo + * Two property for testing. + * @param propertyThree + * Three property for testing. + * @param propertyFour + * Four property for testing. + * @param propertyFive + * Five property for testing. + * @return + * True if property is on, else false. + */ +bool +Annotation::testPropertiesAny(const Property propertyOne, + const Property propertyTwo, + const Property propertyThree, + const Property propertyFour, + const Property propertyFive) const +{ + if (testProperty(propertyOne)) { + return true; + } + if (testProperty(propertyTwo)) { + return true; + } + if (propertyThree != Property::INVALID) { + if (testProperty(propertyThree)) { + return true; + } + } + if (propertyFour != Property::INVALID) { + if (testProperty(propertyFour)) { + return true; + } + } + if (propertyFive != Property::INVALID) { + if (testProperty(propertyFive)) { + return true; + } + } + + return false; +} + + +/** + * Set a property. + * + * @param property + * Property for testing. + * @param value + * New on/off status for property (optional argument defaults to true). + */ +void +Annotation::setProperty(const Property property, + const bool value) +{ + const int32_t propertyIndex = static_cast::type>(property); + m_properties.set(propertyIndex, + value); +} + +/** + * Reset (turn off) a property. + * + * @param property + * Property for testing. + */ +void +Annotation::resetProperty(const Property property) +{ + const int32_t propertyIndex = static_cast::type>(property); + m_properties.reset(propertyIndex); +} + + /** * @return The key to the annotation group that owns this annotation. */ @@ -1633,12 +1964,19 @@ TriStateSelectionStatusEnum::Enum Annotation::getItemDisplaySelected(const DisplayGroupEnum::Enum displayGroup, const int32_t tabIndex) const { - if (m_coordinateSpace == AnnotationCoordinateSpaceEnum::WINDOW) { - return m_displayGroupAndTabItemHelper->getSelectedInWindow(m_windowIndex); + if (testProperty(Annotation::Property::DISPLAY_GROUP)) { + if (m_coordinateSpace == AnnotationCoordinateSpaceEnum::WINDOW) { + return m_displayGroupAndTabItemHelper->getSelectedInWindow(m_windowIndex); + } + + return m_displayGroupAndTabItemHelper->getSelected(displayGroup, + tabIndex); } - return m_displayGroupAndTabItemHelper->getSelected(displayGroup, - tabIndex); + /* + * Annotations that do not support a "display group" are always displayed + */ + return TriStateSelectionStatusEnum::SELECTED; } /** @@ -1767,13 +2105,14 @@ Annotation::setUserDefaultCustomBackgroundColor(const float rgba[4]) } /** - * Set the default value for line width + * Set the default value for line width percentage * - * @param lineWidth + * @param lineWidthPercentage * Default for newly created annotations. */ void -Annotation::setUserDefaultLineWidth(const float lineWidth) +Annotation::setUserDefaultLineWidthPercentage(const float lineWidthPercentage) { - s_userDefaultLineWidth = lineWidth; + s_userDefaultLineWidthPercentage = lineWidthPercentage; } + diff --git a/src/Annotations/Annotation.h b/src/Annotations/Annotation.h index d867e0ca16bdea50db62b9ff943f9ce94cbb733c..82f79383f932a386b4994eb6e5091626fb54563a 100644 --- a/src/Annotations/Annotation.h +++ b/src/Annotations/Annotation.h @@ -42,6 +42,69 @@ namespace caret { class Annotation : public CaretObjectTracksModification, public DisplayGroupAndTabItemInterface, public SceneableInterface { public: + /** + * Properties supported by an annotation + */ + enum class Property : int32_t { + /** Invalid (for internal use only) */ + INVALID, + /** Annotation can be arranged by user */ + ARRANGE, + /** Annotation can be moved by user */ + COORDINATE, + /** Annotation allows cut, copy, and paste by user*/ + COPY_CUT_PASTE, + /** Annotation may be deleted by user (note 'DELETE' will not compile on windows) */ + DELETION, + /** Annotation display controlled by Display Group and Tab */ + DISPLAY_GROUP, + /** Annotation has fill (background) color */ + FILL_COLOR, + /** Annotation can be grouped by user */ + GROUP, + /** Annotation has arrows at its line endpoints */ + LINE_ARROWS, + /** Annotation has a line color */ + LINE_COLOR, + /** Annotation has line thickness */ + LINE_THICKNESS, + /** Annotation can be rotated by user */ + ROTATION, + /** + * Annotation's attributes are saved to and restored from scene. + * Typically used for special annotation that are saved to a scene + * and NOT saved in a file. + */ + SCENE_CONTAINS_ATTRIBUTES, + /** Annotation is selectable */ + SELECT, + /** Annotation has alignment of text */ + TEXT_ALIGNMENT, + /** Annotation allows connection to brainordinate */ + TEXT_CONNECT_TO_BRAINORDINATE, + /** Annotation allows editing of its text by user */ + TEXT_EDIT, + /** Annotation has text color */ + TEXT_COLOR, + /** Annotation has font name */ + TEXT_FONT_NAME, + /** Annotation has font size*/ + TEXT_FONT_SIZE, + /** Annotation has font style */ + TEXT_FONT_STYLE, + /** Annotation has orientation of text */ + TEXT_ORIENTATION, + /** Count of properties MUST BE LAST */ + COUNT_FOR_BITSET + }; + + /** Identifies properities for specialized uses of annotations */ + enum class PropertiesSpecializedUsage { + CHART_LABEL, + CHART_TITLE, + VIEWPORT_ANNOTATION + }; + Annotation(const AnnotationTypeEnum::Enum type, const AnnotationAttributesDefaultTypeEnum::Enum attributeDefaultType); @@ -56,6 +119,23 @@ namespace caret { Annotation* clone() const; + bool testProperty(const Property property) const; + + bool testPropertiesAny(const Property propertyOne, + const Property propertyTwo, + const Property propertyThree = Property::INVALID, + const Property propertyFour = Property::INVALID, + const Property propertyFive = Property::INVALID) const; + + void setProperty(const Property property, + const bool value = true); + + void resetProperty(const Property property); + + void setPropertiesForSpecializedUsage(const PropertiesSpecializedUsage specializedUsage); + + virtual void setModified(); + AnnotationGroupKey getAnnotationGroupKey() const; int32_t getUniqueKey() const; @@ -83,6 +163,10 @@ namespace caret { void setWindowIndex(const int32_t windowIndex); + void getViewportCoordinateSpaceViewport(int viewportOut[4]) const; + + void setViewportCoordinateSpaceViewport(const int viewport[4]); + CaretColorEnum::Enum getLineColor() const; void setLineColor(const CaretColorEnum::Enum color); @@ -115,13 +199,11 @@ namespace caret { void setCustomBackgroundColor(const uint8_t rgba[4]); - float getLineWidth() const; + void convertObsoleteLineWidthPixelsToPercentageWidth(const float viewportHeight) const; - void setLineWidth(const float lineWidth); + float getLineWidthPercentage() const; - virtual bool isLineWidthSupported() const; - - virtual bool isBackgroundColorSupported() const; + void setLineWidthPercentage(const float lineWidthPercentage); bool isSelectedForEditing(const int32_t windowIndex) const; @@ -137,9 +219,7 @@ namespace caret { static void setUserDefaultCustomBackgroundColor(const float rgba[4]); - static void setUserDefaultLineWidth(const float lineWidth); - - virtual bool isDeletable() const; + static void setUserDefaultLineWidthPercentage(const float lineWidthPercentage); virtual bool isFixedAspectRatio() const; @@ -244,6 +324,8 @@ namespace caret { void initializeAnnotationMembers(); + void initializeProperties(); + // private - AnnotationManager handles selection and allowing // public access to this method could cause improper selection status void setSelectedForEditing(const int32_t windowIndex, @@ -263,6 +345,10 @@ namespace caret { void clearDrawnInWindowStatusForAllWindows(); private: + float getLineWidthPixelsObsolete() const; + + void setLineWidthPixelsObsolete(const float lineWidthPixels); + void setAnnotationGroupKey(const AnnotationGroupKey& annotationGroupKey); void invalidateAnnotationGroupKey(); @@ -279,6 +365,8 @@ namespace caret { int32_t m_windowIndex; + int32_t m_viewportCoordinateSpaceViewport[4]; + CaretColorEnum::Enum m_colorLine; CaretColorEnum::Enum m_colorBackground; @@ -287,8 +375,10 @@ namespace caret { float m_customColorBackground[4]; - float m_lineWidth; + float m_lineWidthPixels; + float m_lineWidthPercentage = -1.0f; + AString m_name; int32_t m_uniqueKey; @@ -301,11 +391,15 @@ namespace caret { * Selection (NOT DISPLAY) status in each window. * * Number of elements must be same as Constants::MAXIMUM_NUMBER_OF_BROWSER_WINDOWS - * An assertion will fail in the if number of elements differs. + * An assertion will fail in cxx file the if number of elements differs. */ mutable std::bitset<10> m_selectedForEditingInWindowFlag; - // defaults + /** + * Contains annotation properties. In the cxx file, an assertion will fail + * if the number of elements is insufficient. + */ + std::bitset<32> m_properties; static CaretColorEnum::Enum s_userDefaultColorLine; @@ -319,12 +413,15 @@ namespace caret { static float s_userDefaultCustomColorBackground[4]; - static float s_userDefaultLineWidth; + static float s_userDefaultLineWidthPixelsObsolete; + + static float s_userDefaultLineWidthPercentage; // ADD_NEW_MEMBERS_HERE friend class AnnotationFile; friend class AnnotationFileXmlReader; + friend class AnnotationFileXmlWriter; friend class AnnotationGroup; friend class AnnotationManager; }; @@ -342,7 +439,9 @@ namespace caret { float Annotation::s_userDefaultCustomColorBackground[4] = { 0.0, 0.0, 0.0, 1.0 }; - float Annotation::s_userDefaultLineWidth = 3.0; + float Annotation::s_userDefaultLineWidthPixelsObsolete = 3.0f; + + float Annotation::s_userDefaultLineWidthPercentage = 1.0f; #endif // __ANNOTATION_DECLARE__ } // namespace diff --git a/src/Annotations/AnnotationBox.cxx b/src/Annotations/AnnotationBox.cxx index 581e72181e4871e93141d585a58a5c69fec10862..aabc052a01fcf392ab2a60d9db4cfae61c37a25d 100644 --- a/src/Annotations/AnnotationBox.cxx +++ b/src/Annotations/AnnotationBox.cxx @@ -93,6 +93,10 @@ void AnnotationBox::initializeMembersAnnotationBox() { m_sceneAssistant.grabNew(new SceneClassAssistant()); + + if (testProperty(Property::SCENE_CONTAINS_ATTRIBUTES)) { + + } } /** diff --git a/src/Annotations/AnnotationColorBar.cxx b/src/Annotations/AnnotationColorBar.cxx index 2a9244f7fff2cfbf038e40b7f22442b820c9a89b..6f6ea1d1d5919883c3224be5ec767fa545ca8214 100644 --- a/src/Annotations/AnnotationColorBar.cxx +++ b/src/Annotations/AnnotationColorBar.cxx @@ -54,21 +54,23 @@ AnnotationFontAttributesInterface() reset(); m_sceneAssistant.grabNew(new SceneClassAssistant()); - m_sceneAssistant->add("m_fontName", - &m_fontName); - m_sceneAssistant->add("m_fontPercentViewportHeight", - &m_fontPercentViewportHeight); - m_sceneAssistant->add("m_positionMode", - &m_positionMode); - m_sceneAssistant->add("m_displayedFlag", - &m_displayedFlag); - - m_sceneAssistant->add("m_showTickMarksSelected", - &m_showTickMarksSelected); - m_sceneAssistant->add("m_colorText", - &m_colorText); - m_sceneAssistant->addArray("m_customColorText", - m_customColorText, 4, 1.0); + if (testProperty(Property::SCENE_CONTAINS_ATTRIBUTES)) { + m_sceneAssistant->add("m_fontName", + &m_fontName); + m_sceneAssistant->add("m_fontPercentViewportHeight", + &m_fontPercentViewportHeight); + m_sceneAssistant->add("m_positionMode", + &m_positionMode); + m_sceneAssistant->add("m_displayedFlag", + &m_displayedFlag); + + m_sceneAssistant->add("m_showTickMarksSelected", + &m_showTickMarksSelected); + m_sceneAssistant->add("m_colorText", + &m_colorText); + m_sceneAssistant->addArray("m_customColorText", + m_customColorText, 4, 1.0); + } } /** @@ -136,6 +138,7 @@ AnnotationColorBar::copyHelperAnnotationColorBar(const AnnotationColorBar& obj) m_customColorText[1] = obj.m_customColorText[1]; m_customColorText[2] = obj.m_customColorText[2]; m_customColorText[3] = obj.m_customColorText[3]; + m_fontTooSmallWhenLastDrawnFlag = obj.m_fontTooSmallWhenLastDrawnFlag; } /** @@ -163,6 +166,8 @@ AnnotationColorBar::reset() clearSections(); clearNumericText(); + + m_fontTooSmallWhenLastDrawnFlag = false; } /** @@ -226,15 +231,6 @@ AnnotationColorBar::setFontPercentViewportSize(const float fontPercentViewportHe } } -/** - * Are font styles (Bold, Italic, Underline) supported? - */ -bool -AnnotationColorBar::isStylesSupported() const -{ - return false; -} - /** * @return The foreground color. */ @@ -295,7 +291,7 @@ AnnotationColorBar::getTextColorRGBA(float rgbaOut[4]) const case CaretColorEnum::TEAL: case CaretColorEnum::WHITE: case CaretColorEnum::YELLOW: - CaretColorEnum::toRGBFloat(m_colorText, + CaretColorEnum::toRGBAFloat(m_colorText, rgbaOut); rgbaOut[3] = 1.0; break; @@ -470,19 +466,6 @@ AnnotationColorBar::setOutlineStyleEnabled(const bool /*enabled*/) { } -/** - * @return Is foreground line width supported? - * Most annotations support a foreground line width. - * Annotations that do not support a foreground line width - * must override this method and return a value of false. - */ -bool -AnnotationColorBar::isLineWidthSupported() const -{ - return false; -} - - /** * @return The position mode for the colorbar annotation. */ @@ -538,17 +521,6 @@ AnnotationColorBar::setDisplayed(const bool displayed) } } -/** - * @return Is this annotation deletable? This method may be overridden - * by annotations (such as colorbars) that cannot be deleted. - */ -bool -AnnotationColorBar::isDeletable() const -{ - return false; -} - - /** * Save subclass data to the scene. * @@ -790,4 +762,21 @@ AnnotationColorBar::getScalarMinimumAndMaximumValues(float& minimumScalarOut, } } +/** + * @return Is the font too small when it is last drawn + * that may cause an OpenGL error and, as a result, + * the text is not seen by the user. + */ +bool +AnnotationColorBar::isFontTooSmallWhenLastDrawn() const +{ + return m_fontTooSmallWhenLastDrawnFlag; +} + +void +AnnotationColorBar::setFontTooSmallWhenLastDrawn(const bool tooSmallFontFlag) const +{ + m_fontTooSmallWhenLastDrawnFlag = tooSmallFontFlag; +} + diff --git a/src/Annotations/AnnotationColorBar.h b/src/Annotations/AnnotationColorBar.h index addde99e2b5de08b1dff06a0bb4007b722e59f40..7edec95b3e881c0bc829875c5ef0c96c9e99726f 100644 --- a/src/Annotations/AnnotationColorBar.h +++ b/src/Annotations/AnnotationColorBar.h @@ -74,8 +74,6 @@ namespace caret { virtual void setCustomTextColor(const uint8_t rgba[4]); - virtual bool isStylesSupported() const; - virtual bool isBoldStyleEnabled() const; virtual void setBoldStyleEnabled(const bool enabled); @@ -92,8 +90,6 @@ namespace caret { virtual void setOutlineStyleEnabled(const bool enabled); - virtual bool isLineWidthSupported() const; - bool isDisplayed() const; void setDisplayed(const bool displayed); @@ -127,7 +123,9 @@ namespace caret { void setShowTickMarksSelected(const bool selected); - virtual bool isDeletable() const; + bool isFontTooSmallWhenLastDrawn() const override; + + void setFontTooSmallWhenLastDrawn(const bool tooSmallFontFlag) const override; // ADD_NEW_METHODS_HERE @@ -163,6 +161,8 @@ namespace caret { bool m_showTickMarksSelected; + mutable bool m_fontTooSmallWhenLastDrawnFlag = false; + // ADD_NEW_MEMBERS_HERE }; diff --git a/src/Annotations/AnnotationCoordinate.cxx b/src/Annotations/AnnotationCoordinate.cxx index 036a29c04c48b2e25a0f198fcf7cb9b86c3afe31..2b5bd4b057de0566f6200239c50b1ebd385d0135 100644 --- a/src/Annotations/AnnotationCoordinate.cxx +++ b/src/Annotations/AnnotationCoordinate.cxx @@ -220,6 +220,27 @@ AnnotationCoordinate::setXYZ(const float x, } } +/** + * Add to the annotation's XYZ coordinate. + * + * @param dx + * Change in X-coordinate for the annotation. + * @param dy + * Change in Y-coordinate for the annotation. + * @param dz + * Change in Z-coordinate for the annotation. + */ +void +AnnotationCoordinate::addToXYZ(const float dx, + const float dy, + const float dz) +{ + setXYZ(m_xyz[0] + dx, + m_xyz[1] + dy, + m_xyz[2] + dz); +} + + /** * Set the window or tab XYZ from viewport X, Y. * diff --git a/src/Annotations/AnnotationCoordinate.h b/src/Annotations/AnnotationCoordinate.h index 7e8279f3eca0215bde5cd873fe9050bf78bb9f23..1234146f752ab757ca5f66a9d3bff37f5388dfeb 100644 --- a/src/Annotations/AnnotationCoordinate.h +++ b/src/Annotations/AnnotationCoordinate.h @@ -53,6 +53,10 @@ namespace caret { const float y, const float z); + void addToXYZ(const float dx, + const float dy, + const float dz); + void setXYZFromViewportXYZ(const float viewportWidth, const float viewportHeight, const float viewportX, diff --git a/src/Annotations/AnnotationCoordinateSpaceEnum.cxx b/src/Annotations/AnnotationCoordinateSpaceEnum.cxx index 46a5d58f888856abb2cbb2e55476fb3303643f7f..0c767caaea15bbc276c3d3e399659fe7d80160b3 100644 --- a/src/Annotations/AnnotationCoordinateSpaceEnum.cxx +++ b/src/Annotations/AnnotationCoordinateSpaceEnum.cxx @@ -110,10 +110,10 @@ AnnotationCoordinateSpaceEnum::initialize() } initializedFlag = true; - enumData.push_back(AnnotationCoordinateSpaceEnum(PIXELS, - "PIXELS", - "Pixels", - "P")); + enumData.push_back(AnnotationCoordinateSpaceEnum(CHART, + "CHART", + "Chart", + "Ch")); enumData.push_back(AnnotationCoordinateSpaceEnum(STEREOTAXIC, "STEREOTAXIC", @@ -130,6 +130,11 @@ AnnotationCoordinateSpaceEnum::initialize() "Tab", "T")); + enumData.push_back(AnnotationCoordinateSpaceEnum(VIEWPORT, + "VIEWPORT", + "Viewport", + "V")); + enumData.push_back(AnnotationCoordinateSpaceEnum(WINDOW, "WINDOW", "Window", @@ -299,8 +304,8 @@ AnnotationCoordinateSpaceEnum::toToolTip(Enum enumValue) AString text; switch (enumValue) { - case PIXELS: - text = "New annotation is drawn at an XY pixel coordinate"; + case CHART: + text = "New annotation is drawn at a chart data XYZ coordinate"; break; case STEREOTAXIC: text = "New annotation is drawn at a surface/volume XYZ coordinate"; @@ -311,6 +316,9 @@ AnnotationCoordinateSpaceEnum::toToolTip(Enum enumValue) case TAB: text = "New annotation is drawn at an XY coordinate in the tab"; break; + case VIEWPORT: + text = "New annotation is drawn at an XY coordinate in the viewport"; + break; case WINDOW: text = "New annotation is drawn at an XY coordinate in the window"; break; diff --git a/src/Annotations/AnnotationCoordinateSpaceEnum.h b/src/Annotations/AnnotationCoordinateSpaceEnum.h index 5f7e4b8d10ce7fda8910092e77b72c42b3cce9c9..3dc4a839fe5a09a7a874f1418e249d4131ea9890 100644 --- a/src/Annotations/AnnotationCoordinateSpaceEnum.h +++ b/src/Annotations/AnnotationCoordinateSpaceEnum.h @@ -35,14 +35,16 @@ public: * Enumerated values. */ enum Enum { - /** Annotation in pixels (origin is bottom, left corner). */ - PIXELS, + /** Chart space */ + CHART, /** Annotation in stereotaxic (3D) space */ STEREOTAXIC, /** Annotation on surface node */ SURFACE, /** Annotation in tab space */ TAB, + /** Annotation in viewport space */ + VIEWPORT, /** Annotation in window space */ WINDOW }; diff --git a/src/Annotations/AnnotationEditingSelectionInformation.cxx b/src/Annotations/AnnotationEditingSelectionInformation.cxx index b35a0d1b03bbbaafbb27ed36b8f7f51ad77689d2..ec71cd139a39fad98d362113246daa649896d641 100644 --- a/src/Annotations/AnnotationEditingSelectionInformation.cxx +++ b/src/Annotations/AnnotationEditingSelectionInformation.cxx @@ -147,11 +147,18 @@ AnnotationEditingSelectionInformation::update(const std::vector& se m_annotations = selectedAnnotations; + bool allAnnotationsGroupableFlag = false; std::set groupKeysSet; - for (std::vector::iterator annIter = m_annotations.begin(); - annIter != m_annotations.end(); - annIter++) { - groupKeysSet.insert((*annIter)->getAnnotationGroupKey()); + if ( ! m_annotations.empty()) { + allAnnotationsGroupableFlag = true; + for (auto ann : m_annotations) { + if (ann->testProperty(Annotation::Property::GROUP)) { + groupKeysSet.insert(ann->getAnnotationGroupKey()); + } + else { + allAnnotationsGroupableFlag = false; + } + } } // std::set groupSet; @@ -163,38 +170,40 @@ AnnotationEditingSelectionInformation::update(const std::vector& se // groupSet.insert(annGroup); // } - m_annotationGroupKeys.insert(m_annotationGroupKeys.end(), - groupKeysSet.begin(), - groupKeysSet.end()); + if (allAnnotationsGroupableFlag) { + m_annotationGroupKeys.insert(m_annotationGroupKeys.end(), + groupKeysSet.begin(), + groupKeysSet.end()); + } const int32_t numGroups = static_cast(m_annotationGroupKeys.size()); /* * Are TWO or more annotations selected */ - if (m_annotations.size() > 1) { - if (numGroups == 1) { - CaretAssertVectorIndex(m_annotationGroupKeys, 0); - const AnnotationGroupKey& groupKey = m_annotationGroupKeys[0]; - - switch (groupKey.getGroupType()) { - case AnnotationGroupTypeEnum::INVALID: - break; - case AnnotationGroupTypeEnum::SPACE: - /* - * All annotations in a space group, allow creating a user group - */ - m_groupingValid = true; - break; - case AnnotationGroupTypeEnum::USER: - /* - * All annotations in a user group, allow ungrouping to a space group - */ - m_ungroupValid = true; - break; + if (m_annotations.size() > 1) { + if (numGroups == 1) { + CaretAssertVectorIndex(m_annotationGroupKeys, 0); + const AnnotationGroupKey& groupKey = m_annotationGroupKeys[0]; + + switch (groupKey.getGroupType()) { + case AnnotationGroupTypeEnum::INVALID: + break; + case AnnotationGroupTypeEnum::SPACE: + /* + * All annotations in a space group, allow creating a user group + */ + m_groupingValid = true; + break; + case AnnotationGroupTypeEnum::USER: + /* + * All annotations in a user group, allow ungrouping to a space group + */ + m_ungroupValid = true; + break; + } } } - } /* * Is ANY annotation selected @@ -276,6 +285,14 @@ AnnotationEditingSelectionInformation::getAnnotationsSelectedForEditing(std::vec annotationsOut = m_annotations; } +/** + * Is the given grouping mode valid? + * + * @param groupingMode + * The grouping mode. + * @return + * True if the grouping mode is valid, else false. + */ bool AnnotationEditingSelectionInformation::isGroupingModeValid(const AnnotationGroupingModeEnum::Enum groupingMode) const { diff --git a/src/Annotations/AnnotationFontAttributesInterface.h b/src/Annotations/AnnotationFontAttributesInterface.h index 3d5f9670772f78d24d47743368db3cc43872128a..9e1332c8ef23f7dfdde85d8a521e76f16aca8a94 100644 --- a/src/Annotations/AnnotationFontAttributesInterface.h +++ b/src/Annotations/AnnotationFontAttributesInterface.h @@ -35,7 +35,7 @@ namespace caret { AnnotationFontAttributesInterface() { } virtual ~AnnotationFontAttributesInterface() { } - + virtual AnnotationTextFontNameEnum::Enum getFont() const = 0; virtual void setFont(const AnnotationTextFontNameEnum::Enum font) = 0; @@ -60,8 +60,6 @@ namespace caret { virtual void setCustomTextColor(const uint8_t rgba[4]) = 0; - virtual bool isStylesSupported() const = 0; - virtual bool isBoldStyleEnabled() const = 0; virtual void setBoldStyleEnabled(const bool enabled) = 0; @@ -74,6 +72,10 @@ namespace caret { virtual void setUnderlineStyleEnabled(const bool enabled) = 0; + virtual bool isFontTooSmallWhenLastDrawn() const = 0; + + virtual void setFontTooSmallWhenLastDrawn(const bool tooSmallFontFlag) const = 0; + // ADD_NEW_METHODS_HERE private: diff --git a/src/Annotations/AnnotationGroup.cxx b/src/Annotations/AnnotationGroup.cxx index 75ddadb0a6be94d3e5fabdca5a576ed1ed04fbeb..4f71c5bbf76c0bcdb0f308fa0afd29b0b8485309 100644 --- a/src/Annotations/AnnotationGroup.cxx +++ b/src/Annotations/AnnotationGroup.cxx @@ -70,7 +70,7 @@ SceneableInterface() CaretAssert(annotationFile); CaretAssert(groupType != AnnotationGroupTypeEnum::INVALID); CaretAssert(uniqueKey > 0); - CaretAssert(coordinateSpace != AnnotationCoordinateSpaceEnum::PIXELS); + CaretAssert(coordinateSpace != AnnotationCoordinateSpaceEnum::VIEWPORT); initializeInstance(); @@ -93,8 +93,7 @@ SceneableInterface() m_tabOrWindowIndex = tabOrWindowIndex; switch (m_coordinateSpace) { - case AnnotationCoordinateSpaceEnum::PIXELS: - CaretAssert(0); + case AnnotationCoordinateSpaceEnum::CHART: break; case AnnotationCoordinateSpaceEnum::STEREOTAXIC: break; @@ -104,6 +103,9 @@ SceneableInterface() CaretAssert((tabOrWindowIndex >= 0) && (tabOrWindowIndex < BrainConstants::MAXIMUM_NUMBER_OF_BROWSER_TABS)); break; + case AnnotationCoordinateSpaceEnum::VIEWPORT: + CaretAssert(0); + break; case AnnotationCoordinateSpaceEnum::WINDOW: CaretAssert((tabOrWindowIndex >= 0) && (tabOrWindowIndex < BrainConstants::MAXIMUM_NUMBER_OF_BROWSER_WINDOWS)); @@ -179,7 +181,7 @@ void AnnotationGroup::initializeInstance() { m_groupKey.reset(); - m_coordinateSpace = AnnotationCoordinateSpaceEnum::PIXELS; + m_coordinateSpace = AnnotationCoordinateSpaceEnum::VIEWPORT; m_name = ""; m_tabOrWindowIndex = -1; @@ -259,8 +261,7 @@ AnnotationGroup::getName() const AString spaceName = AnnotationCoordinateSpaceEnum::toGuiName(m_coordinateSpace); switch (m_coordinateSpace) { - case AnnotationCoordinateSpaceEnum::PIXELS: - CaretAssertMessage(0, "Should never be pixels"); + case AnnotationCoordinateSpaceEnum::CHART: break; case AnnotationCoordinateSpaceEnum::STEREOTAXIC: break; @@ -270,6 +271,9 @@ AnnotationGroup::getName() const spaceName.append(" " + AString::number(getTabOrWindowIndex() + 1)); break; + case AnnotationCoordinateSpaceEnum::VIEWPORT: + CaretAssertMessage(0, "Should never be viewport"); + break; case AnnotationCoordinateSpaceEnum::WINDOW: spaceName.append(" " + AString::number(getTabOrWindowIndex() + 1)); @@ -419,7 +423,7 @@ AnnotationGroup::validateAddedAnnotation(const Annotation* annotation) } switch (space) { - case AnnotationCoordinateSpaceEnum::PIXELS: + case AnnotationCoordinateSpaceEnum::CHART: break; case AnnotationCoordinateSpaceEnum::STEREOTAXIC: break; @@ -432,6 +436,8 @@ AnnotationGroup::validateAddedAnnotation(const Annotation* annotation) return false; } break; + case AnnotationCoordinateSpaceEnum::VIEWPORT: + break; case AnnotationCoordinateSpaceEnum::WINDOW: if (m_tabOrWindowIndex != annotation->getWindowIndex()) { CaretLogSevere("Attempting to add anntation with non-matching tab index: "); diff --git a/src/Annotations/AnnotationImage.cxx b/src/Annotations/AnnotationImage.cxx index 8baf0e515c3e6c11b1bf02a8e64598764d696286..3ff626f3035383001f95f0a546007bfe3e886c82 100644 --- a/src/Annotations/AnnotationImage.cxx +++ b/src/Annotations/AnnotationImage.cxx @@ -24,7 +24,8 @@ #undef __ANNOTATION_IMAGE_DECLARE__ #include "CaretAssert.h" -#include "DrawnWithOpenGLTextureInfo.h" +#include "CaretLogger.h" +#include "GraphicsPrimitiveV3fT3f.h" #include "SceneClass.h" #include "SceneClassAssistant.h" @@ -46,8 +47,7 @@ using namespace caret; */ AnnotationImage::AnnotationImage(const AnnotationAttributesDefaultTypeEnum::Enum attributeDefaultType) : AnnotationTwoDimensionalShape(AnnotationTypeEnum::IMAGE, - attributeDefaultType), -DrawnWithOpenGLTextureInterface() + attributeDefaultType) { initializeMembersAnnotationImage(); @@ -67,8 +67,7 @@ AnnotationImage::~AnnotationImage() * Object that is copied. */ AnnotationImage::AnnotationImage(const AnnotationImage& obj) -: AnnotationTwoDimensionalShape(obj), -DrawnWithOpenGLTextureInterface() +: AnnotationTwoDimensionalShape(obj) { initializeMembersAnnotationImage(); this->copyHelperAnnotationImage(obj); @@ -102,7 +101,6 @@ AnnotationImage::copyHelperAnnotationImage(const AnnotationImage& obj) m_imageBytesRGBA = obj.m_imageBytesRGBA; m_imageWidth = obj.m_imageWidth; m_imageHeight = obj.m_imageHeight; - m_drawnWithOpenGLTextureInfo = obj.m_drawnWithOpenGLTextureInfo; } /** @@ -188,33 +186,8 @@ AnnotationImage::initializeMembersAnnotationImage() m_imageHeight = 0; m_sceneAssistant = new SceneClassAssistant(); - - /* Texture info not saved to scenes */ - m_drawnWithOpenGLTextureInfo.grabNew(new DrawnWithOpenGLTextureInfo); -} - -/** - * @return Is foreground line width supported? - * Most annotations support a foreground line width. - * Annotations that do not support a foreground line width - * must override this method and return a value of false. - */ -bool -AnnotationImage::isLineWidthSupported() const -{ - return true; -} - -/** - * @return Is background color supported? - * Most annotations support a background color. - * Annotations that do not support a background color - * must override this method and return a value of false. - */ -bool -AnnotationImage::isBackgroundColorSupported() const -{ - return false; + if (testProperty(Property::SCENE_CONTAINS_ATTRIBUTES)) { + } } /** @@ -251,23 +224,60 @@ AnnotationImage::getFixedAspectRatio() const } /** - * @return The OpenGL texture information used for managing - * texture resources. + * @return The graphics primitive for drawing the image as a texture. */ -DrawnWithOpenGLTextureInfo* -AnnotationImage::getDrawWithOpenGLTextureInfo() +GraphicsPrimitiveV3fT3f* +AnnotationImage::getGraphicsPrimitive() const { - return m_drawnWithOpenGLTextureInfo; + if (m_graphicsPrimitive == NULL) { + if ( ! m_imageBytesRGBA.empty()) { + GraphicsPrimitiveV3fT3f* primitive = GraphicsPrimitive::newPrimitiveV3fT3f(GraphicsPrimitive::PrimitiveType::OPENGL_TRIANGLE_STRIP, + &m_imageBytesRGBA[0], + m_imageWidth, + m_imageHeight); + /* + * A Triangle Strip (consisting of two triangles) is used + * for drawing the image. At this time, the XYZ coordinates + * do not matter and they will be updated when the annotation + * is drawn by a call to ::setVertexBounds(). + * The order of the vertices in the triangle strip is + * Top Left, Bottom Left, Top Right, Bottom Right. If this + * order changes, ::setVertexBounds must be updated. + * + * Zeros are used for the X- and Y-coordinates. + * The third and fourth parameters are the texture + * S and T coordinates. + */ + primitive->addVertex(0, 0, 0, 1); /* Top Left */ + primitive->addVertex(0, 0, 0, 0); /* Bottom Left */ + primitive->addVertex(0, 0, 1, 1); /* Top Right */ + primitive->addVertex(0, 0, 1, 0); /* Bottom Right */ + + m_graphicsPrimitive.reset(primitive); + + +// create triangles above and add method to set the vertex coordintes (bottom left, bottom right, etc) + } + } + + return m_graphicsPrimitive.get(); } -/** - * @return The OpenGL texture information used for managing - * texture resources (const method) - */ -const DrawnWithOpenGLTextureInfo* -AnnotationImage::getDrawWithOpenGLTextureInfo() const +void +AnnotationImage::setVertexBounds(const float bottomLeft[3], + const float bottomRight[3], + const float topRight[3], + const float topLeft[3]) { - return m_drawnWithOpenGLTextureInfo; + GraphicsPrimitiveV3fT3f* primitive = getGraphicsPrimitive(); + CaretAssert(primitive); + + CaretAssert(primitive->getNumberOfVertices() == 4); + + primitive->replaceVertexFloatXYZ(0, topLeft); + primitive->replaceVertexFloatXYZ(1, bottomLeft); + primitive->replaceVertexFloatXYZ(2, topRight); + primitive->replaceVertexFloatXYZ(3, bottomRight); } /** diff --git a/src/Annotations/AnnotationImage.h b/src/Annotations/AnnotationImage.h index f5c2316d55dbabc672894560ff229bc831c74b38..172e616ef9d13d993875dd3d29e1da1264a18456 100644 --- a/src/Annotations/AnnotationImage.h +++ b/src/Annotations/AnnotationImage.h @@ -21,16 +21,15 @@ */ /*LICENSE_END*/ +#include #include "AnnotationTwoDimensionalShape.h" -#include "CaretPointer.h" -#include "DrawnWithOpenGLTextureInfo.h" -#include "DrawnWithOpenGLTextureInterface.h" - namespace caret { - class AnnotationImage : public AnnotationTwoDimensionalShape, public DrawnWithOpenGLTextureInterface { + class GraphicsPrimitiveV3fT3f; + + class AnnotationImage : public AnnotationTwoDimensionalShape { public: AnnotationImage(const AnnotationAttributesDefaultTypeEnum::Enum attributeDefaultType); @@ -41,10 +40,6 @@ namespace caret { AnnotationImage& operator=(const AnnotationImage& obj); - virtual bool isLineWidthSupported() const; - - virtual bool isBackgroundColorSupported() const; - virtual bool isFixedAspectRatio() const; virtual float getFixedAspectRatio() const; @@ -59,11 +54,12 @@ namespace caret { const uint8_t* getImageBytesRGBA() const; - virtual DrawnWithOpenGLTextureInfo* getDrawWithOpenGLTextureInfo(); - - virtual const DrawnWithOpenGLTextureInfo* getDrawWithOpenGLTextureInfo() const; - + GraphicsPrimitiveV3fT3f* getGraphicsPrimitive() const; + void setVertexBounds(const float bottomLeft[3], + const float bottomRight[3], + const float topRight[3], + const float topLeft[3]); // ADD_NEW_METHODS_HERE @@ -90,9 +86,9 @@ namespace caret { int32_t m_imageWidth; int32_t m_imageHeight; - - CaretPointer m_drawnWithOpenGLTextureInfo; - + + mutable std::unique_ptr m_graphicsPrimitive; + // ADD_NEW_MEMBERS_HERE }; diff --git a/src/Annotations/AnnotationLine.cxx b/src/Annotations/AnnotationLine.cxx index c9568f851bcb16cefc08a3fea0eeca71cf3b1e54..f4557e234fb0ff00d0dcbf53191423c274ba2a24 100644 --- a/src/Annotations/AnnotationLine.cxx +++ b/src/Annotations/AnnotationLine.cxx @@ -117,6 +117,8 @@ AnnotationLine::initializeMembersAnnotationLine() m_sceneAssistant.grabNew(new SceneClassAssistant()); + if (testProperty(Property::SCENE_CONTAINS_ATTRIBUTES)) { + } } /** @@ -161,19 +163,6 @@ AnnotationLine::setDisplayEndArrow(const bool displayArrow) m_displayEndArrow = displayArrow; } -/** - * @return Is background color supported? - * Most annotations support a background color. - * Annotations that do not support a background color - * must override this method and return a value of false. - */ -bool -AnnotationLine::isBackgroundColorSupported() const -{ - return false; -} - - /** * Save subclass data to the scene. * diff --git a/src/Annotations/AnnotationLine.h b/src/Annotations/AnnotationLine.h index d0fb0b27dcbaccd2769a27d374389925c282f97c..e17e68e3b0758b02a2e62cb44f47536a66b6ebfc 100644 --- a/src/Annotations/AnnotationLine.h +++ b/src/Annotations/AnnotationLine.h @@ -46,9 +46,6 @@ namespace caret { void setDisplayEndArrow(const bool displayArrow); - virtual bool isBackgroundColorSupported() const; - - static void setUserDefaultDisplayStartArrow(const bool displayArrow); static void setUserDefaultDisplayEndArrow(const bool displayArrow); diff --git a/src/Annotations/AnnotationOneDimensionalShape.cxx b/src/Annotations/AnnotationOneDimensionalShape.cxx index a71f1660b4eadfdf6c996585f5eb363adfb166ec..30416ca2dfe551ac4fcd134a7cc69e1f48a64fb5 100644 --- a/src/Annotations/AnnotationOneDimensionalShape.cxx +++ b/src/Annotations/AnnotationOneDimensionalShape.cxx @@ -116,6 +116,14 @@ AnnotationOneDimensionalShape::initializeMembersAnnotationOneDimensionalShape() m_endCoordinate.grabNew(new AnnotationCoordinate()); m_sceneAssistant.grabNew(new SceneClassAssistant()); + if (testProperty(Property::SCENE_CONTAINS_ATTRIBUTES)) { + m_sceneAssistant->add("m_startCoordinate", + "AnnotationCoordinate", + m_startCoordinate); + m_sceneAssistant->add("m_endCoordinate", + "AnnotationCoordinate", + m_endCoordinate); + } } /** @@ -282,14 +290,14 @@ AnnotationOneDimensionalShape::setRotationAngle(const float viewportWidth, getEndCoordinate()->getViewportXY(viewportWidth, viewportHeight, annTwoX, annTwoY); const float midPointXYZ[3] = { - (annOneX + annTwoX) / 2.0, - (annOneY + annTwoY) / 2.0, - 0.0 + (annOneX + annTwoX) / 2.0f, + (annOneY + annTwoY) / 2.0f, + 0.0f }; - const float vpOneXYZ[3] = { annOneX, annOneY, 0.0 }; + const float vpOneXYZ[3] = { annOneX, annOneY, 0.0f }; const float lengthMidToOne = MathFunctions::distance3D(midPointXYZ, vpOneXYZ); - const float newRotationAngle = 180.0 - rotationAngle; + const float newRotationAngle = 180.0f - rotationAngle; const float angleRadians = MathFunctions::toRadians(newRotationAngle); const float dy = lengthMidToOne * std::sin(angleRadians); @@ -315,24 +323,22 @@ AnnotationOneDimensionalShape::setRotationAngle(const float viewportWidth, bool AnnotationOneDimensionalShape::isSizeHandleValid(const AnnotationSizingHandleTypeEnum::Enum sizingHandle) const { - bool pixelsFlag = false; + bool chartFlag = false; bool tabWindowFlag = false; - bool stereotaxicFlag = false; - bool surfaceFlag = false; switch (getCoordinateSpace()) { - case AnnotationCoordinateSpaceEnum::PIXELS: - pixelsFlag = true; + case AnnotationCoordinateSpaceEnum::CHART: + chartFlag = true; break; case AnnotationCoordinateSpaceEnum::STEREOTAXIC: - stereotaxicFlag = true; break; case AnnotationCoordinateSpaceEnum::SURFACE: - surfaceFlag = true; break; case AnnotationCoordinateSpaceEnum::TAB: tabWindowFlag = true; break; + case AnnotationCoordinateSpaceEnum::VIEWPORT: + break; case AnnotationCoordinateSpaceEnum::WINDOW: tabWindowFlag = true; break; @@ -340,41 +346,40 @@ AnnotationOneDimensionalShape::isSizeHandleValid(const AnnotationSizingHandleTyp bool validFlag = false; - if ( ! pixelsFlag) { - switch (sizingHandle) { - case AnnotationSizingHandleTypeEnum::ANNOTATION_SIZING_HANDLE_BOX_BOTTOM: - break; - case AnnotationSizingHandleTypeEnum::ANNOTATION_SIZING_HANDLE_BOX_BOTTOM_LEFT: - break; - case AnnotationSizingHandleTypeEnum::ANNOTATION_SIZING_HANDLE_BOX_BOTTOM_RIGHT: - break; - case AnnotationSizingHandleTypeEnum::ANNOTATION_SIZING_HANDLE_BOX_LEFT: - break; - case AnnotationSizingHandleTypeEnum::ANNOTATION_SIZING_HANDLE_BOX_RIGHT: - break; - case AnnotationSizingHandleTypeEnum::ANNOTATION_SIZING_HANDLE_BOX_TOP: - break; - case AnnotationSizingHandleTypeEnum::ANNOTATION_SIZING_HANDLE_BOX_TOP_LEFT: - break; - case AnnotationSizingHandleTypeEnum::ANNOTATION_SIZING_HANDLE_BOX_TOP_RIGHT: - break; - case AnnotationSizingHandleTypeEnum::ANNOTATION_SIZING_HANDLE_LINE_END: + switch (sizingHandle) { + case AnnotationSizingHandleTypeEnum::ANNOTATION_SIZING_HANDLE_BOX_BOTTOM: + break; + case AnnotationSizingHandleTypeEnum::ANNOTATION_SIZING_HANDLE_BOX_BOTTOM_LEFT: + break; + case AnnotationSizingHandleTypeEnum::ANNOTATION_SIZING_HANDLE_BOX_BOTTOM_RIGHT: + break; + case AnnotationSizingHandleTypeEnum::ANNOTATION_SIZING_HANDLE_BOX_LEFT: + break; + case AnnotationSizingHandleTypeEnum::ANNOTATION_SIZING_HANDLE_BOX_RIGHT: + break; + case AnnotationSizingHandleTypeEnum::ANNOTATION_SIZING_HANDLE_BOX_TOP: + break; + case AnnotationSizingHandleTypeEnum::ANNOTATION_SIZING_HANDLE_BOX_TOP_LEFT: + break; + case AnnotationSizingHandleTypeEnum::ANNOTATION_SIZING_HANDLE_BOX_TOP_RIGHT: + break; + case AnnotationSizingHandleTypeEnum::ANNOTATION_SIZING_HANDLE_LINE_END: + validFlag = true; + break; + case AnnotationSizingHandleTypeEnum::ANNOTATION_SIZING_HANDLE_LINE_START: + validFlag = true; + break; + case AnnotationSizingHandleTypeEnum::ANNOTATION_SIZING_HANDLE_NONE: + if (chartFlag + || tabWindowFlag) { validFlag = true; - break; - case AnnotationSizingHandleTypeEnum::ANNOTATION_SIZING_HANDLE_LINE_START: + } + break; + case AnnotationSizingHandleTypeEnum::ANNOTATION_SIZING_HANDLE_ROTATION: + if (tabWindowFlag) { validFlag = true; - break; - case AnnotationSizingHandleTypeEnum::ANNOTATION_SIZING_HANDLE_NONE: - if (tabWindowFlag) { - validFlag = true; - } - break; - case AnnotationSizingHandleTypeEnum::ANNOTATION_SIZING_HANDLE_ROTATION: - if (tabWindowFlag) { - validFlag = true; - } - break; - } + } + break; } return validFlag; @@ -485,12 +490,12 @@ AnnotationOneDimensionalShape::applySpatialModificationTabOrWindowSpace(const An float newX2 = xyz2[0]; float newY2 = xyz2[1]; - const float spaceDX = 100.0 * ((spatialModification.m_viewportWidth != 0.0) + const float spaceDX = 100.0f * ((spatialModification.m_viewportWidth != 0.0f) ? (spatialModification.m_mouseDX / spatialModification.m_viewportWidth) - : 0.0); - const float spaceDY = 100.0 * ((spatialModification.m_viewportHeight != 0.0) + : 0.0f); + const float spaceDY = 100.0f * ((spatialModification.m_viewportHeight != 0.0f) ? (spatialModification.m_mouseDY / spatialModification.m_viewportHeight) - : 0.0); + : 0.0f); bool validFlag = false; switch (spatialModification.m_sizingHandleType) { case AnnotationSizingHandleTypeEnum::ANNOTATION_SIZING_HANDLE_BOX_BOTTOM: @@ -540,9 +545,9 @@ AnnotationOneDimensionalShape::applySpatialModificationTabOrWindowSpace(const An vpTwoXYZ); float vpXYZ[3] = { - (vpOneXYZ[0] + vpTwoXYZ[0]) / 2.0, - (vpOneXYZ[1] + vpTwoXYZ[1]) / 2.0, - (vpOneXYZ[2] + vpTwoXYZ[2]) / 2.0 + (vpOneXYZ[0] + vpTwoXYZ[0]) / 2.0f, + (vpOneXYZ[1] + vpTwoXYZ[1]) / 2.0f, + (vpOneXYZ[2] + vpTwoXYZ[2]) / 2.0f }; /* @@ -602,6 +607,71 @@ AnnotationOneDimensionalShape::applySpatialModificationTabOrWindowSpace(const An return validFlag; } +/** + * Apply a spatial modification to an annotation in chart space. + * + * @param spatialModification + * Contains information about the spatial modification. + * @return + * True if the annotation was modified, else false. + */ +bool +AnnotationOneDimensionalShape::applySpatialModificationChartSpace(const AnnotationSpatialModification& spatialModification) +{ + bool validFlag = false; + + switch (spatialModification.m_sizingHandleType) { + case AnnotationSizingHandleTypeEnum::ANNOTATION_SIZING_HANDLE_BOX_BOTTOM: + break; + case AnnotationSizingHandleTypeEnum::ANNOTATION_SIZING_HANDLE_BOX_BOTTOM_LEFT: + break; + case AnnotationSizingHandleTypeEnum::ANNOTATION_SIZING_HANDLE_BOX_BOTTOM_RIGHT: + break; + case AnnotationSizingHandleTypeEnum::ANNOTATION_SIZING_HANDLE_BOX_LEFT: + break; + case AnnotationSizingHandleTypeEnum::ANNOTATION_SIZING_HANDLE_BOX_RIGHT: + break; + case AnnotationSizingHandleTypeEnum::ANNOTATION_SIZING_HANDLE_BOX_TOP: + break; + case AnnotationSizingHandleTypeEnum::ANNOTATION_SIZING_HANDLE_BOX_TOP_LEFT: + break; + case AnnotationSizingHandleTypeEnum::ANNOTATION_SIZING_HANDLE_BOX_TOP_RIGHT: + break; + case AnnotationSizingHandleTypeEnum::ANNOTATION_SIZING_HANDLE_LINE_END: + if (spatialModification.m_chartCoordAtMouseXY.m_chartXYZValid) { + m_endCoordinate->setXYZ(spatialModification.m_chartCoordAtMouseXY.m_chartXYZ); + validFlag = true; + } + break; + case AnnotationSizingHandleTypeEnum::ANNOTATION_SIZING_HANDLE_LINE_START: + if (spatialModification.m_chartCoordAtMouseXY.m_chartXYZValid) { + m_startCoordinate->setXYZ(spatialModification.m_chartCoordAtMouseXY.m_chartXYZ); + validFlag = true; + } + break; + case AnnotationSizingHandleTypeEnum::ANNOTATION_SIZING_HANDLE_NONE: + if (spatialModification.m_chartCoordAtMouseXY.m_chartXYZValid + && spatialModification.m_chartCoordAtPreviousMouseXY.m_chartXYZValid) { + const float dx = spatialModification.m_chartCoordAtMouseXY.m_chartXYZ[0] - spatialModification.m_chartCoordAtPreviousMouseXY.m_chartXYZ[0]; + const float dy = spatialModification.m_chartCoordAtMouseXY.m_chartXYZ[1] - spatialModification.m_chartCoordAtPreviousMouseXY.m_chartXYZ[1]; + const float dz = spatialModification.m_chartCoordAtMouseXY.m_chartXYZ[2] - spatialModification.m_chartCoordAtPreviousMouseXY.m_chartXYZ[2]; + + m_startCoordinate->addToXYZ(dx, dy, dz); + m_endCoordinate->addToXYZ(dx, dy, dz); + validFlag = true; + } + break; + case AnnotationSizingHandleTypeEnum::ANNOTATION_SIZING_HANDLE_ROTATION: + break; + } + + if (validFlag) { + setModified(); + } + + return validFlag; +} + /** * Apply a spatial modification to an annotation in stereotaxic space. * @@ -673,7 +743,8 @@ AnnotationOneDimensionalShape::applySpatialModification(const AnnotationSpatialM } switch (getCoordinateSpace()) { - case AnnotationCoordinateSpaceEnum::PIXELS: + case AnnotationCoordinateSpaceEnum::CHART: + return applySpatialModificationChartSpace(spatialModification); break; case AnnotationCoordinateSpaceEnum::STEREOTAXIC: return applySpatialModificationStereotaxicSpace(spatialModification); @@ -684,6 +755,8 @@ AnnotationOneDimensionalShape::applySpatialModification(const AnnotationSpatialM case AnnotationCoordinateSpaceEnum::TAB: return applySpatialModificationTabOrWindowSpace(spatialModification); break; + case AnnotationCoordinateSpaceEnum::VIEWPORT: + break; case AnnotationCoordinateSpaceEnum::WINDOW: return applySpatialModificationTabOrWindowSpace(spatialModification); break; diff --git a/src/Annotations/AnnotationOneDimensionalShape.h b/src/Annotations/AnnotationOneDimensionalShape.h index b116506e5d6c6feb92ba3764f071d386e33203c5..e9f05f9dcfd3e7edf9c04e992bcc7eacdf407d87 100644 --- a/src/Annotations/AnnotationOneDimensionalShape.h +++ b/src/Annotations/AnnotationOneDimensionalShape.h @@ -86,6 +86,8 @@ namespace caret { void initializeMembersAnnotationOneDimensionalShape(); + bool applySpatialModificationChartSpace(const AnnotationSpatialModification& spatialModification); + bool applySpatialModificationSurfaceSpace(const AnnotationSpatialModification& spatialModification); bool applySpatialModificationStereotaxicSpace(const AnnotationSpatialModification& spatialModification); diff --git a/src/Annotations/AnnotationOval.cxx b/src/Annotations/AnnotationOval.cxx index 490118ea7165424e6bc57298c44210deec8e26bd..f8c9212e50767666edd3c3e49cfbae0197b6e4ce 100644 --- a/src/Annotations/AnnotationOval.cxx +++ b/src/Annotations/AnnotationOval.cxx @@ -104,6 +104,8 @@ void AnnotationOval::initializeMembersAnnotationOval() { m_sceneAssistant.grabNew(new SceneClassAssistant()); + if (testProperty(Property::SCENE_CONTAINS_ATTRIBUTES)) { + } } /** diff --git a/src/Annotations/AnnotationPercentSizeText.cxx b/src/Annotations/AnnotationPercentSizeText.cxx index f52dfe5be9e2e487d655e280ea8af1a61c4d5848..62b860592ec3f22261c01aca6d8e0baec94a680b 100644 --- a/src/Annotations/AnnotationPercentSizeText.cxx +++ b/src/Annotations/AnnotationPercentSizeText.cxx @@ -24,6 +24,8 @@ #undef __ANNOTATION_PERCENT_SIZE_TEXT_DECLARE__ #include "CaretAssert.h" +#include "CaretLogger.h" + using namespace caret; @@ -34,6 +36,60 @@ using namespace caret; * \ingroup Annotations */ +/** + * Constructor. + * + * @param attributeDefaultType + * Type for attribute defaults + * @param textFontSizeType + * Type of font sizing must be percentage height/width + */ +AnnotationPercentSizeText::AnnotationPercentSizeText(const AnnotationAttributesDefaultTypeEnum::Enum attributeDefaultType, + const AnnotationTextFontSizeTypeEnum::Enum textFontSizeType) +: AnnotationText(attributeDefaultType, + textFontSizeType) +{ + switch (textFontSizeType) { + case AnnotationTextFontSizeTypeEnum::POINTS: + CaretAssertMessage(0, "POINTS font size type not allowed for AnnotationPercentSizeText"); + CaretLogSevere("POINTS font size type not allowed for AnnotationPercentSizeText"); + break; + case AnnotationTextFontSizeTypeEnum::PERCENTAGE_OF_VIEWPORT_WIDTH: + break; + case AnnotationTextFontSizeTypeEnum::PERCENTAGE_OF_VIEWPORT_HEIGHT: + break; + } +} + +/** + * Constructor for subclass. + * + * @param type + * Type of annotation. + * @param attributeDefaultType + * Type for attribute defaults + * @param textFontSizeType + * Type of font sizing must be percentage height/width + */ +AnnotationPercentSizeText::AnnotationPercentSizeText(const AnnotationTypeEnum::Enum type, + const AnnotationAttributesDefaultTypeEnum::Enum attributeDefaultType, + const AnnotationTextFontSizeTypeEnum::Enum textFontSizeType) +: AnnotationText(type, + attributeDefaultType, + textFontSizeType) +{ + switch (textFontSizeType) { + case AnnotationTextFontSizeTypeEnum::POINTS: + CaretAssertMessage(0, "POINTS font size type not allowed for AnnotationPercentSizeText"); + CaretLogSevere("POINTS font size type not allowed for AnnotationPercentSizeText"); + break; + case AnnotationTextFontSizeTypeEnum::PERCENTAGE_OF_VIEWPORT_WIDTH: + break; + case AnnotationTextFontSizeTypeEnum::PERCENTAGE_OF_VIEWPORT_HEIGHT: + break; + } +} + /** * Constructor. * diff --git a/src/Annotations/AnnotationPercentSizeText.h b/src/Annotations/AnnotationPercentSizeText.h index 9984efa03e644e6b011876c0f83ccd77ce8de0f7..983c60a6109074da29ce693a8f79a03f6f7dc2a9 100644 --- a/src/Annotations/AnnotationPercentSizeText.h +++ b/src/Annotations/AnnotationPercentSizeText.h @@ -33,7 +33,10 @@ namespace caret { public: AnnotationPercentSizeText(const AnnotationAttributesDefaultTypeEnum::Enum attributeDefaultType); - virtual ~AnnotationPercentSizeText(); + AnnotationPercentSizeText(const AnnotationAttributesDefaultTypeEnum::Enum attributeDefaultType, + const AnnotationTextFontSizeTypeEnum::Enum textFontSizeType); + + virtual ~AnnotationPercentSizeText(); AnnotationPercentSizeText(const AnnotationPercentSizeText& obj); @@ -45,6 +48,11 @@ namespace caret { // ADD_NEW_METHODS_HERE + protected: + AnnotationPercentSizeText(const AnnotationTypeEnum::Enum type, + const AnnotationAttributesDefaultTypeEnum::Enum attributeDefaultType, + const AnnotationTextFontSizeTypeEnum::Enum textFontSizeType); + private: void copyHelperAnnotationPercentSizeText(const AnnotationPercentSizeText& obj); diff --git a/src/Annotations/AnnotationRedoUndoCommand.cxx b/src/Annotations/AnnotationRedoUndoCommand.cxx index ba84295a84c60aebb00b254493755625d3bbc518..df16def5dc97eaced726bbf20b90f1be50624ab8 100644 --- a/src/Annotations/AnnotationRedoUndoCommand.cxx +++ b/src/Annotations/AnnotationRedoUndoCommand.cxx @@ -188,6 +188,11 @@ AnnotationRedoUndoCommand::applyRedoOrUndo(Annotation* annotation, + AnnotationRedoUndoCommandModeEnum::toName(m_mode) + " is handle in the redo() and undo() functions.")); break; + case AnnotationRedoUndoCommandModeEnum::DUPLICATE_ANNOTATION: + CaretAssertMessage(0, ("This mode " + + AnnotationRedoUndoCommandModeEnum::toName(m_mode) + + " is handle in the redo() and undo() functions.")); + break; case AnnotationRedoUndoCommandModeEnum::GROUPING_GROUP: CaretAssert(0); case AnnotationRedoUndoCommandModeEnum::GROUPING_REGROUP: @@ -219,9 +224,8 @@ AnnotationRedoUndoCommand::applyRedoOrUndo(Annotation* annotation, } break; case AnnotationRedoUndoCommandModeEnum::LINE_WIDTH_FOREGROUND: - if (annotation->isLineWidthSupported()) { - //annotation->setLineWidth(value.m_floatValue); - annotation->setLineWidth(annotationValue->getLineWidth()); + if (annotation->testProperty(Annotation::Property::LINE_THICKNESS)) { + annotation->setLineWidthPercentage(annotationValue->getLineWidthPercentage()); } break; case AnnotationRedoUndoCommandModeEnum::LOCATION_AND_SIZE: @@ -483,8 +487,6 @@ AnnotationRedoUndoCommand::redo(AString& errorMessageOut) EventManager::get()->sendEvent(groupEvent.getPointer()); - //m_annotationGroupMemento->setUndoAnnotationGroupKey(groupEvent.getGroupKeyToWhichAnnotationsWereMoved()); - if (groupEvent.isError()) { errorMessageOut = groupEvent.getErrorMessage(); return false; @@ -550,6 +552,17 @@ AnnotationRedoUndoCommand::redo(AString& errorMessageOut) validFlag = false; } } + else if (m_mode == AnnotationRedoUndoCommandModeEnum::DUPLICATE_ANNOTATION) { + EventAnnotationAddToRemoveFromFile duplicateEvent(EventAnnotationAddToRemoveFromFile::MODE_DUPLICATE, + annMem->m_annotationFile, + annMem->m_annotation); + EventManager::get()->sendEvent(duplicateEvent.getPointer()); + + if (duplicateEvent.isError()) { + errorMessageOut.appendWithNewLine(duplicateEvent.getErrorMessage()); + validFlag = false; + } + } else if (m_mode == AnnotationRedoUndoCommandModeEnum::PASTE_ANNOTATION) { EventAnnotationAddToRemoveFromFile pasteEvent(EventAnnotationAddToRemoveFromFile::MODE_PASTE, annMem->m_annotationFile, @@ -607,9 +620,6 @@ AnnotationRedoUndoCommand::undo(AString& errorMessageOut) EventManager::get()->sendEvent(groupEvent.getPointer()); - //m_annotationGroupMemento->setUndoAnnotationGroupKey(groupEvent.getGroupKeyToWhichAnnotationsWereMoved()); - - if (groupEvent.isError()) { errorMessageOut = groupEvent.getErrorMessage(); return false; @@ -676,6 +686,17 @@ AnnotationRedoUndoCommand::undo(AString& errorMessageOut) validFlag = false; } } + else if (m_mode == AnnotationRedoUndoCommandModeEnum::DUPLICATE_ANNOTATION) { + EventAnnotationAddToRemoveFromFile duplicateEvent(EventAnnotationAddToRemoveFromFile::MODE_UNDUPLICATE, + annMem->m_annotationFile, + annMem->m_annotation); + EventManager::get()->sendEvent(duplicateEvent.getPointer()); + + if (duplicateEvent.isError()) { + errorMessageOut.appendWithNewLine(duplicateEvent.getErrorMessage()); + validFlag = false; + } + } else if (m_mode == AnnotationRedoUndoCommandModeEnum::PASTE_ANNOTATION) { EventAnnotationAddToRemoveFromFile pasteEvent(EventAnnotationAddToRemoveFromFile::MODE_UNPASTE, annMem->m_annotationFile, @@ -710,8 +731,6 @@ AnnotationRedoUndoCommand::isValid() const } return false; - -// return m_annotationMementos.size(); } /** @@ -1047,7 +1066,7 @@ AnnotationRedoUndoCommand::setModeLineWidth(const float newLineWidth, CaretAssert(annotation); Annotation* redoAnnotation = annotation->clone(); - redoAnnotation->setLineWidth(newLineWidth); + redoAnnotation->setLineWidthPercentage(newLineWidth); Annotation* undoAnnotation = annotation->clone(); AnnotationMemento* am = new AnnotationMemento(annotation, @@ -1325,6 +1344,41 @@ AnnotationRedoUndoCommand::setModePasteAnnotation(AnnotationFile* annotationFile m_annotationMementos.push_back(am); } +/** + * Set the mode to duplicate annotations and create the undo/redo instances. + * + * @param annotationFile + * File to which annotation is pasted. + * @param annotations + * The new anntotation duplicated from existing annotation. + */ +void +AnnotationRedoUndoCommand::setModeDuplicateAnnotation(AnnotationFile* annotationFile, + Annotation* annotation) +{ + m_mode = AnnotationRedoUndoCommandModeEnum::DUPLICATE_ANNOTATION; + setDescription("Duplicate Annotation"); + + CaretAssert(annotationFile); + CaretAssert(annotation); + + /* + * NOTE: We only need the pointer since the file containing + * the annotation will handle delete/undelete of the + * annotation. If we don't use NULL for the redo and + * undo annotations, copies of the annotation would be + * needed since the AnnotationMemento will delete + * the redo and undo annotations when it is deleted. + */ + AnnotationMemento* am = new AnnotationMemento(annotationFile, + annotation, + NULL, + NULL); + + m_annotationMementos.push_back(am); +} + + /** * Set the mode to rotation angle and create the undo/redo instances * @@ -1362,7 +1416,7 @@ AnnotationRedoUndoCommand::setModeRotationAngle(const float newRotationAngle, int32_t viewport[4] = { 0, 0, 0, 0 }; bool viewportValid = false; switch (oneDimAnn->getCoordinateSpace()) { - case AnnotationCoordinateSpaceEnum::PIXELS: + case AnnotationCoordinateSpaceEnum::CHART: break; case AnnotationCoordinateSpaceEnum::STEREOTAXIC: break; @@ -1380,6 +1434,8 @@ AnnotationRedoUndoCommand::setModeRotationAngle(const float newRotationAngle, } } break; + case AnnotationCoordinateSpaceEnum::VIEWPORT: + break; case AnnotationCoordinateSpaceEnum::WINDOW: { const int windowIndex = oneDimAnn->getWindowIndex(); @@ -1407,37 +1463,10 @@ AnnotationRedoUndoCommand::setModeRotationAngle(const float newRotationAngle, const bool rotateAroundMiddleFlag = true; if (rotateAroundMiddleFlag) { -// const float midPointXYZ[3] = { -// (annOneX + annTwoX) / 2.0, -// (annOneY + annTwoY) / 2.0, -// 0.0 -// }; -// -// const float vpOneXYZ[3] = { annOneX, annOneY, 0.0 }; -// const float vpTwoXYZ[3] = { annTwoX, annTwoY, 0.0 }; -// const float length = MathFunctions::distance3D(vpOneXYZ, vpTwoXYZ); -// const float lengthMidToOne = MathFunctions::distance3D(midPointXYZ, vpOneXYZ); -// const float angleRadians = MathFunctions::toRadians(-newRotationAngle); -// const float dy = lengthMidToOne * std::sin(angleRadians); -// const float dx = lengthMidToOne * std::cos(angleRadians); -// annOneX = midPointXYZ[0] - dx; -// annOneY = midPointXYZ[1] - dy; -// -// annTwoX = midPointXYZ[0] + dx; -// annTwoY = midPointXYZ[1] + dy; -// -//// const float newVpOneXYZ[3] = { annOneX, annOneY, 0.0 }; -//// float vectorOneToMid[3] = { 0.0, 0.0, 0.0 }; -//// MathFunctions::subtractVectors(newVpOneXYZ, midPointXYZ, vectorOneToMid); -//// MathFunctions::normalizeVector(vectorOneToMid); -//// annTwoX = annOneX + vectorOneToMid[0] * length; -//// annTwoY = annOneY + vectorOneToMid[1] * length; AnnotationOneDimensionalShape* redoAnnotation = dynamic_cast(annotation->clone()); CaretAssert(redoAnnotation); redoAnnotation->setRotationAngle(vpWidth, vpHeight, newRotationAngle); -// redoAnnotation->getStartCoordinate()->setXYZFromViewportXYZ(vpWidth, vpHeight, annOneX, annOneY); -// redoAnnotation->getEndCoordinate()->setXYZFromViewportXYZ(vpWidth, vpHeight, annTwoX, annTwoY); Annotation* undoAnnotation = annotation->clone(); AnnotationMemento* am = new AnnotationMemento(annotation, redoAnnotation, @@ -1465,29 +1494,6 @@ AnnotationRedoUndoCommand::setModeRotationAngle(const float newRotationAngle, m_annotationMementos.push_back(am); } } -// float xyzOne[3]; -// oneDimAnn->getStartCoordinate()->getXYZ(xyzOne); -// float xyzTwo[3]; -// oneDimAnn->getEndCoordinate()->getXYZ(xyzTwo); -// -//// const float dx = xyzTwo[0] - xyzOne[0]; -//// const float dy = xyzTwo[1] - xyzOne[1]; -// const float length = MathFunctions::distance3D(xyzOne, xyzTwo); -// const float angleRadians = MathFunctions::toRadians(-newRotationAngle); -// const float dy = length * std::sin(angleRadians); -// const float dx = length * std::cos(angleRadians); -// xyzTwo[0] = xyzOne[0] + dx; -// xyzTwo[1] = xyzOne[1] + dy; -// -// -// AnnotationOneDimensionalShape* redoAnnotation = dynamic_cast(annotation->clone()); -// CaretAssert(redoAnnotation); -// redoAnnotation->getEndCoordinate()->setXYZ(xyzTwo); -// Annotation* undoAnnotation = annotation->clone(); -// AnnotationMemento* am = new AnnotationMemento(annotation, -// redoAnnotation, -// undoAnnotation); -// m_annotationMementos.push_back(am); } } @@ -1586,9 +1592,29 @@ AnnotationRedoUndoCommand::setModeTextCharacters(const AString& text, Annotation* annotation = *iter; CaretAssert(annotation); - if (annotation->getType() == AnnotationTypeEnum::TEXT) { - AnnotationText* redoAnnotation = dynamic_cast(annotation->clone()); - CaretAssert(redoAnnotation); + AnnotationText* redoAnnotation = NULL; + switch (annotation->getType()) { + case AnnotationTypeEnum::BOX: + break; + case AnnotationTypeEnum::COLOR_BAR: + break; + case AnnotationTypeEnum::IMAGE: + break; + case AnnotationTypeEnum::LINE: + break; + case AnnotationTypeEnum::OVAL: + break; + case AnnotationTypeEnum::TEXT: + redoAnnotation = dynamic_cast(annotation->clone()); + break; + } + + /* + * This method should only get call for text-type annotations. + */ + CaretAssert(redoAnnotation); + + if (redoAnnotation != NULL) { redoAnnotation->setText(text); Annotation* undoAnnotation = annotation->clone(); @@ -1853,7 +1879,7 @@ AnnotationRedoUndoCommand::setModeTextFontPercentSize(const float newFontPercent float percentSize = newFontPercentSize; switch (redoAnnotation->getCoordinateSpace()) { - case AnnotationCoordinateSpaceEnum::PIXELS: + case AnnotationCoordinateSpaceEnum::CHART: break; case AnnotationCoordinateSpaceEnum::STEREOTAXIC: percentSize *= surfaceSpaceRowCount; @@ -1863,6 +1889,8 @@ AnnotationRedoUndoCommand::setModeTextFontPercentSize(const float newFontPercent break; case AnnotationCoordinateSpaceEnum::TAB: break; + case AnnotationCoordinateSpaceEnum::VIEWPORT: + break; case AnnotationCoordinateSpaceEnum::WINDOW: break; } @@ -1878,33 +1906,6 @@ AnnotationRedoUndoCommand::setModeTextFontPercentSize(const float newFontPercent } } -//void -//AnnotationRedoUndoCommand::setModeTextFontPercentSize(const float newFontPercentSize, -// const std::vector& annotations) -//{ -// m_mode = AnnotationRedoUndoCommandModeEnum::TEXT_FONT_PERCENT_SIZE; -// setDescription("Text Font Percent Size"); -// -// for (std::vector::const_iterator iter = annotations.begin(); -// iter != annotations.end(); -// iter++) { -// Annotation* annotation = *iter; -// CaretAssert(annotation); -// -// if (annotation->getType() == AnnotationTypeEnum::TEXT) { -// AnnotationPercentSizeText* redoAnnotation = dynamic_cast(annotation->clone()); -// CaretAssert(redoAnnotation); -// redoAnnotation->setFontPercentViewportSize(newFontPercentSize); -// -// Annotation* undoAnnotation = annotation->clone(); -// AnnotationMemento* am = new AnnotationMemento(annotation, -// redoAnnotation, -// undoAnnotation); -// m_annotationMementos.push_back(am); -// } -// } -//} - /** * Set the mode to text font underline and create the undo/redo instances. * diff --git a/src/Annotations/AnnotationRedoUndoCommand.h b/src/Annotations/AnnotationRedoUndoCommand.h index ec99acd5c0658b188bbb2e57b5bdb1e75397770c..c15773d4652b0030f85436c8debb0351298aec8b 100644 --- a/src/Annotations/AnnotationRedoUndoCommand.h +++ b/src/Annotations/AnnotationRedoUndoCommand.h @@ -96,6 +96,9 @@ namespace caret { void setModePasteAnnotation(AnnotationFile* annotationFile, Annotation* annotation); + void setModeDuplicateAnnotation(AnnotationFile* annotationFile, + Annotation* annotation); + void setModeRotationAngle(const float newRotationAngle, const std::vector& annotations); diff --git a/src/Annotations/AnnotationRedoUndoCommandModeEnum.cxx b/src/Annotations/AnnotationRedoUndoCommandModeEnum.cxx index 63884b8bc5bdb073173ad8f0063aae7b1c1c6c13..8ec48ec93223c199bdc9f0e1a2cb8a7d85ba5735 100644 --- a/src/Annotations/AnnotationRedoUndoCommandModeEnum.cxx +++ b/src/Annotations/AnnotationRedoUndoCommandModeEnum.cxx @@ -138,6 +138,10 @@ AnnotationRedoUndoCommandModeEnum::initialize() "DELETE_ANNOTATIONS", "Delete Annotations")); + enumData.push_back(AnnotationRedoUndoCommandModeEnum(DUPLICATE_ANNOTATION, + "DUPLICATE_ANNOTATION", + "Duplicate Annotation")); + enumData.push_back(AnnotationRedoUndoCommandModeEnum(GROUPING_GROUP, "GROUPING_GROUP", "Group Annotations")); diff --git a/src/Annotations/AnnotationRedoUndoCommandModeEnum.h b/src/Annotations/AnnotationRedoUndoCommandModeEnum.h index b9c0caf76db667250f634ac864a114416abafdcb..3b8d2c84f1c7cabc64d4e5a68f37c462098473a4 100644 --- a/src/Annotations/AnnotationRedoUndoCommandModeEnum.h +++ b/src/Annotations/AnnotationRedoUndoCommandModeEnum.h @@ -53,6 +53,8 @@ public: CUT_ANNOTATION, /** Delete Annotations */ DELETE_ANNOTATIONS, + /** Duplicate Annotation */ + DUPLICATE_ANNOTATION, /** Group Annotations */ GROUPING_GROUP, /** Regroup Annotations */ diff --git a/src/Annotations/AnnotationSpatialModification.cxx b/src/Annotations/AnnotationSpatialModification.cxx index f4e3ebdcbe76f09a4e2d0d4f7b1d976364f8d76f..f78b65882c8bd891c49eb5ac5c1246a654c477d3 100644 --- a/src/Annotations/AnnotationSpatialModification.cxx +++ b/src/Annotations/AnnotationSpatialModification.cxx @@ -80,6 +80,10 @@ m_mouseDX(mouseDX), m_mouseDY(mouseDY), m_startOfDraggingFlag(startOfDraggingFlag) { + m_chartCoordAtMouseXY.m_chartXYZValid = false; + m_chartCoordAtPreviousMouseXY.m_chartXYZValid = false; + m_stereotaxicCoordinateAtMouseXY.m_stereotaxicValid = false; + m_surfaceCoordinateAtMouseXY.m_surfaceNodeValid = false; } /** @@ -137,6 +141,49 @@ AnnotationSpatialModification::setStereotaxicCoordinateAtMouseXY(const float ste m_stereotaxicCoordinateAtMouseXY.m_stereotaxicValid = true; } +/** + * Set the chart coordinate at mouse X/Y + * + * @param chartX + * chart X-coordinate. + * @param chartY + * chart Y-coordinate. + * @param chartZ + * chart Z-coordinate. + */ +void +AnnotationSpatialModification::setChartCoordinateAtMouseXY(const float chartX, + const float chartY, + const float chartZ) +{ + m_chartCoordAtMouseXY.m_chartXYZ[0] = chartX; + m_chartCoordAtMouseXY.m_chartXYZ[1] = chartY; + m_chartCoordAtMouseXY.m_chartXYZ[2] = chartZ; + m_chartCoordAtMouseXY.m_chartXYZValid = true; +} + +/** + * Set the chart coordinate at previous mouse X/Y + * + * @param chartX + * chart X-coordinate. + * @param chartY + * chart Y-coordinate. + * @param chartZ + * chart Z-coordinate. + */ +void +AnnotationSpatialModification::setChartCoordinateAtPreviousMouseXY(const float chartX, + const float chartY, + const float chartZ) +{ + m_chartCoordAtPreviousMouseXY.m_chartXYZ[0] = chartX; + m_chartCoordAtPreviousMouseXY.m_chartXYZ[1] = chartY; + m_chartCoordAtPreviousMouseXY.m_chartXYZ[2] = chartZ; + m_chartCoordAtPreviousMouseXY.m_chartXYZValid = true; +} + + /** * Get a description of this object's content. * @return String describing this object's content. diff --git a/src/Annotations/AnnotationSpatialModification.h b/src/Annotations/AnnotationSpatialModification.h index ea2a7d6b86fb8c714bdcd1aa26458fdbb97e49b2..4699198f31d25f025600934e6d76eb4cfc491fba 100644 --- a/src/Annotations/AnnotationSpatialModification.h +++ b/src/Annotations/AnnotationSpatialModification.h @@ -52,6 +52,14 @@ namespace caret { const float stereotaxicY, const float stereotaxicZ); + void setChartCoordinateAtMouseXY(const float chartX, + const float chartY, + const float chartZ); + + void setChartCoordinateAtPreviousMouseXY(const float chartX, + const float chartY, + const float chartZ); + virtual ~AnnotationSpatialModification(); @@ -77,6 +85,21 @@ namespace caret { bool m_surfaceNodeValid; }; + class ChartCoord { + public: + ChartCoord() { + m_chartXYZ[0] = 0.0; + m_chartXYZ[1] = 0.0; + m_chartXYZ[2] = 0.0; + + m_chartXYZValid = false; + } + + float m_chartXYZ[3]; + + bool m_chartXYZValid; + }; + class StereotaxicCoord { public: @@ -117,6 +140,10 @@ namespace caret { const bool m_startOfDraggingFlag; + ChartCoord m_chartCoordAtMouseXY; + + ChartCoord m_chartCoordAtPreviousMouseXY; + SurfaceCoord m_surfaceCoordinateAtMouseXY; StereotaxicCoord m_stereotaxicCoordinateAtMouseXY; diff --git a/src/Annotations/AnnotationText.cxx b/src/Annotations/AnnotationText.cxx index aa5cbaeadd90f01bc502859d5d46a94af307a98c..74ea28c96ab2e34237e4de7f12b249e0d0eb10a7 100644 --- a/src/Annotations/AnnotationText.cxx +++ b/src/Annotations/AnnotationText.cxx @@ -41,16 +41,6 @@ using namespace caret; * \ingroup Annotations */ -///** -// * Constructor for a text annotation. -// */ -//AnnotationText::AnnotationText() -//: AnnotationTwoDimensionalShape(AnnotationTypeEnum::TEXT), -//m_fontSizeType(AnnotationTextFontSizeTypeEnum::PERCENTAGE_OF_VIEWPORT_HEIGHT) -//{ -// initializeAnnotationTextMembers(); -//} - /** * Constructor for subclass. * @@ -69,6 +59,27 @@ m_fontSizeType(fontSizeType) initializeAnnotationTextMembers(); } +/** + * Constructor for subclass. + * + * @param type + * Type of annotation. + * @param attributeDefaultType + * Type for attribute defaults + * @param fontSizeType + * Type of font sizing. + */ +AnnotationText::AnnotationText(const AnnotationTypeEnum::Enum type, + const AnnotationAttributesDefaultTypeEnum::Enum attributeDefaultType, + const AnnotationTextFontSizeTypeEnum::Enum fontSizeType) +: AnnotationTwoDimensionalShape(type, + attributeDefaultType), +AnnotationFontAttributesInterface(), +m_fontSizeType(fontSizeType) +{ + initializeAnnotationTextMembers(); +} + /** * Destructor. */ @@ -152,7 +163,35 @@ AnnotationText::initializeAnnotationTextMembers() m_text = ""; - m_sceneAssistant.grabNew(new SceneClassAssistant()); + /* + * Assists with attributes that may be saved to scene (controlled by annotation property). + */ + m_attributesAssistant.grabNew(new SceneClassAssistant()); + m_attributesAssistant->add("m_alignmentHorizontal", + &m_alignmentHorizontal); + m_attributesAssistant->add("m_alignmentVertical", + &m_alignmentVertical); + m_attributesAssistant->add("m_font", + &m_font); + m_attributesAssistant->add("m_fontPointSize", + &m_fontPointSize); + m_attributesAssistant->add("m_orientation", + &m_orientation); + m_attributesAssistant->add("m_colorText", + &m_colorText); + m_attributesAssistant->addArray("m_customColorText", m_customColorText, 4, 1.0); + m_attributesAssistant->add("m_boldEnabled", + &m_boldEnabled); + m_attributesAssistant->add("m_italicEnabled", + &m_italicEnabled); + m_attributesAssistant->add("m_underlineEnabled", + &m_underlineEnabled); + m_attributesAssistant->add("m_connectToBrainordinate", + &m_connectToBrainordinate); + m_attributesAssistant->add("m_fontPercentViewportSize", + &m_fontPercentViewportSize); + m_attributesAssistant->add("m_text", + &m_text); } /** @@ -173,23 +212,44 @@ AnnotationText::initializeAnnotationTextMembers() * scale while drawing the text (using glScale()), the * quality is poor. * + * @param drawingViewportWidth + * Width of the viewport that may be used to scale the font height. * @param drawingViewportHeight * Height of the viewport that may be used to scale the font height. * @return * Encoded name for font. */ AString -AnnotationText::getFontRenderingEncodedName(const float drawingViewportHeight) const +AnnotationText::getFontRenderingEncodedName(const float drawingViewportWidth, + const float drawingViewportHeight) const { AString fontSizeID = AnnotationTextFontPointSizeEnum::toName(m_fontPointSize); - if (m_fontPercentViewportSize > 0.0) { - if (drawingViewportHeight> 0.0) { - const int32_t fontSizeInt = getFontSizeForDrawing(drawingViewportHeight); - if (fontSizeInt > 0) { - fontSizeID = "SIZE" + AString::number(fontSizeInt); + switch (m_fontSizeType) { + case AnnotationTextFontSizeTypeEnum::PERCENTAGE_OF_VIEWPORT_HEIGHT: + if (m_fontPercentViewportSize > 0.0) { + if (drawingViewportHeight > 0.0) { + const int32_t fontSizeInt = getFontSizeForDrawing(drawingViewportWidth, + drawingViewportHeight); + if (fontSizeInt > 0) { + fontSizeID = "SIZE" + AString::number(fontSizeInt); + } + } } - } + break; + case AnnotationTextFontSizeTypeEnum::PERCENTAGE_OF_VIEWPORT_WIDTH: + if (m_fontPercentViewportSize > 0.0) { + if (drawingViewportWidth > 0.0) { + const int32_t fontSizeInt = getFontSizeForDrawing(drawingViewportWidth, + drawingViewportHeight); + if (fontSizeInt > 0) { + fontSizeID = "SIZE" + AString::number(fontSizeInt); + } + } + } + break; + case AnnotationTextFontSizeTypeEnum::POINTS: + break; } AString encodedName; @@ -206,15 +266,6 @@ AnnotationText::getFontRenderingEncodedName(const float drawingViewportHeight) c if (m_italicEnabled) { encodedName.append("_I"); } - -// Underline and outline are not drawn by font rendering -// if (m_underlineEnabled) { -// encodedName.append("_U"); -// } -// -// if (m_outlineEnabled) { -// encodedName.append("_O"); -// } return encodedName; } @@ -362,17 +413,33 @@ AnnotationText::setFont(const AnnotationTextFontNameEnum::Enum font) * "percent size" of the viewport height in a range from zero to * one where one equivalent to the viewport's height. * + * @param drawingViewportWidth + * Width of the viewport that may be used to scale the font height. * @param drawingViewportHeight * Height of the viewport that may be used to scale the font height. * @return * Size of the font. */ int32_t -AnnotationText::getFontSizeForDrawing(const int32_t drawingViewportHeight) const +AnnotationText::getFontSizeForDrawing(const int32_t drawingViewportWidth, + const int32_t drawingViewportHeight) const { float sizeForDrawing = AnnotationTextFontPointSizeEnum::toSizeNumeric(AnnotationTextFontPointSizeEnum::SIZE14); + /* + * Minimum pixel size for text that is sized as a percent of height (tab/window). + * Note that some characters in a font may cause an OpenGL error and this + * error may unique to the underlying OpenGL implementation. + */ + const float minimumPixelSizeForPercentageText = 1.0; + switch (m_fontSizeType) { + case AnnotationTextFontSizeTypeEnum::POINTS: + sizeForDrawing = AnnotationTextFontPointSizeEnum::toSizeNumeric(m_fontPointSize); + if (sizeForDrawing < AnnotationTextFontPointSizeEnum::getMinimumSizeNumeric()) { + sizeForDrawing = AnnotationTextFontPointSizeEnum::getMinimumSizeNumeric(); + } + break; case AnnotationTextFontSizeTypeEnum::PERCENTAGE_OF_VIEWPORT_HEIGHT: { /* @@ -380,16 +447,25 @@ AnnotationText::getFontSizeForDrawing(const int32_t drawingViewportHeight) const */ const float pixelSize = drawingViewportHeight * (m_fontPercentViewportSize / 100.0); sizeForDrawing = pixelSize; + if (sizeForDrawing < minimumPixelSizeForPercentageText) { + sizeForDrawing = minimumPixelSizeForPercentageText; + } } break; - case AnnotationTextFontSizeTypeEnum::POINTS: - sizeForDrawing = AnnotationTextFontPointSizeEnum::toSizeNumeric(m_fontPointSize); + case AnnotationTextFontSizeTypeEnum::PERCENTAGE_OF_VIEWPORT_WIDTH: + { + /* + * May need pixel to points conversion if not 72 DPI + */ + const float pixelSize = drawingViewportWidth * (m_fontPercentViewportSize / 100.0); + sizeForDrawing = pixelSize; + if (sizeForDrawing < minimumPixelSizeForPercentageText) { + sizeForDrawing = minimumPixelSizeForPercentageText; + } + } break; } - if (sizeForDrawing < AnnotationTextFontPointSizeEnum::getMinimumSizeNumeric()) { - sizeForDrawing = AnnotationTextFontPointSizeEnum::getMinimumSizeNumeric(); - } const int32_t sizeInt = static_cast(MathFunctions::round(sizeForDrawing)); return sizeInt; @@ -541,7 +617,7 @@ AnnotationText::getTextColorRGBA(float rgbaOut[4]) const case CaretColorEnum::TEAL: case CaretColorEnum::WHITE: case CaretColorEnum::YELLOW: - CaretColorEnum::toRGBFloat(m_colorText, + CaretColorEnum::toRGBAFloat(m_colorText, rgbaOut); rgbaOut[3] = 1.0; break; @@ -632,15 +708,6 @@ AnnotationText::setCustomTextColor(const uint8_t rgba[4]) } } -/** - * Are font styles (Bold, Italic, Underline) supported? - */ -bool -AnnotationText::isStylesSupported() const -{ - return true; -} - /** * @return * Is bold enabled ? @@ -701,19 +768,6 @@ AnnotationText::isUnderlineStyleEnabled() const return m_underlineEnabled; } -/** - * @return Is foreground line width supported? - * Most annotations support a foreground line width. - * Annotations that do not support a foreground line width - * must override this method and return a value of false. - */ -bool -AnnotationText::isLineWidthSupported() const -{ - return true; -} - - /** * Set underline enabled. * @@ -753,6 +807,7 @@ AnnotationText::copyHelperAnnotationText(const AnnotationText& obj) m_underlineEnabled = obj.m_underlineEnabled; m_connectToBrainordinate = obj.m_connectToBrainordinate; m_fontPercentViewportSize = obj.m_fontPercentViewportSize; + m_fontTooSmallWhenLastDrawnFlag = obj.m_fontTooSmallWhenLastDrawnFlag; } /** @@ -824,6 +879,24 @@ AnnotationText::setFontPercentViewportSizeProtected(const float fontPercentViewp } } +/** + * @return Is the font too small when it is last drawn + * that may cause an OpenGL error and, as a result, + * the text is not seen by the user. + */ +bool +AnnotationText::isFontTooSmallWhenLastDrawn() const +{ + return m_fontTooSmallWhenLastDrawnFlag; +} + +void +AnnotationText::setFontTooSmallWhenLastDrawn(const bool tooSmallFontFlag) const +{ + m_fontTooSmallWhenLastDrawnFlag = tooSmallFontFlag; +} + + /** * Apply a spatial modification to an annotation. * @@ -895,8 +968,11 @@ AnnotationText::saveSubClassDataToScene(const SceneAttributes* sceneAttributes, { AnnotationTwoDimensionalShape::saveSubClassDataToScene(sceneAttributes, sceneClass); - m_sceneAssistant->saveMembers(sceneAttributes, - sceneClass); + if (testProperty(Property::SCENE_CONTAINS_ATTRIBUTES)) { + sceneClass->addBoolean("hasAttributesFlag", true); + m_attributesAssistant->saveMembers(sceneAttributes, + sceneClass); + } } /** @@ -917,8 +993,18 @@ AnnotationText::restoreSubClassDataFromScene(const SceneAttributes* sceneAttribu { AnnotationTwoDimensionalShape::restoreSubClassDataFromScene(sceneAttributes, sceneClass); - m_sceneAssistant->restoreMembers(sceneAttributes, - sceneClass); + if (testProperty(Property::SCENE_CONTAINS_ATTRIBUTES)) { + /* + * This flag will tell us if the scene has attributes. + * If this test was not performed and attributes were not in scene + * members in the atttributes assistant would overwrite initialized + * values with invalid values. + */ + if (sceneClass->getBooleanValue("hasAttributesFlag")) { + m_attributesAssistant->restoreMembers(sceneAttributes, + sceneClass); + } + } } /** diff --git a/src/Annotations/AnnotationText.h b/src/Annotations/AnnotationText.h index 6b3688ccfa72f1d52c3d0f6c527f583b591b6feb..e546c9580084a5489cdafbe40183dd16a37e550f 100644 --- a/src/Annotations/AnnotationText.h +++ b/src/Annotations/AnnotationText.h @@ -36,13 +36,22 @@ namespace caret { class AnnotationText : public AnnotationTwoDimensionalShape, public AnnotationFontAttributesInterface { public: + /** + * @return The "too small" text size. + * + * When the text is smaller than this font height, either all of the + * text string may not be drawn or just particular characters + */ + static uint32_t getTooSmallTextHeight() { return 8; } + virtual ~AnnotationText(); AnnotationText(const AnnotationText& obj); AnnotationText& operator=(const AnnotationText& obj); - AString getFontRenderingEncodedName(const float drawingViewportHeight) const; + AString getFontRenderingEncodedName(const float drawingViewportWidth, + const float drawingViewportHeight) const; AString getText() const; @@ -68,7 +77,12 @@ namespace caret { void setOrientation(const AnnotationTextOrientationEnum::Enum orientation); - int32_t getFontSizeForDrawing(const int32_t drawingViewportHeight) const; + int32_t getFontSizeForDrawing(const int32_t drawingViewportWidth, + const int32_t drawingViewportHeight) const; + + bool isFontTooSmallWhenLastDrawn() const override; + + void setFontTooSmallWhenLastDrawn(const bool tooSmallFontFlag) const override; AnnotationTextFontSizeTypeEnum::Enum getFontSizeType() const; @@ -88,8 +102,6 @@ namespace caret { virtual void setCustomTextColor(const uint8_t rgba[4]); - virtual bool isStylesSupported() const; - virtual bool isBoldStyleEnabled() const; virtual void setBoldStyleEnabled(const bool enabled); @@ -108,8 +120,6 @@ namespace caret { bool isConnectToBrainordinateValid() const; - virtual bool isLineWidthSupported() const; - virtual bool applySpatialModification(const AnnotationSpatialModification& spatialModification); virtual void applyCoordinatesSizeAndRotationFromOther(const Annotation* otherAnnotation); @@ -156,6 +166,10 @@ namespace caret { AnnotationText(const AnnotationAttributesDefaultTypeEnum::Enum attributeDefaultType, const AnnotationTextFontSizeTypeEnum::Enum fontSizeType); + AnnotationText(const AnnotationTypeEnum::Enum type, + const AnnotationAttributesDefaultTypeEnum::Enum attributeDefaultType, + const AnnotationTextFontSizeTypeEnum::Enum fontSizeType); + virtual void saveSubClassDataToScene(const SceneAttributes* sceneAttributes, SceneClass* sceneClass); @@ -181,7 +195,7 @@ namespace caret { /* Not saved to scene since it is set by sub-class constructor. */ const AnnotationTextFontSizeTypeEnum::Enum m_fontSizeType; - CaretPointer m_sceneAssistant; + CaretPointer m_attributesAssistant; AString m_text; @@ -209,6 +223,8 @@ namespace caret { bool m_underlineEnabled; + mutable bool m_fontTooSmallWhenLastDrawnFlag = false; + // Defaults static AnnotationTextAlignHorizontalEnum::Enum s_userDefaultAlignmentHorizontal; @@ -241,9 +257,9 @@ namespace caret { }; #ifdef __ANNOTATION_TEXT_DECLARE__ - AnnotationTextAlignHorizontalEnum::Enum AnnotationText::s_userDefaultAlignmentHorizontal = AnnotationTextAlignHorizontalEnum::LEFT; + AnnotationTextAlignHorizontalEnum::Enum AnnotationText::s_userDefaultAlignmentHorizontal = AnnotationTextAlignHorizontalEnum::CENTER; - AnnotationTextAlignVerticalEnum::Enum AnnotationText::s_userDefaultAlignmentVertical = AnnotationTextAlignVerticalEnum::TOP; + AnnotationTextAlignVerticalEnum::Enum AnnotationText::s_userDefaultAlignmentVertical = AnnotationTextAlignVerticalEnum::MIDDLE; AnnotationTextFontNameEnum::Enum AnnotationText::s_userDefaultFont = AnnotationTextFontNameEnum::VERA; diff --git a/src/Annotations/AnnotationTextFontSizeTypeEnum.cxx b/src/Annotations/AnnotationTextFontSizeTypeEnum.cxx index 9c1b14c9a8a37a5a854d707f197226b4ed974ecc..7609791a0f4af762899ee62cd163a13e725cb190 100644 --- a/src/Annotations/AnnotationTextFontSizeTypeEnum.cxx +++ b/src/Annotations/AnnotationTextFontSizeTypeEnum.cxx @@ -31,9 +31,7 @@ using namespace caret; /** * \class caret::AnnotationTextFontSizeTypeEnum - * \brief - * - * + * \brief Enumerated type for font sizing * * Using this enumerated type in the GUI with an EnumComboBoxTemplate * @@ -110,12 +108,15 @@ AnnotationTextFontSizeTypeEnum::initialize() enumData.push_back(AnnotationTextFontSizeTypeEnum(POINTS, "POINTS", - "")); + "Points")); enumData.push_back(AnnotationTextFontSizeTypeEnum(PERCENTAGE_OF_VIEWPORT_HEIGHT, "PERCENTAGE_OF_VIEWPORT_HEIGHT", - "")); + "Percentage of Viewport Height")); + enumData.push_back(AnnotationTextFontSizeTypeEnum(PERCENTAGE_OF_VIEWPORT_WIDTH, + "PERCENTAGE_OF_VIEWPORT_WIDTH", + "Percentage of Viewport Width")); } /** diff --git a/src/Annotations/AnnotationTextFontSizeTypeEnum.h b/src/Annotations/AnnotationTextFontSizeTypeEnum.h index e9cd63d3822f661b9ddd94ac75263a2a37bedaa4..1cfccf494b34b124d3ce8c872a6d9f02b4780747 100644 --- a/src/Annotations/AnnotationTextFontSizeTypeEnum.h +++ b/src/Annotations/AnnotationTextFontSizeTypeEnum.h @@ -35,10 +35,12 @@ public: * Enumerated values. */ enum Enum { - /** */ + /** Sized in Points */ POINTS, - /** */ - PERCENTAGE_OF_VIEWPORT_HEIGHT + /** Sized as percentage of viewport height */ + PERCENTAGE_OF_VIEWPORT_HEIGHT, + /** Sized as percentage of viewport width */ + PERCENTAGE_OF_VIEWPORT_WIDTH }; diff --git a/src/Annotations/AnnotationTwoDimensionalShape.cxx b/src/Annotations/AnnotationTwoDimensionalShape.cxx index 70d0a0a0f0e441bf475a7a1a4018018053b7dbc7..3141b8563da519b539d6eef6fc836ca80ec631ae 100644 --- a/src/Annotations/AnnotationTwoDimensionalShape.cxx +++ b/src/Annotations/AnnotationTwoDimensionalShape.cxx @@ -135,7 +135,7 @@ AnnotationTwoDimensionalShape::initializeMembersAnnotationTwoDimensionalShape() m_sceneAssistant.grabNew(new SceneClassAssistant()); - if (getType() == AnnotationTypeEnum::COLOR_BAR) { + if (testProperty(Property::SCENE_CONTAINS_ATTRIBUTES)) { /* * Saves/restores color bar manual position and size mode. */ @@ -328,26 +328,21 @@ AnnotationTwoDimensionalShape::applyCoordinatesSizeAndRotationFromOther(const An bool AnnotationTwoDimensionalShape::isSizeHandleValid(const AnnotationSizingHandleTypeEnum::Enum sizingHandle) const { - bool pixelsFlag = false; - bool tabWindowFlag = false; - bool stereotaxicFlag = false; - bool surfaceFlag = false; + bool viewportFlag = false; switch (getCoordinateSpace()) { - case AnnotationCoordinateSpaceEnum::PIXELS: - pixelsFlag = true; + case AnnotationCoordinateSpaceEnum::CHART: break; case AnnotationCoordinateSpaceEnum::STEREOTAXIC: - stereotaxicFlag = true; break; case AnnotationCoordinateSpaceEnum::SURFACE: - surfaceFlag = true; break; case AnnotationCoordinateSpaceEnum::TAB: - tabWindowFlag = true; + break; + case AnnotationCoordinateSpaceEnum::VIEWPORT: + viewportFlag = true; break; case AnnotationCoordinateSpaceEnum::WINDOW: - tabWindowFlag = true; break; } @@ -390,7 +385,7 @@ AnnotationTwoDimensionalShape::isSizeHandleValid(const AnnotationSizingHandleTyp bool validFlag = false; - if ( ! pixelsFlag) { + if (! viewportFlag) { switch (sizingHandle) { case AnnotationSizingHandleTypeEnum::ANNOTATION_SIZING_HANDLE_BOX_BOTTOM: if (allowsSideResizingFlag) { @@ -452,6 +447,198 @@ AnnotationTwoDimensionalShape::isSizeHandleValid(const AnnotationSizingHandleTyp return validFlag; } +/** + * Apply a spatial modification to an annotation in chart space. + * + * @param spatialModification + * Contains information about the spatial modification. + * @return + * True if the annotation was modified, else false. + */ +bool +AnnotationTwoDimensionalShape::applySpatialModificationChartSpace(const AnnotationSpatialModification& spatialModification) +{ + bool validFlag = false; + + const float rotationRadians = MathFunctions::toRadians(m_rotationAngle); + float dx = (spatialModification.m_mouseDX * std::cos(rotationRadians) + - spatialModification.m_mouseDY * std::sin(rotationRadians)); + float dy = (spatialModification.m_mouseDX * std::sin(rotationRadians) + + spatialModification.m_mouseDY * std::cos(rotationRadians)); + + + if (spatialModification.m_viewportWidth > 0) { + dx = (dx / spatialModification.m_viewportWidth) * 100.0; + } + else { + dx = 0.0; + } + if (spatialModification.m_viewportHeight > 0) { + dy = (dy / spatialModification.m_viewportHeight) * 100.0; + } + else { + dy = 0.0; + } + + bool validDxyFlag = false; + + switch (spatialModification.m_sizingHandleType) { + case AnnotationSizingHandleTypeEnum::ANNOTATION_SIZING_HANDLE_BOX_BOTTOM: + dx = 0.0; + dy = -dy; + validDxyFlag = true; + break; + case AnnotationSizingHandleTypeEnum::ANNOTATION_SIZING_HANDLE_BOX_BOTTOM_LEFT: + dx = -dx; + dy = -dy; + validDxyFlag = true; + break; + case AnnotationSizingHandleTypeEnum::ANNOTATION_SIZING_HANDLE_BOX_BOTTOM_RIGHT: + dy = -dy; + validDxyFlag = true; + break; + case AnnotationSizingHandleTypeEnum::ANNOTATION_SIZING_HANDLE_BOX_LEFT: + dx = -dx; + dy = 0.0; + validDxyFlag = true; + break; + case AnnotationSizingHandleTypeEnum::ANNOTATION_SIZING_HANDLE_BOX_RIGHT: + dy = 0.0; + validDxyFlag = true; + break; + case AnnotationSizingHandleTypeEnum::ANNOTATION_SIZING_HANDLE_BOX_TOP: + dx = 0.0; + validDxyFlag = true; + break; + case AnnotationSizingHandleTypeEnum::ANNOTATION_SIZING_HANDLE_BOX_TOP_LEFT: + dx = -dx; + validDxyFlag = true; + break; + case AnnotationSizingHandleTypeEnum::ANNOTATION_SIZING_HANDLE_BOX_TOP_RIGHT: + validDxyFlag = true; + break; + case AnnotationSizingHandleTypeEnum::ANNOTATION_SIZING_HANDLE_LINE_END: + break; + case AnnotationSizingHandleTypeEnum::ANNOTATION_SIZING_HANDLE_LINE_START: + break; + case AnnotationSizingHandleTypeEnum::ANNOTATION_SIZING_HANDLE_NONE: + if (spatialModification.m_chartCoordAtMouseXY.m_chartXYZValid) { + m_coordinate->setXYZ(spatialModification.m_chartCoordAtMouseXY.m_chartXYZ); + validFlag = true; + } + break; + case AnnotationSizingHandleTypeEnum::ANNOTATION_SIZING_HANDLE_ROTATION: + { + static float centerX = 0.0; + static float centerY = 0.0; + + /* + * Stereotaxic and surface rotations may be displayed in + * multiple locations if more that one surface is dislayed + * when surface montage is viewed and/or tile tabs is enabled. + * + * We don't have the window location of the annotation but + * we can estimate it as it is in the center of the annotation. + * Once we have approximated the center we can use the center + * as the rotation point and so that the rotation handle points + * to the mouse. During rotation, the center will not change + * so set its position when the user starts to drag the mouse. + */ + if (spatialModification.m_startOfDraggingFlag) { + const float height = ((m_height / 100.0) * spatialModification.m_viewportHeight); + const float halfHeight = height / 2.0; + + const float handleOffsetHeight = 15.0; + const float centerAngleRadians = MathFunctions::toRadians(m_rotationAngle - 90); + const float centerOffsetY = (halfHeight + handleOffsetHeight) * std::sin(centerAngleRadians); + const float centerOffsetX = -halfHeight * std::cos(centerAngleRadians); + centerX = spatialModification.m_mousePressX + centerOffsetX; + centerY = spatialModification.m_mousePressY + centerOffsetY; + } + + /* + * Rotation angle is a formed by the triangle + * (Mouse XY, Annotation XY, Positive X-axis). + */ + const float dy = (spatialModification.m_mouseY + - centerY); + const float dx = (spatialModification.m_mouseX + - centerX); + + const float angleRadians = std::atan2(dy, dx); + const float angleDegrees = MathFunctions::toDegrees(angleRadians); + m_rotationAngle = 90.0 - angleDegrees; + if (m_rotationAngle > 360.0) { + m_rotationAngle -= 360.0; + } + else if (m_rotationAngle < 0.0) { + m_rotationAngle += 360.0; + } + + validFlag = true; + } + break; + } + + if (validDxyFlag) { + if (isFixedAspectRatio()) { + switch (spatialModification.m_sizingHandleType) { + case AnnotationSizingHandleTypeEnum::ANNOTATION_SIZING_HANDLE_BOX_BOTTOM: + case AnnotationSizingHandleTypeEnum::ANNOTATION_SIZING_HANDLE_BOX_LEFT: + case AnnotationSizingHandleTypeEnum::ANNOTATION_SIZING_HANDLE_BOX_RIGHT: + case AnnotationSizingHandleTypeEnum::ANNOTATION_SIZING_HANDLE_BOX_TOP: + if (std::fabs(dx) > std::fabs(dy)) { + /* + * For fixed aspect ratio, the width percentage is not a percentage + * of window width but of the window's height since the annotation + * is sized by its height (width = height / aspect). + * + * So when width is changed, need to set height. + */ + const float aspectRatio = getFixedAspectRatio(); + const float newHeight = MathFunctions::limitRange(getHeight() + dx * aspectRatio, 0.0f, 100.0f); + setHeight(newHeight); + validFlag = true; + } + else if (std::fabs(dx) < std::fabs(dy)) { + const float newHeight = MathFunctions::limitRange(getHeight() + dy, 0.0f, 100.0f); + setHeight(newHeight); + validFlag = true; + } + break; + case AnnotationSizingHandleTypeEnum::ANNOTATION_SIZING_HANDLE_BOX_TOP_LEFT: + case AnnotationSizingHandleTypeEnum::ANNOTATION_SIZING_HANDLE_BOX_BOTTOM_LEFT: + case AnnotationSizingHandleTypeEnum::ANNOTATION_SIZING_HANDLE_BOX_BOTTOM_RIGHT: + case AnnotationSizingHandleTypeEnum::ANNOTATION_SIZING_HANDLE_BOX_TOP_RIGHT: + break; + case AnnotationSizingHandleTypeEnum::ANNOTATION_SIZING_HANDLE_LINE_END: + case AnnotationSizingHandleTypeEnum::ANNOTATION_SIZING_HANDLE_LINE_START: + case AnnotationSizingHandleTypeEnum::ANNOTATION_SIZING_HANDLE_NONE: + case AnnotationSizingHandleTypeEnum::ANNOTATION_SIZING_HANDLE_ROTATION: + break; + } + } + else { + if (dx != 0.0) { + m_width += dx; + m_width = MathFunctions::limitRange(m_width, 1.0f, 100.0f); + validFlag = true; + } + if (dy != 0.0) { + m_height += dy; + m_height = MathFunctions::limitRange(m_height, 1.0f, 100.0f); + validFlag = true; + } + } + } + + if (validFlag) { + setModified(); + } + + return validFlag; +} + /** * Apply a spatial modification to an annotation in surface * or stereotaxic space. @@ -472,7 +659,7 @@ AnnotationTwoDimensionalShape::applySpatialModificationSurfaceOrStereotaxicSpace bool surfaceSpaceFlag = false; switch (coordinateSpace) { - case AnnotationCoordinateSpaceEnum::PIXELS: + case AnnotationCoordinateSpaceEnum::CHART: badSpaceFlag = true; break; case AnnotationCoordinateSpaceEnum::STEREOTAXIC: @@ -484,6 +671,9 @@ AnnotationTwoDimensionalShape::applySpatialModificationSurfaceOrStereotaxicSpace case AnnotationCoordinateSpaceEnum::TAB: badSpaceFlag = true; break; + case AnnotationCoordinateSpaceEnum::VIEWPORT: + badSpaceFlag = true; + break; case AnnotationCoordinateSpaceEnum::WINDOW: badSpaceFlag = true; break; @@ -991,11 +1181,7 @@ AnnotationTwoDimensionalShape::applySpatialModificationTabOrWindowSpace(const An if ((newX >= 0.0) && (newX <= 100.0) && (newY >= 0.0) - && (newY <= 100.0) - && (newWidth > 0.01) - && (newWidth <= 100.0) - && (newHeight > 0.01) - && (newHeight <= 100.0)) { + && (newY <= 100.0)) { if (isFixedAspectRatio()) { xyz[0] = newX; @@ -1111,7 +1297,8 @@ AnnotationTwoDimensionalShape::applySpatialModification(const AnnotationSpatialM const AnnotationCoordinateSpaceEnum::Enum space = getCoordinateSpace(); switch (space) { - case AnnotationCoordinateSpaceEnum::PIXELS: + case AnnotationCoordinateSpaceEnum::CHART: + return applySpatialModificationChartSpace(spatialModification); break; case AnnotationCoordinateSpaceEnum::STEREOTAXIC: return applySpatialModificationSurfaceOrStereotaxicSpace(spatialModification, @@ -1124,6 +1311,8 @@ AnnotationTwoDimensionalShape::applySpatialModification(const AnnotationSpatialM case AnnotationCoordinateSpaceEnum::TAB: return applySpatialModificationTabOrWindowSpace(spatialModification); break; + case AnnotationCoordinateSpaceEnum::VIEWPORT: + break; case AnnotationCoordinateSpaceEnum::WINDOW: return applySpatialModificationTabOrWindowSpace(spatialModification); break; diff --git a/src/Annotations/AnnotationTwoDimensionalShape.h b/src/Annotations/AnnotationTwoDimensionalShape.h index 5d93898f3bfd5e190c5888b83ed063088a64c0f6..9bc0117d922b3c1fa4a4b3ddb95e1afa4156e196 100644 --- a/src/Annotations/AnnotationTwoDimensionalShape.h +++ b/src/Annotations/AnnotationTwoDimensionalShape.h @@ -119,10 +119,10 @@ namespace caret { bool applySpatialModificationSurfaceOrStereotaxicSpace(const AnnotationSpatialModification& spatialModification, const AnnotationCoordinateSpaceEnum::Enum coordinateSpace); -// bool applySpatialModificationStereotaxicSpace(const AnnotationSpatialModification& spatialModification); - bool applySpatialModificationTabOrWindowSpace(const AnnotationSpatialModification& spatialModification); + bool applySpatialModificationChartSpace(const AnnotationSpatialModification& spatialModification); + bool rotationAngleTest(const float previousMouseXYZ[3], const float shapeXYZ[3], const float currentMouseXYZ[3]) const; diff --git a/src/Annotations/AnnotationTypeEnum.cxx b/src/Annotations/AnnotationTypeEnum.cxx index 8f800fc65aa25db716ef7cdec9347a7c8f0a3e47..1f316d52155681cb5ac1cf3a99668235ec1c0f87 100644 --- a/src/Annotations/AnnotationTypeEnum.cxx +++ b/src/Annotations/AnnotationTypeEnum.cxx @@ -113,7 +113,7 @@ AnnotationTypeEnum::initialize() enumData.push_back(AnnotationTypeEnum(COLOR_BAR, "COLOR_BAR", "Color Bar")); - + enumData.push_back(AnnotationTypeEnum(IMAGE, "IMAGE", "Image")); diff --git a/src/Annotations/CMakeLists.txt b/src/Annotations/CMakeLists.txt index 03d7f03b668c9b5f2c1ccc876c8b221201d47877..b2cbdc0c6b909cf3dc6d28d734ac9a69a59a075d 100644 --- a/src/Annotations/CMakeLists.txt +++ b/src/Annotations/CMakeLists.txt @@ -11,6 +11,12 @@ SET(QT_DONT_USE_QTGUI TRUE) # # QT include files # +if(Qt5_FOUND) + include_directories(${Qt5Core_INCLUDE_DIRS}) + #include_directories(${Qt5Network_INCLUDE_DIRS}) + include_directories(${Qt5Xml_INCLUDE_DIRS}) +endif() + ###INCLUDE(${QT_USE_FILE}) # @@ -56,6 +62,7 @@ AnnotationTextOrientationEnum.h AnnotationTwoDimensionalShape.h AnnotationTypeEnum.h EventAnnotationAddToRemoveFromFile.h +EventAnnotationChartLabelGet.h EventAnnotationGroupGetWithKey.h EventAnnotationGrouping.h @@ -97,15 +104,19 @@ AnnotationTextOrientationEnum.cxx AnnotationTwoDimensionalShape.cxx AnnotationTypeEnum.cxx EventAnnotationAddToRemoveFromFile.cxx +EventAnnotationChartLabelGet.cxx EventAnnotationGroupGetWithKey.cxx EventAnnotationGrouping.cxx ) +TARGET_LINK_LIBRARIES(Annotations ${CARET_QT5_LINK}) + # # Find headers # INCLUDE_DIRECTORIES( ${CMAKE_SOURCE_DIR}/Common +${CMAKE_SOURCE_DIR}/Graphics ${CMAKE_SOURCE_DIR}/Scenes ${CMAKE_SOURCE_DIR}/Nifti ) diff --git a/src/Annotations/EventAnnotationAddToRemoveFromFile.h b/src/Annotations/EventAnnotationAddToRemoveFromFile.h index 8219e1bc59fdbf0edf6a4345e7e524a03c573af8..d03719a148f25ee4ba93cf898b74c5cb197f1260 100644 --- a/src/Annotations/EventAnnotationAddToRemoveFromFile.h +++ b/src/Annotations/EventAnnotationAddToRemoveFromFile.h @@ -38,10 +38,12 @@ namespace caret { MODE_CREATE, MODE_CUT, MODE_DELETE, + MODE_DUPLICATE, MODE_PASTE, MODE_UNCREATE, MODE_UNCUT, MODE_UNDELETE, + MODE_UNDUPLICATE, MODE_UNPASTE }; diff --git a/src/Annotations/EventAnnotationChartLabelGet.cxx b/src/Annotations/EventAnnotationChartLabelGet.cxx new file mode 100644 index 0000000000000000000000000000000000000000..9ba0d42384904a1a5235afc8fb1aa8eb5deed33f --- /dev/null +++ b/src/Annotations/EventAnnotationChartLabelGet.cxx @@ -0,0 +1,74 @@ + +/*LICENSE_START*/ +/* + * Copyright (C) 2017 Washington University School of Medicine + * + * 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 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ +/*LICENSE_END*/ + +#define __EVENT_ANNOTATION_CHART_LABEL_GET_DECLARE__ +#include "EventAnnotationChartLabelGet.h" +#undef __EVENT_ANNOTATION_CHART_LABEL_GET_DECLARE__ + +#include "CaretAssert.h" +#include "EventTypeEnum.h" + +using namespace caret; + + + +/** + * \class caret::EventAnnotationChartLabelGet + * \brief Event to get annotation chart labels + * \ingroup Brain + */ + +/** + * Constructor. + */ +EventAnnotationChartLabelGet::EventAnnotationChartLabelGet() +: Event(EventTypeEnum::EVENT_ANNOTATION_CHART_LABEL_GET) +{ + +} + +/** + * Destructor. + */ +EventAnnotationChartLabelGet::~EventAnnotationChartLabelGet() +{ +} + +/** + * Add a chart label. + * + * @param annotationChartLabel + */ +void +EventAnnotationChartLabelGet::addAnnotationChartLabel(Annotation* annotationChartLabel) +{ + m_annotationChartLabels.push_back(annotationChartLabel); +} + +/** + * @return The annotation chart labels. + */ +std::vector +EventAnnotationChartLabelGet::getAnnotationChartLabels() +{ + return m_annotationChartLabels; +} + diff --git a/src/Annotations/EventAnnotationChartLabelGet.h b/src/Annotations/EventAnnotationChartLabelGet.h new file mode 100644 index 0000000000000000000000000000000000000000..16bda97775b3857a70a89e78edd9f8314bafa6a4 --- /dev/null +++ b/src/Annotations/EventAnnotationChartLabelGet.h @@ -0,0 +1,65 @@ +#ifndef __EVENT_ANNOTATION_CHART_LABEL_GET_H__ +#define __EVENT_ANNOTATION_CHART_LABEL_GET_H__ + +/*LICENSE_START*/ +/* + * Copyright (C) 2017 Washington University School of Medicine + * + * 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 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ +/*LICENSE_END*/ + + + +#include + +#include "Event.h" + + + +namespace caret { + + class Annotation; + + class EventAnnotationChartLabelGet : public Event { + + public: + EventAnnotationChartLabelGet(); + + virtual ~EventAnnotationChartLabelGet(); + + void addAnnotationChartLabel(Annotation* annotationChartLabel); + + std::vector getAnnotationChartLabels(); + + // ADD_NEW_METHODS_HERE + + private: + EventAnnotationChartLabelGet(const EventAnnotationChartLabelGet&); + + EventAnnotationChartLabelGet& operator=(const EventAnnotationChartLabelGet&); + + std::vector m_annotationChartLabels; + + // ADD_NEW_MEMBERS_HERE + + }; + +#ifdef __EVENT_ANNOTATION_CHART_LABEL_GET_DECLARE__ + // +#endif // __EVENT_ANNOTATION_CHART_LABEL_GET_DECLARE__ + +} // namespace +#endif //__EVENT_ANNOTATION_CHART_LABEL_GET_H__ diff --git a/src/Brain/AnnotationArrangerExecutor.cxx b/src/Brain/AnnotationArrangerExecutor.cxx index ebe2f9d9788c4f5a94c85392e7b092ebfb40c80d..210fc7ac06e0ea5a6af204d2690392e01a50c121 100644 --- a/src/Brain/AnnotationArrangerExecutor.cxx +++ b/src/Brain/AnnotationArrangerExecutor.cxx @@ -612,7 +612,7 @@ AnnotationArrangerExecutor::setupAnnotationInfo(const AnnotationArrangerInputs& */ int32_t annViewport[4] = { 0, 0, 0, 0 }; switch (annotation->getCoordinateSpace()) { - case AnnotationCoordinateSpaceEnum::PIXELS: + case AnnotationCoordinateSpaceEnum::CHART: CaretAssert(0); break; case AnnotationCoordinateSpaceEnum::STEREOTAXIC: @@ -625,6 +625,9 @@ AnnotationArrangerExecutor::setupAnnotationInfo(const AnnotationArrangerInputs& getTabViewport(annotation->getTabIndex(), annViewport); break; + case AnnotationCoordinateSpaceEnum::VIEWPORT: + CaretAssert(0); + break; case AnnotationCoordinateSpaceEnum::WINDOW: annViewport[0] = m_windowViewport[0]; annViewport[1] = m_windowViewport[1]; @@ -698,6 +701,7 @@ AnnotationArrangerExecutor::setupAnnotationInfo(const AnnotationArrangerInputs& viewportPixelOneXYZ[0], viewportPixelOneXYZ[1], viewportPixelOneXYZ[2], + annViewport[2], annViewport[3], bottomLeft, bottomRight, diff --git a/src/Brain/AnnotationManager.cxx b/src/Brain/AnnotationManager.cxx index a5750b361265c934f736b3d726f9eaa397c67ac2..46549ac3412f7965fb52457107104d9508d9d1a7 100644 --- a/src/Brain/AnnotationManager.cxx +++ b/src/Brain/AnnotationManager.cxx @@ -19,10 +19,6 @@ */ /*LICENSE_END*/ -//#include -//#include -//#include - #define __ANNOTATION_MANAGER_DECLARE__ #include "AnnotationManager.h" #undef __ANNOTATION_MANAGER_DECLARE__ @@ -41,6 +37,7 @@ #include "CaretLogger.h" #include "CaretUndoStack.h" #include "DisplayPropertiesAnnotation.h" +#include "EventAnnotationChartLabelGet.h" #include "EventAnnotationColorBarGet.h" #include "EventAnnotationGroupGetWithKey.h" #include "EventGetDisplayedDataFiles.h" @@ -202,53 +199,16 @@ AnnotationManager::deselectAllAnnotationsForEditing(const int32_t windowIndex) cb->setSelectedForEditing(windowIndex, false); } + + EventAnnotationChartLabelGet chartLabelEvent; + EventManager::get()->sendEvent(chartLabelEvent.getPointer()); + std::vector chartLabels = chartLabelEvent.getAnnotationChartLabels(); + for (auto label : chartLabels) { + label->setSelectedForEditing(windowIndex, + false); + } } -///** -// * Get the files containing the given annotations. If a file is not found -// * for an annotation NULL is selected for the file. -// * -// * @param annotations -// * Annotations for which file is found. -// * @return -// * Files containing the annotations (NULL entry if file not found). The -// * size of this vector will ALWAYS be the same as the size of the -// * input vector. -// */ -//std::vector -//AnnotationManager::getFilesContainingAnnotations(const std::vector annotations) const -//{ -// CaretAssertMessage(0, "Use annotation's getAnnotationFile() method."); -//// std::vector allFiles; -//// m_brain->getAllAnnotationFilesIncludingSceneAnnotationFile(allFiles); -// -// std::vector filesOut; -// -//// for (std::vector::const_iterator annIter = annotations.begin(); -//// annIter != annotations.end(); -//// annIter++) { -//// Annotation* ann = *annIter; -//// -//// AnnotationFile* file = NULL; -//// for (std::vector::const_iterator fileIter = allFiles.begin(); -//// fileIter != allFiles.end(); -//// fileIter++) { -//// AnnotationFile* annFile = *fileIter; -//// if (annFile->containsAnnotation(ann)) { -//// file = annFile; -//// break; -//// } -//// } -//// -//// filesOut.push_back(file); -//// } -// -// -// CaretAssert(filesOut.size() == annotations.size()); -// return filesOut; -//} - - /** * Select the given annotation for editing using the given mode. * @@ -436,7 +396,7 @@ AnnotationManager::isAnnotationSelectedForEditingDeletable(const int32_t windowI iter != selectedAnnotations.end(); iter++) { const Annotation* ann = *iter; - if ( ! ann->isDeletable()) { + if ( ! ann->testProperty(Annotation::Property::DELETION)) { selectedAnnotationsDeletableFlag = false; break; } @@ -483,6 +443,13 @@ AnnotationManager::getAllAnnotations() const allAnnotations.push_back(*iter); } + EventAnnotationChartLabelGet chartLabelEvent; + EventManager::get()->sendEvent(chartLabelEvent.getPointer()); + std::vector chartLabels = chartLabelEvent.getAnnotationChartLabels(); + allAnnotations.insert(allAnnotations.end(), + chartLabels.begin(), + chartLabels.end()); + return allAnnotations; } @@ -604,6 +571,34 @@ AnnotationManager::getAnnotationsSelectedForEditing(const int32_t windowIndex, } } } + +} + +/** + * Get the selected annotations for editing and the files that contain them. + * INCLUDES chart labels. + * + * @param windowIndex + * Index of window for annotation selection. + * @param annotationsAndFileOut + * A 'pair' containing a selected annotation and the file that contains the annotation. + */ +void +AnnotationManager::getAnnotationsSelectedForEditingIncludingLabels(const int32_t windowIndex, + std::vector >& annotationsAndFileOut) const +{ + getAnnotationsSelectedForEditing(windowIndex, + annotationsAndFileOut); + + EventAnnotationChartLabelGet chartLabelEvent; + EventManager::get()->sendEvent(chartLabelEvent.getPointer()); + std::vector chartLabels = chartLabelEvent.getAnnotationChartLabels(); + AnnotationFile* nullFile = NULL; + for (auto cl : chartLabels) { + if (cl->isSelectedForEditing(windowIndex)) { + annotationsAndFileOut.push_back(std::make_pair(cl, nullFile)); + } + } } /** @@ -916,7 +911,6 @@ AnnotationManager::getDisplayedAnnotationFiles(EventGetDisplayedDataFiles* displ const std::vector tabIndices = displayedFilesEvent->getTabIndices(); -// const DisplayPropertiesAnnotation* annProps = m_brain->getDisplayPropertiesAnnotation(); std::vector annotationFiles; m_brain->getAllAnnotationFilesIncludingSceneAnnotationFile(annotationFiles); @@ -935,17 +929,20 @@ AnnotationManager::getDisplayedAnnotationFiles(EventGetDisplayedDataFiles* displ bool displayedFlag = false; switch (ann->getCoordinateSpace()) { - case AnnotationCoordinateSpaceEnum::PIXELS: + case AnnotationCoordinateSpaceEnum::CHART: + displayedFlag = true; break; case AnnotationCoordinateSpaceEnum::STEREOTAXIC: displayedFlag = true; break; case AnnotationCoordinateSpaceEnum::SURFACE: - displayedFlag = true; + displayedFlag = true; break; case AnnotationCoordinateSpaceEnum::TAB: displayedFlag = true; break; + case AnnotationCoordinateSpaceEnum::VIEWPORT: + break; case AnnotationCoordinateSpaceEnum::WINDOW: displayedFlag = true; break; diff --git a/src/Brain/AnnotationManager.h b/src/Brain/AnnotationManager.h index 33b4da75977560cef2cd3f8e385257bc5dc0a918..779aeba83d4534b9156b449ff4200c5fc44eb460 100644 --- a/src/Brain/AnnotationManager.h +++ b/src/Brain/AnnotationManager.h @@ -103,7 +103,8 @@ namespace caret { void getAnnotationsSelectedForEditing(const int32_t windowIndex, std::vector >& annotationsAndFileOut) const; -// std::vector getFilesContainingAnnotations(const std::vector annotations) const; + void getAnnotationsSelectedForEditingIncludingLabels(const int32_t windowIndex, + std::vector >& annotationsAndFileOut) const; bool isAnnotationOnClipboardValid() const; diff --git a/src/Brain/BorderDrawingTypeEnum.cxx b/src/Brain/BorderDrawingTypeEnum.cxx index 429316665d831234f3fcc6afdbf014562cb7e078..71f3ca3b0fa523e4b02d79eb3b5243606fdb987b 100644 --- a/src/Brain/BorderDrawingTypeEnum.cxx +++ b/src/Brain/BorderDrawingTypeEnum.cxx @@ -31,10 +31,9 @@ using namespace caret; /** * \class caret::BorderDrawingTypeEnum - * \brief - * - * + * \brief Enumerated type for border drawing */ + /** * Constructor. * @@ -78,7 +77,11 @@ BorderDrawingTypeEnum::initialize() "DRAW_AS_LINES", "Lines")); - enumData.push_back(BorderDrawingTypeEnum(DRAW_AS_POINTS_SPHERES, + enumData.push_back(BorderDrawingTypeEnum(DRAW_AS_POLYLINES, + "DRAW_AS_POLYLINES", + "Polylines")); + + enumData.push_back(BorderDrawingTypeEnum(DRAW_AS_POINTS_SPHERES, "DRAW_AS_POINTS_SPHERES", "Spheres")); diff --git a/src/Brain/BorderDrawingTypeEnum.h b/src/Brain/BorderDrawingTypeEnum.h index c5d56532249a7438b43bae830c116c2949156dfc..884f139fc02c33544787655e0cd060f0897571f5 100644 --- a/src/Brain/BorderDrawingTypeEnum.h +++ b/src/Brain/BorderDrawingTypeEnum.h @@ -37,6 +37,8 @@ public: enum Enum { /** draw as lines */ DRAW_AS_LINES, + /** draw as lines but thicker and slower */ + DRAW_AS_POLYLINES, /** draw as spherical points */ DRAW_AS_POINTS_SPHERES, /** draw as square points */ diff --git a/src/Brain/Brain.cxx b/src/Brain/Brain.cxx index e48cd3447274d7c6e5aa766dab96a5ad77a96cf7..5b4882918d6d3c49ce6194007e048ea81a384bab 100644 --- a/src/Brain/Brain.cxx +++ b/src/Brain/Brain.cxx @@ -38,6 +38,8 @@ #include "CaretLogger.h" #include "CaretPreferences.h" #include "ChartingDataManager.h" +#include "ChartableTwoFileDelegate.h" +#include "ChartableTwoFileMatrixChart.h" #include "ChartableLineSeriesBrainordinateInterface.h" #include "CiftiBrainordinateDataSeriesFile.h" #include "CiftiBrainordinateLabelFile.h" @@ -69,10 +71,12 @@ #include "EventDataFileDelete.h" #include "EventDataFileRead.h" #include "EventDataFileReload.h" +#include "EventCaretDataFilesGet.h" #include "EventGetDisplayedDataFiles.h" #include "EventModelAdd.h" #include "EventModelDelete.h" #include "EventModelGetAll.h" +#include "EventModelGetAllDisplayed.h" #include "EventPaletteGetByName.h" #include "EventProgressUpdate.h" #include "EventSpecFileReadDataFiles.h" @@ -87,6 +91,7 @@ #include "MathFunctions.h" #include "MetricFile.h" #include "ModelChart.h" +#include "ModelChartTwo.h" #include "ModelSurface.h" #include "ModelSurfaceMontage.h" #include "ModelVolume.h" @@ -118,8 +123,11 @@ using namespace caret; /** * Constructor. + * + * @param caretPreferences + * The caret preferencers used to initialize some components. */ -Brain::Brain() +Brain::Brain(const CaretPreferences* caretPreferences) { m_annotationManager = new AnnotationManager(this); @@ -139,6 +147,7 @@ Brain::Brain() m_sceneAnnotationFile->clearModified(); m_modelChart = NULL; + m_modelChartTwo = NULL; m_surfaceMontageModel = NULL; m_volumeSliceModel = NULL; m_wholeBrainModel = NULL; @@ -167,6 +176,8 @@ Brain::Brain() m_displayPropertiesVolume = new DisplayPropertiesVolume(); m_displayProperties.push_back(m_displayPropertiesVolume); + EventManager::get()->addEventListener(this, + EventTypeEnum::EVENT_CARET_DATA_FILES_GET); EventManager::get()->addEventListener(this, EventTypeEnum::EVENT_DATA_FILE_ADD); EventManager::get()->addEventListener(this, @@ -231,7 +242,7 @@ Brain::Brain() m_selectionManager = new SelectionManager(); - m_identificationManager = new IdentificationManager(); + m_identificationManager = new IdentificationManager(caretPreferences); m_brainordinateHighlightRegionOfInterest = new BrainordinateRegionOfInterest(); @@ -265,6 +276,9 @@ Brain::~Brain() if (m_modelChart != NULL) { delete m_modelChart; } + if (m_modelChartTwo != NULL) { + delete m_modelChartTwo; + } if (m_surfaceMontageModel != NULL) { delete m_surfaceMontageModel; } @@ -370,6 +384,30 @@ Brain::getBrainStructure(StructureEnum::Enum structure, return NULL; } +/** + * Update brain structures by removing any that contain zero data files + */ +void +Brain::updateBrainStructures() +{ + std::vector validBrainStructures; + + for (auto bs : m_brainStructures) { + CaretAssert(bs); + if (bs->hasDataFiles()) { + validBrainStructures.push_back(bs); + } + else { + delete bs; + } + } + + if (m_brainStructures.size() != validBrainStructures.size()) { + m_brainStructures = validBrainStructures; + } +} + + /** * Increment and return the duplicate counter for the given data file type. * @@ -656,6 +694,9 @@ Brain::resetBrain(const ResetBrainKeepSceneFiles keepSceneFiles, if (m_modelChart != NULL) { m_modelChart->reset(); } + if (m_modelChartTwo != NULL) { + m_modelChartTwo->reset(); + } m_gapsAndMargins->reset(); @@ -697,6 +738,29 @@ Brain::resetBrainKeepSceneFiles() if (caretDataFile->isModified()) { continue; } + else { + /* + * If a palette is modified by user or by showing a previous scene, + * then the file MUST be reloaded because the palette may be different + * in the new scene that is being loaded. + */ + CaretMappableDataFile* cmdf = dynamic_cast(caretDataFile); + if (cmdf != NULL) { + switch (cmdf->getPaletteColorMappingModifiedStatus()) { + case PaletteModifiedStatusEnum::MODIFIED: + continue; + break; + case PaletteModifiedStatusEnum::MODIFIED_BY_SHOW_SCENE: + continue; + break; + case PaletteModifiedStatusEnum::UNMODIFIED: + break; + } + } + } + if (caretDataFile->isModifiedSinceTimeOfLastReadOrWrite()) { + continue; + } const DataFileTypeEnum::Enum dataFileType = caretDataFile->getDataFileType(); @@ -1459,7 +1523,7 @@ Brain::addReadOrReloadVolumeFile(const FileModeAddReadReload fileMode, ElapsedTimer timer; timer.start(); - vf->updateScalarColoringForAllMaps(m_paletteFile); + vf->updateScalarColoringForAllMaps(); CaretLogInfo("Time to color volume data is " + AString::number(timer.getElapsedTimeSeconds(), 'f', 3) + " seconds."); @@ -3421,7 +3485,7 @@ Brain::getAllChartableLineSeriesDataFiles(std::vector& chartableDataFilesOut) const { chartableDataFilesOut.clear(); @@ -3518,7 +3582,7 @@ Brain::getAllChartableMatrixDataFiles(std::vector& ch * Contains all chartable data files upon exit. */ void -Brain::getAllChartableMatrixDataFilesForChartDataType(const ChartDataTypeEnum::Enum chartDataType, +Brain::getAllChartableMatrixDataFilesForChartDataType(const ChartOneDataTypeEnum::Enum chartDataType, std::vector& chartableDataFilesOut) const { chartableDataFilesOut.clear(); @@ -4684,12 +4748,61 @@ Brain::updateChartModel() m_modelChart = NULL; } } + + /* + * For Chart Two, test for any files that report a valid charting data type + * and then update (add/remove charting two model) + */ + EventCaretMappableDataFilesGet allMapFilesEvent; + EventManager::get()->sendEvent(allMapFilesEvent.getPointer()); + std::vector allMapFiles; + allMapFilesEvent.getAllFiles(allMapFiles); + + bool haveChartableFileFlag = false; + for (auto mapFile : allMapFiles) { + CaretAssert(mapFile); + std::vector chartDataTypes; + mapFile->getChartingDelegate()->getSupportedChartTwoDataTypes(chartDataTypes); + if ( ! chartDataTypes.empty()) { + haveChartableFileFlag = true; + break; + } + } + + if (haveChartableFileFlag) { + if (m_modelChartTwo == NULL) { + createModelChartTwo(); + } + } + else { + if (m_modelChartTwo != NULL) { + EventModelDelete eventDeleteModel(m_modelChartTwo); + EventManager::get()->sendEvent(eventDeleteModel.getPointer()); + delete m_modelChartTwo; + m_modelChartTwo = NULL; + } + } +} + +/** + * Create the model chart two. + */ +void +Brain::createModelChartTwo() +{ + m_modelChartTwo = new ModelChartTwo(this); + EventModelAdd eventAddModel(m_modelChartTwo); + EventManager::get()->sendEvent(eventAddModel.getPointer()); + + if (m_isSpecFileBeingRead == false) { + m_modelChartTwo->initializeOverlays(); + } } /** * Update the volume slice model. */ -void +void Brain::updateVolumeSliceModel() { bool isValid = false; @@ -5149,6 +5262,8 @@ Brain::addReadOrReloadDataFile(const FileModeAddReadReload fileMode, throw dfe; } + loadMatrixChartingFileDefaultRowOrColumn(caretDataFileRead); + updateAfterFilesAddedOrRemoved(); return caretDataFileRead; @@ -5195,7 +5310,7 @@ Brain::readDataFile(const DataFileTypeEnum::Enum dataFileType, FileInformation fileInfoFullPath(dataFileName); if (fileInfoFullPath.exists() == false) { throw DataFileException(dataFileName, - "File does not exist!"); + "File not found:"); } } @@ -5215,10 +5330,11 @@ Brain::readDataFile(const DataFileTypeEnum::Enum dataFileType, void Brain::updateAfterFilesAddedOrRemoved() { - updateChartModel(); + updateBrainStructures(); + updateSurfaceMontageModel(); updateVolumeSliceModel(); updateWholeBrainModel(); - updateSurfaceMontageModel(); + updateChartModel(); updateFiberTrajectoryMatchingFiberOrientationFiles(); } @@ -5768,6 +5884,15 @@ Brain::receiveEvent(Event* event) processReloadDataFileEvent(reloadDataFileEvent); } } + else if (event->getEventType() == EventTypeEnum::EVENT_CARET_DATA_FILES_GET) { + EventCaretDataFilesGet* filesEvent = dynamic_cast(event); + CaretAssert(filesEvent); + + std::vector caretDataFiles; + getAllDataFiles(caretDataFiles); + filesEvent->addAllCaretDataFiles(caretDataFiles); + filesEvent->setEventProcessed(); + } else if (event->getEventType() == EventTypeEnum::EVENT_CARET_MAPPABLE_DATA_FILES_GET) { EventCaretMappableDataFilesGet* dataFilesEvent = dynamic_cast(event); @@ -5900,6 +6025,24 @@ Brain::getChartModel() const return m_modelChart; } +/** + * @return The chart two model (warning may be NULL!) + */ +ModelChartTwo* +Brain::getChartTwoModel() +{ + return m_modelChartTwo; +} + +/** + * @return The chart two model (warning may be NULL!) + */ +const ModelChartTwo* +Brain::getChartTwoModel() const +{ + return m_modelChartTwo; +} + /** * @return The annotation manager. */ @@ -6480,7 +6623,10 @@ Brain::removeWithoutDeleteDataFile(const CaretDataFile* caretDataFile) const bool wasRemoved = removeWithoutDeleteDataFilePrivate(caretDataFile); if (wasRemoved) { - m_specFile->removeCaretDataFile(caretDataFile); + /* Scene annotation file is NOT in the spec file */ + if (caretDataFile != m_sceneAnnotationFile) { + m_specFile->removeCaretDataFile(caretDataFile); + } updateAfterFilesAddedOrRemoved(); } @@ -6522,6 +6668,12 @@ Brain::removeWithoutDeleteDataFilePrivate(const CaretDataFile* caretDataFile) return true; } + if (caretDataFile == m_sceneAnnotationFile) { + m_sceneAnnotationFile->clear(); + m_sceneAnnotationFile->clearModified(); + return true; + } + std::vector::iterator borderIterator = std::find(m_borderFiles.begin(), m_borderFiles.end(), caretDataFile); @@ -6694,7 +6846,14 @@ bool Brain::removeAndDeleteDataFile(CaretDataFile* caretDataFile) { if (removeWithoutDeleteDataFile(caretDataFile)) { - delete caretDataFile; + /* NEVER delete scene annotation file */ + if (caretDataFile == m_sceneAnnotationFile) { + m_sceneAnnotationFile->clear(); + m_sceneAnnotationFile->clearModified(); + } + else { + delete caretDataFile; + } return true; } @@ -6927,16 +7086,13 @@ Brain::saveToScene(const SceneAttributes* sceneAttributes, m_sceneAnnotationFile->clearModified(); /* - * Save all models + * Save all DISPLAYED models */ std::vector modelClassVector; - EventModelGetAll getAllModels; + EventModelGetAllDisplayed getAllModels; EventManager::get()->sendEvent(getAllModels.getPointer()); - std::vector allModels = getAllModels.getModels(); - for (std::vector::iterator iter = allModels.begin(); - iter != allModels.end(); - iter++) { - Model* mdc = *iter; + std::set allModels = getAllModels.getModels(); + for (auto mdc : allModels) { modelClassVector.push_back(mdc->saveToScene(sceneAttributes, "models")); } @@ -7097,6 +7253,8 @@ Brain::restoreFromScene(const SceneAttributes* sceneAttributes, caretDataFile->restoreFromScene(sceneAttributes, bestMatchingSceneClass); } + + loadMatrixChartingFileDefaultRowOrColumn(caretDataFile); } } @@ -7128,10 +7286,17 @@ Brain::restoreFromScene(const SceneAttributes* sceneAttributes, CiftiMappableConnectivityMatrixDataFile* cmf = *iter; if (cmf->isEmpty() == false) { const int32_t mapIndex = 0; - cmf->updateScalarColoringForMap(mapIndex, - getPaletteFile()); + cmf->updateScalarColoringForMap(mapIndex); } } + + /* + * Need to color all volume files since a thresholded volume may + * not get loaded until after its "thresholdee" + */ + for (auto vf : m_volumeFiles) { + vf->updateScalarColoringForAllMaps(); + } /* * Restore all models @@ -7139,6 +7304,9 @@ Brain::restoreFromScene(const SceneAttributes* sceneAttributes, EventModelGetAll getAllModels; EventManager::get()->sendEvent(getAllModels.getPointer()); std::vector allModels = getAllModels.getModels(); + for (auto model : allModels) { + model->setRestoredFromScene(false); + } const SceneClassArray* modelsClassArray = sceneClass->getClassArray("models"); const int32_t numModelClasses = modelsClassArray->getNumberOfArrayElements(); for (int32_t i = 0; i < numModelClasses; i++) { @@ -7150,7 +7318,7 @@ Brain::restoreFromScene(const SceneAttributes* sceneAttributes, iter != allModels.end(); iter++) { Model* mdc = *iter; - mdc->restoreFromScene(sceneAttributes, + mdc->restoreFromScene(sceneAttributes, modelsClassArray->getClassAtIndex(i)); } } @@ -7218,6 +7386,54 @@ Brain::restoreFromScene(const SceneAttributes* sceneAttributes, m_sceneAnnotationFile->clearModified(); } +/** + * Load the default row/column for a matrix charting file. + * If the file is not matrix chartable, no action is taken. + * + * @param caretDataFile + * The Caret Data File. + */ +void +Brain::loadMatrixChartingFileDefaultRowOrColumn(CaretDataFile* caretDataFile) +{ + if (caretDataFile == NULL) { + return; + } + + CaretMappableDataFile* cmdf = dynamic_cast(caretDataFile); + if (cmdf != NULL) { + cmdf->getChartingDelegate()->getMatrixCharting()->loadDefaultRowOrColumn(); + } +} + + +/** + * If model one charts are in scene but not + * model two charts, attempt to restore model + * one charts to model two charts. + * + * This must be performed AFTER browser tabs + * have been restored by the SessionManager. + */ +void +Brain::restoreModelChartOneToModelChartTwo() +{ + if (m_modelChartTwo != NULL) { + if (m_modelChartTwo->isRestoredFromScene()) { + return; + } + } + + if (m_modelChart != NULL) { + if (m_modelChart->isRestoredFromScene()) { + if (m_modelChartTwo == NULL) { + createModelChartTwo(); + } + m_modelChartTwo->restoreSceneFromChartOneModel(m_modelChart); + } + } +} + /** * @return The gaps and margins. */ diff --git a/src/Brain/Brain.h b/src/Brain/Brain.h index 742f08dcbabf1991ed55bb92fb5a20dd3b856c35..e0e839aced96d8bfeb5c20334c3eff2b12974231 100644 --- a/src/Brain/Brain.h +++ b/src/Brain/Brain.h @@ -25,7 +25,7 @@ #include #include "CaretObject.h" -#include "ChartDataTypeEnum.h" +#include "ChartOneDataTypeEnum.h" #include "DataFileTypeEnum.h" #include "DisplayGroupEnum.h" #include "EventListenerInterface.h" @@ -47,6 +47,7 @@ namespace caret { class BrainStructure; class CaretDataFile; class CaretMappableDataFile; + class CaretPreferences; class ChartingDataManager; class ChartableLineSeriesBrainordinateInterface; class ChartableMatrixInterface; @@ -84,6 +85,7 @@ namespace caret { class LabelFile; class MetricFile; class ModelChart; + class ModelChartTwo; class ModelSurfaceMontage; class ModelVolume; class ModelWholeBrain; @@ -101,7 +103,7 @@ namespace caret { class Brain : public CaretObject, public EventListenerInterface, public SceneableInterface { public: - Brain(); + Brain(const CaretPreferences* caretPreferences); ~Brain(); @@ -195,6 +197,10 @@ namespace caret { const ModelChart* getChartModel() const; + ModelChartTwo* getChartTwoModel(); + + const ModelChartTwo* getChartTwoModel() const; + ChartingDataManager* getChartingDataManager(); const ChartingDataManager* getChartingDataManager() const; @@ -321,14 +327,14 @@ namespace caret { void getAllChartableLineSeriesDataFiles(std::vector& chartableDataFilesOut) const; - void getAllChartableLineSeriesDataFilesForChartDataType(const ChartDataTypeEnum::Enum chartDataType, + void getAllChartableLineSeriesDataFilesForChartDataType(const ChartOneDataTypeEnum::Enum chartDataType, std::vector& chartableDataFilesOut) const; void getAllChartableBrainordinateDataFilesWithChartingEnabled(std::vector& chartableDataFilesOut) const; void getAllChartableMatrixDataFiles(std::vector& chartableDataFilesOut) const; - void getAllChartableMatrixDataFilesForChartDataType(const ChartDataTypeEnum::Enum chartDataType, + void getAllChartableMatrixDataFilesForChartDataType(const ChartOneDataTypeEnum::Enum chartDataType, std::vector& chartableDataFilesOut) const; AString getCurrentDirectory() const; @@ -402,6 +408,10 @@ namespace caret { virtual void restoreFromScene(const SceneAttributes* sceneAttributes, const SceneClass* sceneClass); + void restoreModelChartOneToModelChartTwo(); + + void loadMatrixChartingFileDefaultRowOrColumn(CaretDataFile* caretDataFile); + IdentificationManager* getIdentificationManager(); SelectionManager* getSelectionManager(); @@ -478,6 +488,8 @@ namespace caret { const AString& dataFileName, const bool markDataFileAsModified); + void createModelChartTwo(); + /** * Is the data file with the given name already loaded? * @@ -691,6 +703,8 @@ namespace caret { void updateSurfaceMontageModel(); + void updateBrainStructures(); + void updateFiberTrajectoryMatchingFiberOrientationFiles(); void validateCiftiMappableDataFile(const CiftiMappableDataFile* ciftiMapFile) const; @@ -751,6 +765,8 @@ namespace caret { ModelChart* m_modelChart; + ModelChartTwo* m_modelChartTwo; + ModelVolume* m_volumeSliceModel; ModelWholeBrain* m_wholeBrainModel; diff --git a/src/Brain/BrainOpenGL.cxx b/src/Brain/BrainOpenGL.cxx index 3843f7179fd79ba8763316a002b00888cdeb6f63..8ba370ccc2f315a26c81bd500d6801a259f88eb5 100644 --- a/src/Brain/BrainOpenGL.cxx +++ b/src/Brain/BrainOpenGL.cxx @@ -32,6 +32,17 @@ #include "CaretLogger.h" #include "CaretPreferences.h" #include "DummyFontTextRenderer.h" +#include "EventGetBrainOpenGLTextRenderer.h" +#include "EventGraphicsOpenGLCreateBufferObject.h" +#include "EventGraphicsOpenGLCreateTextureName.h" +#include "EventGraphicsOpenGLDeleteBufferObject.h" +#include "EventGraphicsOpenGLDeleteTextureName.h" +#include "EventOpenGLObjectToWindowTransform.h" +#include "EventManager.h" +#include "GraphicsOpenGLBufferObject.h" +#include "GraphicsOpenGLError.h" +#include "GraphicsOpenGLTextureName.h" +#include "GraphicsUtilitiesOpenGL.h" #include "Model.h" #include "SessionManager.h" @@ -52,6 +63,13 @@ BrainOpenGL::BrainOpenGL(BrainOpenGLTextRenderInterface* textRenderer) m_textRenderer = textRenderer; this->borderBeingDrawn = NULL; m_drawHighlightedEndPoints = false; + + EventManager::get()->addEventListener(this, EventTypeEnum::EVENT_GET_TEXT_RENDERER_FOR_WINDOW); + EventManager::get()->addEventListener(this, EventTypeEnum::EVENT_GRAPHICS_OPENGL_CREATE_BUFFER_OBJECT); + EventManager::get()->addEventListener(this, EventTypeEnum::EVENT_GRAPHICS_OPENGL_CREATE_TEXTURE_NAME); + EventManager::get()->addEventListener(this, EventTypeEnum::EVENT_GRAPHICS_OPENGL_DELETE_BUFFER_OBJECT); + EventManager::get()->addEventListener(this, EventTypeEnum::EVENT_GRAPHICS_OPENGL_DELETE_TEXTURE_NAME); + EventManager::get()->addEventListener(this, EventTypeEnum::EVENT_OPENGL_OBJECT_TO_WINDOW_TRANSFORM); } /** @@ -59,12 +77,330 @@ BrainOpenGL::BrainOpenGL(BrainOpenGLTextRenderInterface* textRenderer) */ BrainOpenGL::~BrainOpenGL() { + EventManager::get()->removeAllEventsFromListener(this); + if (m_textRenderer != NULL) { delete m_textRenderer; m_textRenderer = NULL; } } +/** + * Receive an event. + * + * @param event + * An event for which this instance is listening. + */ +void +BrainOpenGL::receiveEvent(Event* event) +{ + if (event->getEventType() == EventTypeEnum::EVENT_GRAPHICS_OPENGL_CREATE_BUFFER_OBJECT) { + EventGraphicsOpenGLCreateBufferObject* createBufferEvent + = dynamic_cast(event); + CaretAssert(createBufferEvent); + + if (m_contextSharingGroupPointer == NULL) { + AString msg("PROGRAM ERROR: " + "A request for a new OpenGL Buffer Object has been made. " + "However, there is no OpenGL context currently active so " + "a buffer cannot be created. This is an error in the " + "code and it is likely that the software will crash."); + createBufferEvent->setErrorMessage(msg); + createBufferEvent->setOpenGLBufferObject(NULL); + CaretAssertMessage(0, msg); + CaretLogSevere(msg); + return; + } + + GLuint bufferName = 0; + glGenBuffers(1, &bufferName); + + createBufferEvent->setOpenGLBufferObject(new GraphicsOpenGLBufferObject(m_contextSharingGroupPointer, + bufferName)); + createBufferEvent->setEventProcessed(); + } + else if (event->getEventType() == EventTypeEnum::EVENT_GRAPHICS_OPENGL_CREATE_TEXTURE_NAME) { + EventGraphicsOpenGLCreateTextureName* createTextureEvent + = dynamic_cast(event); + CaretAssert(createTextureEvent); + + if (m_contextSharingGroupPointer == NULL) { + AString msg("PROGRAM ERROR: " + "A request for a new OpenGL Texture Name has been made. " + "However, there is no OpenGL context currently active so " + "a texture cannot be created. This is an error in the " + "code and it is likely that the software will crash."); + createTextureEvent->setErrorMessage(msg); + createTextureEvent->setOpenGLTextureName(NULL); + CaretAssertMessage(0, msg); + CaretLogSevere(msg); + return; + } + + GLuint textureName = 0; + glGenTextures(1, &textureName); + + createTextureEvent->setOpenGLTextureName(new GraphicsOpenGLTextureName(m_contextSharingGroupPointer, + textureName)); + createTextureEvent->setEventProcessed(); + } + else if (event->getEventType() == EventTypeEnum::EVENT_GRAPHICS_OPENGL_DELETE_BUFFER_OBJECT) { + QMutexLocker locker(&m_buffersForDeletionLaterMutex); + + EventGraphicsOpenGLDeleteBufferObject* deleteBufferEvent + = dynamic_cast(event); + CaretAssert(deleteBufferEvent); + const GraphicsOpenGLBufferObject* bufferObject = deleteBufferEvent->getOpenGLBufferObject(); + if (bufferObject != NULL) { + /* + * Buffers are created within an OpenGL context and must + * be deleted within that OpenGL context. + */ + m_buffersForDeletionLater.emplace(m_buffersForDeletionLater.end(), + bufferObject->getOpenGLContextPointer(), + bufferObject->getBufferObjectName()); + } + + deleteBufferEvent->setEventProcessed(); + } + else if (event->getEventType() == EventTypeEnum::EVENT_GRAPHICS_OPENGL_DELETE_TEXTURE_NAME) { + QMutexLocker locker(&m_texturesForDeletionLaterMutex); + + EventGraphicsOpenGLDeleteTextureName* deleteTextureEvent + = dynamic_cast(event); + CaretAssert(deleteTextureEvent); + const GraphicsOpenGLTextureName* textureName = deleteTextureEvent->getOpenGLTextureName(); + if (textureName != NULL) { + /* + * Textures names are created within an OpenGL context and must + * be deleted within that OpenGL context. + */ + m_texturesForDeletionLater.emplace(m_texturesForDeletionLater.end(), + textureName->getOpenGLContextPointer(), + textureName->getTextureName()); + } + + deleteTextureEvent->setEventProcessed(); + } + else if (event->getEventType() == EventTypeEnum::EVENT_GET_TEXT_RENDERER_FOR_WINDOW) { + EventGetBrainOpenGLTextRenderer* textRenderEvent = dynamic_cast(event); + CaretAssert(textRenderEvent); + + textRenderEvent->setTextRenderer(m_textRenderer); + textRenderEvent->setEventProcessed(); + } + else if (event->getEventType() == EventTypeEnum::EVENT_OPENGL_OBJECT_TO_WINDOW_TRANSFORM) { + EventOpenGLObjectToWindowTransform* transformEvent = dynamic_cast(event); + loadObjectToWindowTransform(transformEvent); + } +} + +/** + * Draw models in their respective viewports. + * + * @param windowIndex + * Index of window for drawing. + * @param brain + * The brain (must be valid!) + * @param contextSharingGroupPointer + * Pointer to the active OpenGL context. + * @param viewportContents + * Viewport info for drawing. + */ +void BrainOpenGL::drawModels(const int32_t windowIndex, + Brain* brain, + void* contextSharingGroupPointer, + const std::vector& viewportContents) +{ + m_contextSharingGroupPointer = contextSharingGroupPointer; + + const std::vector vpContents(viewportContents.begin(), + viewportContents.end()); +// for (const auto& vp : viewportContents) { +// vpContents.push_back(vp); +// } + + + drawModelsImplementation(windowIndex, + brain, + vpContents); + + deleteUnusedOpenGLNames(); + + m_contextSharingGroupPointer = NULL; +} + +/** + * Selection on a model. + * + * @param windowIndex + * Index of window for selection. + * @param brain + * The brain (must be valid!) + * @param contextSharingGroupPointer + * Pointer to the active OpenGL context + * @param viewportContent + * Viewport content in which mouse was clicked + * @param mouseX + * X position of mouse click + * @param mouseY + * Y position of mouse click + * @param applySelectionBackgroundFiltering + * If true (which is in most cases), if there are multiple items + * selected, those items "behind" other items are not reported. + * For example, suppose a focus is selected and there is a node + * the focus. If this parameter is true, the node will NOT be + * selected. If this parameter is false, the node will be + * selected. + */ +void BrainOpenGL::selectModel(const int32_t windowIndex, + Brain* brain, + void* contextSharingGroupPointer, + const BrainOpenGLViewportContent* viewportContent, + const int32_t mouseX, + const int32_t mouseY, + const bool applySelectionBackgroundFiltering) +{ + m_contextSharingGroupPointer = contextSharingGroupPointer; + + selectModelImplementation(windowIndex, + brain, + viewportContent, + mouseX, + mouseY, + applySelectionBackgroundFiltering); + + deleteUnusedOpenGLNames(); + + m_contextSharingGroupPointer = NULL; +} + +/** + * Project the given window coordinate to the active models. + * If the projection is successful, The 'original' XYZ + * coordinate in 'projectionOut' will be valid. In addition, + * the barycentric coordinate may also be valid in 'projectionOut'. + * + * @param windowIndex + * Index of window for projection + * @param brain + * The brain (must be valid!) + * @param contextSharingGroupPointer + * Pointer to the active OpenGL context. + * @param viewportContent + * Viewport content in which mouse was clicked + * @param mouseX + * X position of mouse click + * @param mouseY + * Y position of mouse click + * @param projectionOut + * Output with projection result. + */ +void BrainOpenGL::projectToModel(const int32_t windowIndex, + Brain* brain, + void* contextSharingGroupPointer, + const BrainOpenGLViewportContent* viewportContent, + const int32_t mouseX, + const int32_t mouseY, + SurfaceProjectedItem& projectionOut) +{ + m_contextSharingGroupPointer = contextSharingGroupPointer; + + projectToModelImplementation(windowIndex, + brain, + viewportContent, + mouseX, + mouseY, + projectionOut); + deleteUnusedOpenGLNames(); + + m_contextSharingGroupPointer = NULL; +} + + +/** + * This method must be called only when the OpenGL context + * is current. + */ +void +BrainOpenGL::deleteUnusedOpenGLNames() +{ + if ( ! m_buffersForDeletionLater.empty()) { + QMutexLocker locker(&m_buffersForDeletionLaterMutex); + + std::vector otherContextBufferIdentifiers; + + std::vector bufferNames; + + for (auto bufferInfo : m_buffersForDeletionLater) { + if (bufferInfo.m_openglContextPointer == m_contextSharingGroupPointer) { + if (glIsBuffer(bufferInfo.m_name)) { + bufferNames.push_back(bufferInfo.m_name); + } + else { + CaretLogWarning("Attempting to delete invalid OpenGL buffer ID: " + + AString::number(bufferInfo.m_name)); + } + } + else { + otherContextBufferIdentifiers.push_back(bufferInfo); + } + } + + if ( ! bufferNames.empty()) { + const GLuint* namesPointer = &bufferNames[0]; + glDeleteBuffers(bufferNames.size(), + namesPointer); + m_buffersForDeletionLater.clear(); + } + + m_buffersForDeletionLater = otherContextBufferIdentifiers; + if ( ! m_buffersForDeletionLater.empty()) { + CaretLogWarning("Not deleting " + + AString::number(m_buffersForDeletionLater.size()) + + " buffers in another OpenGL Context."); + } + } + + if ( ! m_texturesForDeletionLater.empty()) { + QMutexLocker locker(&m_texturesForDeletionLaterMutex); + + std::vector otherContextTextureIdentifiers; + + std::vector textureNames; + + for (auto textureInfo : m_texturesForDeletionLater) { + if (textureInfo.m_openglContextPointer == m_contextSharingGroupPointer) { + if (glIsTexture(textureInfo.m_name)) { + textureNames.push_back(textureInfo.m_name); + } + else { + CaretLogWarning("Attempting to delete invalid OpenGL Texture Name: " + + AString::number(textureInfo.m_name)); + } + } + else { + otherContextTextureIdentifiers.push_back(textureInfo); + } + } + + if ( ! textureNames.empty()) { + const GLuint* namesPointer = &textureNames[0]; + glDeleteTextures(textureNames.size(), + namesPointer); + m_texturesForDeletionLater.clear(); + } + + m_texturesForDeletionLater = otherContextTextureIdentifiers; + if ( ! m_texturesForDeletionLater.empty()) { + CaretLogWarning("Not deleting " + + AString::number(m_texturesForDeletionLater.size()) + + " textures in another OpenGL Context."); + } + } +} + + /** * @return The active text renderer. */ @@ -75,27 +411,41 @@ BrainOpenGL::getTextRenderer() } /** - * Set the text renderer. The existing text renderer will - * be destroyed. This instance will take ownership of - * the text renderer passed in and destory it at the - * proper time. - * - * @param textRenderer - * The text renderer is used for text rendering. - * This parameter MUST NOT be NULL. It must be - * a pointer to a text renderer. + * Set the OpenGL line width. Value is clamped + * to minimum and maximum values to prevent + * OpenGL error caused by invalid line width. */ void -BrainOpenGL::setTextRenderer(BrainOpenGLTextRenderInterface* textRenderer) +BrainOpenGL::setLineWidth(const float lineWidth) { - CaretAssert(textRenderer); - - if (m_textRenderer != NULL) { - delete m_textRenderer; - m_textRenderer = NULL; + if (lineWidth > s_maxLineWidth) { + glLineWidth(s_maxLineWidth); + } + else if (lineWidth < s_minLineWidth) { + glLineWidth(s_minLineWidth); + } + else { + glLineWidth(lineWidth); + } +} + +/** + * Set the OpenGL point size. Value is clamped + * to minimum and maximum values to prevent + * OpenGL error caused by invalid point size. + */ +void +BrainOpenGL::setPointSize(const float pointSize) +{ + if (pointSize > s_maxPointSize) { + glPointSize(s_maxPointSize); + } + else if (pointSize < s_minPointSize) { + glPointSize(s_minPointSize); + } + else { + glPointSize(pointSize); } - - m_textRenderer = textRenderer; } /** @@ -216,6 +566,42 @@ BrainOpenGL::testForVersionOfOpenGLSupported(const AString& versionOfOpenGL) return false; } +/** + * Test for the required version of OpenGL Workbench needs. + * + * @param errorMessageOut + * Output with error message if required version is not available. + * @return + * True if required version available, else false. + */ +bool +BrainOpenGL::testForRequiredOpenGLVersion(AString& errorMessageOut) +{ + const AString minimumOpenGLVersion("1.5"); + if ( ! BrainOpenGL::testForVersionOfOpenGLSupported(minimumOpenGLVersion)) { + const AString msg("OpenGL Version " + + minimumOpenGLVersion + + " or later is required. This computer has version " + + BrainOpenGL::getOpenGLVersion() + + "\nYou may continue but the software may crash."); + errorMessageOut = msg; + return false; + } + + return true; +} + + +/** + * @return The OpenGL version. + */ +AString +BrainOpenGL::getOpenGLVersion() +{ + return QLatin1String(reinterpret_cast(glGetString(GL_VERSION))); +} + + /** * Extract the major and minor versions from an OpenGL version string. * @param versionString @@ -493,7 +879,7 @@ BrainOpenGL::initializeOpenGL() glGetFloatv(GL_POINT_SIZE_RANGE, sizes); s_minPointSize = sizes[0]; s_maxPointSize = sizes[1]; - glGetFloatv(GL_LINE_WIDTH_RANGE, sizes); + glGetFloatv(GL_ALIASED_LINE_WIDTH_RANGE, sizes); s_minLineWidth = sizes[0]; s_maxLineWidth = sizes[1]; @@ -632,6 +1018,13 @@ BrainOpenGL::getOpenGLInformation() + "\nOpenGL Vendor: " + AString(vendorStr) + "\nOpenGL Renderer: " + AString(renderStr)); + AString glewVersionName("No GLEW in this version of Workbench"); +#ifdef HAVE_GLEW + glewVersionName = AString(reinterpret_cast(glewGetString(GLEW_VERSION))); +#endif /* HAVE_GLEW */ + lineInfo += "\n"; + lineInfo += ("\nGLEW Version: " + glewVersionName); + lineInfo += "\n"; lineInfo += ("\nFont Renderer: " + m_textRenderer->getName()); lineInfo += "\n"; @@ -651,9 +1044,14 @@ BrainOpenGL::getOpenGLInformation() const AString smoothLineWidthGranularity = ("GL_SMOOTH_LINE_WIDTH_GRANULARITY value is " + AString::number(values[0])); + glGetFloatv(GL_SMOOTH_POINT_SIZE_RANGE, values); + const AString smoothPointSizeRange = ("GL_SMOOTH_POINT_SIZE_RANGE value is " + + AString::fromNumbers(values, 2, ", ")); + lineInfo += ("\n" + aliasedLineWidthRange + "\n" + smoothLineWidthRange - + "\n" + smoothLineWidthGranularity); + + "\n" + smoothLineWidthGranularity + + "\n" + smoothPointSizeRange); } #endif // GL_VERSION_2_0 //#else // GL_VERSION_2_0 @@ -710,6 +1108,16 @@ BrainOpenGL::getOpenGLInformation() lineInfo += ("\nVertex Buffers Supported: " + AString::fromBool(s_supportsVertexBuffers)); + GLint sampleBuffersCount = 0; + glGetIntegerv(GL_SAMPLE_BUFFERS, &sampleBuffersCount); + GLint sampleCount = 0; + glGetIntegerv(GL_SAMPLES, &sampleCount); + + lineInfo += "\n"; + lineInfo += ("\nSample Buffer Count: " + AString::number(sampleBuffersCount)); + lineInfo += ("\nSamples Count: " + AString::number(sampleCount)); + lineInfo += ("\nMultisampling Enabled: " + AString::fromBool(glIsEnabled(GL_MULTISAMPLE))); + lineInfo += "\n"; lineInfo += "\n"; lineInfo += "Note that State of OpenGL may be different when drawing objects.\n"; @@ -819,7 +1227,7 @@ AString BrainOpenGL::getOpenGLEnabledEnumAsText(const AString& enumName, /* * Reset error status */ - glGetError(); + GraphicsUtilitiesOpenGL::resetOpenGLError(); GLboolean boolValue= glIsEnabled(enumValue); @@ -828,7 +1236,7 @@ AString BrainOpenGL::getOpenGLEnabledEnumAsText(const AString& enumName, if (errorCode != GL_NO_ERROR) { const GLubyte* errorChars = gluErrorString(errorCode); if (errorChars != NULL) { - errorText = ("ERROR = " + errorText = ("ERROR failed to glIsEnabled() value. Reason: " + AString((char*)errorChars)); } } @@ -858,7 +1266,7 @@ AString BrainOpenGL::getOpenGLBooleanAsText(const AString& enumName, /* * Reset error status */ - glGetError(); + GraphicsUtilitiesOpenGL::resetOpenGLError(); GLboolean boolValue = GL_FALSE; glGetBooleanv(enumValue, &boolValue); @@ -868,7 +1276,7 @@ AString BrainOpenGL::getOpenGLBooleanAsText(const AString& enumName, if (errorCode != GL_NO_ERROR) { const GLubyte* errorChars = gluErrorString(errorCode); if (errorChars != NULL) { - errorText = ("ERROR = " + errorText = ("ERROR failed to glGetBooleanv() value. Reason: " + AString((char*)errorChars)); } } @@ -901,7 +1309,7 @@ AString BrainOpenGL::getOpenGLFloatAsText(const AString& enumName, /* * Reset error status */ - glGetError(); + GraphicsUtilitiesOpenGL::resetOpenGLError(); AString valuesString; @@ -915,7 +1323,7 @@ AString BrainOpenGL::getOpenGLFloatAsText(const AString& enumName, if (errorCode != GL_NO_ERROR) { const GLubyte* errorChars = gluErrorString(errorCode); if (errorChars != NULL) { - valuesString = ("ERROR = " + valuesString = ("ERROR failed to glGetFloatv() value. Reason: " + AString((char*)errorChars)); } } @@ -955,7 +1363,7 @@ AString BrainOpenGL::getOpenGLLightAsText(const AString& enumName, /* * Reset error status */ - glGetError(); + GraphicsUtilitiesOpenGL::resetOpenGLError(); AString valuesString; @@ -971,7 +1379,7 @@ AString BrainOpenGL::getOpenGLLightAsText(const AString& enumName, if (errorCode != GL_NO_ERROR) { const GLubyte* errorChars = gluErrorString(errorCode); if (errorChars != NULL) { - valuesString = ("ERROR = " + valuesString = ("ERROR failed to get glGetLightfv() value. Reason " + AString((char*)errorChars)); } } @@ -1032,61 +1440,24 @@ BrainOpenGL::testForOpenGLError(const AString& message, const int32_t windowIndex, const int32_t tabIndex) { - GLenum errorCode = glGetError(); - if (errorCode != GL_NO_ERROR) { + std::unique_ptr openglError = GraphicsUtilitiesOpenGL::getOpenGLError(); + if (openglError) { AString msg; if ( ! message.isEmpty()) { msg.appendWithNewLine(message); } - msg += ("OpenGL Error: " + AString((char*)gluErrorString(errorCode)) + "\n"); - msg += ("OpenGL Version: " + AString((char*)glGetString(GL_VERSION)) + "\n"); - msg += ("OpenGL Vendor: " + AString((char*)glGetString(GL_VENDOR)) + "\n"); if (model != NULL) { - msg += ("While drawing brain model " + model->getNameForGUI(true) + "\n"); + msg.appendWithNewLine("While drawing brain model " + model->getNameForGUI(true)); } if (windowIndex >= 0) { - msg += ("In window number " + AString::number(windowIndex) + "\n"); + msg.appendWithNewLine("In window number " + AString::number(windowIndex)); } if (tabIndex >= 0) { - msg += ("In tab number " + AString::number(tabIndex) + "\n"); + msg.appendWithNewLine("In tab number " + AString::number(tabIndex)); } - GLint maxNameStackDepth, maxModelStackDepth, maxProjStackDepth; - glGetIntegerv(GL_MAX_PROJECTION_STACK_DEPTH, - &maxProjStackDepth); - glGetIntegerv(GL_MAX_MODELVIEW_STACK_DEPTH, - &maxModelStackDepth); - glGetIntegerv(GL_MAX_NAME_STACK_DEPTH, - &maxNameStackDepth); - - GLint nameStackDepth, modelStackDepth, projStackDepth; - glGetIntegerv(GL_PROJECTION_STACK_DEPTH, - &projStackDepth); - glGetIntegerv(GL_MODELVIEW_STACK_DEPTH, - &modelStackDepth); - glGetIntegerv(GL_NAME_STACK_DEPTH, - &nameStackDepth); + msg.appendWithNewLine(openglError->getVerboseDescription()); - msg += ("Projection Matrix Stack Depth " - + AString::number(projStackDepth) - + " Max Depth " - + AString::number(maxProjStackDepth) - + "\n"); - msg += ("Model Matrix Stack Depth " - + AString::number(modelStackDepth) - + " Max Depth " - + AString::number(maxModelStackDepth) - + "\n"); - msg += ("Name Matrix Stack Depth " - + AString::number(nameStackDepth) - + " Max Depth " - + AString::number(maxNameStackDepth) - + "\n"); - SystemBacktrace myBacktrace; - SystemUtilities::getBackTrace(myBacktrace); - msg += ("Backtrace:\n" - + myBacktrace.toSymbolString() - + "\n"); CaretLogSevere(msg); } } diff --git a/src/Brain/BrainOpenGL.h b/src/Brain/BrainOpenGL.h index b4dfcc725a56bea8fcb34c24d2d9412ef4e6b850..f95bf5c51631dfd71834091de7757a286cdbecec 100644 --- a/src/Brain/BrainOpenGL.h +++ b/src/Brain/BrainOpenGL.h @@ -22,6 +22,9 @@ */ /*LICENSE_END*/ +#include + +#include #include #include @@ -55,22 +58,24 @@ #endif // GL_VERSION_4_0 + #include "CaretObject.h" +#include "EventListenerInterface.h" namespace caret { class Border; class Brain; class BrainOpenGLTextRenderInterface; - class BrainOpenGLTextureManager; class BrainOpenGLViewportContent; + class EventOpenGLObjectToWindowTransform; class Model; class SurfaceProjectedItem; /** * Performs drawing of graphics using OpenGL. */ - class BrainOpenGL : public CaretObject { + class BrainOpenGL : public CaretObject, public EventListenerInterface { protected: BrainOpenGL(BrainOpenGLTextRenderInterface* textRenderer); @@ -97,66 +102,26 @@ namespace caret { BrainOpenGLTextRenderInterface* getTextRenderer(); - void setTextRenderer(BrainOpenGLTextRenderInterface* textRenderer); - - /** - * Draw models in their respective viewports. - * - * @param brain - * The brain (must be valid!) - * @param viewportContents - * Viewport info for drawing. - */ - virtual void drawModels(Brain* brain, - std::vector& viewportContents) = 0; - - /** - * Selection on a model. - * - * @param brain - * The brain (must be valid!) - * @param viewportContent - * Viewport content in which mouse was clicked - * @param mouseX - * X position of mouse click - * @param mouseY - * Y position of mouse click - * @param applySelectionBackgroundFiltering - * If true (which is in most cases), if there are multiple items - * selected, those items "behind" other items are not reported. - * For example, suppose a focus is selected and there is a node - * the focus. If this parameter is true, the node will NOT be - * selected. If this parameter is false, the node will be - * selected. - */ - virtual void selectModel(Brain* brain, - BrainOpenGLViewportContent* viewportContent, - const int32_t mouseX, - const int32_t mouseY, - const bool applySelectionBackgroundFiltering) = 0; - - /** - * Project the given window coordinate to the active models. - * If the projection is successful, The 'original' XYZ - * coordinate in 'projectionOut' will be valid. In addition, - * the barycentric coordinate may also be valid in 'projectionOut'. - * - * @param brain - * The brain (must be valid!) - * @param viewportContent - * Viewport content in which mouse was clicked - * @param mouseX - * X position of mouse click - * @param mouseY - * Y position of mouse click - */ - virtual void projectToModel(Brain* brain, - BrainOpenGLViewportContent* viewportContent, - const int32_t mouseX, - const int32_t mouseY, - SurfaceProjectedItem& projectionOut) = 0; - - virtual BrainOpenGLTextureManager* getTextureManager() = 0; + void drawModels(const int32_t windowIndex, + Brain* brain, + void* contextSharingGroupPointer, + const std::vector& viewportContents); + + void selectModel(const int32_t windowIndex, + Brain* brain, + void* contextSharingGroupPointer, + const BrainOpenGLViewportContent* viewportContent, + const int32_t mouseX, + const int32_t mouseY, + const bool applySelectionBackgroundFiltering); + + void projectToModel(const int32_t windowIndex, + Brain* brain, + void* contextSharingGroupPointer, + const BrainOpenGLViewportContent* viewportContent, + const int32_t mouseX, + const int32_t mouseY, + SurfaceProjectedItem& projectionOut); /** * @return Half-size of the model window height. @@ -171,12 +136,20 @@ namespace caret { void getBackgroundColor(uint8_t backgroundColor[3]) const; + static void setLineWidth(const float lineWidth); + + static void setPointSize(const float pointSize); + static void getMinMaxPointSize(float& minPointSizeOut, float& maxPointSizeOut); static void getMinMaxLineWidth(float& minLineWidthOut, float& maxLineWidthOut); + static bool testForRequiredOpenGLVersion(AString& errorMessageOut); + static bool testForVersionOfOpenGLSupported(const AString& versionOfOpenGL); + static AString getOpenGLVersion(); + static void testForOpenGLError(const AString& message); static void testForOpenGLError(const AString& message, @@ -212,12 +185,115 @@ namespace caret { virtual AString getStateOfOpenGL() const; + virtual void receiveEvent(Event* event); + + /** + * @return Pointer to OpenGL context sharing group pointer. + */ + inline void* getContextSharingGroupPointer() { return m_contextSharingGroupPointer; } + private: + class OpenGLNameInfo { + public: + OpenGLNameInfo(void* openglContextPointer, + const GLuint name) + : m_openglContextPointer(openglContextPointer), + m_name(name) { } + + void* m_openglContextPointer; + GLuint m_name; + }; + + void deleteUnusedOpenGLNames(); + + /** prevents concurrent access to m_buffersForDeletionLater */ + QMutex m_buffersForDeletionLaterMutex; + + /** use a mutex whenever accessing this member */ + std::vector m_buffersForDeletionLater; + + /** prevents concurrent access to m_texturesForDeletionLater */ + QMutex m_texturesForDeletionLaterMutex; + + /** use a mutex whenever accessing this member */ + std::vector m_texturesForDeletionLater; + + /** Pointer to the current OpenGL Context sharing group */ + void* m_contextSharingGroupPointer = 0; + BrainOpenGL(const BrainOpenGL&); BrainOpenGL& operator=(const BrainOpenGL&); protected: + /** + * Draw models in their respective viewports. + * + * @param windowIndex + * Index of window for drawing + * @param brain + * The brain (must be valid!) + * @param viewportContents + * Viewport info for drawing. + */ + virtual void drawModelsImplementation(const int32_t windowIndex, + Brain* brain, + const std::vector& viewportContents) = 0; + + /** + * Selection on a model. + * + * @param windowIndex + * Index of window for selection + * @param brain + * The brain (must be valid!) + * @param viewportContent + * Viewport content in which mouse was clicked + * @param mouseX + * X position of mouse click + * @param mouseY + * Y position of mouse click + * @param applySelectionBackgroundFiltering + * If true (which is in most cases), if there are multiple items + * selected, those items "behind" other items are not reported. + * For example, suppose a focus is selected and there is a node + * the focus. If this parameter is true, the node will NOT be + * selected. If this parameter is false, the node will be + * selected. + */ + virtual void selectModelImplementation(const int32_t windowIndex, + Brain* brain, + const BrainOpenGLViewportContent* viewportContent, + const int32_t mouseX, + const int32_t mouseY, + const bool applySelectionBackgroundFiltering) = 0; + + /** + * Project the given window coordinate to the active models. + * If the projection is successful, The 'original' XYZ + * coordinate in 'projectionOut' will be valid. In addition, + * the barycentric coordinate may also be valid in 'projectionOut'. + * + * @param windowIndex + * Index of window for projection + * @param brain + * The brain (must be valid!) + * @param viewportContent + * Viewport content in which mouse was clicked + * @param mouseX + * X position of mouse click + * @param mouseY + * Y position of mouse click + * @param projectionOut + * Output with projection result. + */ + virtual void projectToModelImplementation(const int32_t windowIndex, + Brain* brain, + const BrainOpenGLViewportContent* viewportContent, + const int32_t mouseX, + const int32_t mouseY, + SurfaceProjectedItem& projectionOut) = 0; + AString getOpenGLEnabledEnumAsText(const AString& enumName, const GLenum enumValue) const; @@ -233,6 +309,8 @@ namespace caret { const GLenum enumValue, const int32_t numberOfValues) const; + virtual void loadObjectToWindowTransform(EventOpenGLObjectToWindowTransform* transformEvent) = 0; + Border* borderBeingDrawn; bool m_drawHighlightedEndPoints; diff --git a/src/Brain/BrainOpenGLAnnotationDrawingFixedPipeline.cxx b/src/Brain/BrainOpenGLAnnotationDrawingFixedPipeline.cxx index cb0fc638f756067e73a3bf35e973b45a69295b68..6a85693b5becf941ed6e1f7ec256f4f9271e1f98 100644 --- a/src/Brain/BrainOpenGLAnnotationDrawingFixedPipeline.cxx +++ b/src/Brain/BrainOpenGLAnnotationDrawingFixedPipeline.cxx @@ -42,10 +42,7 @@ #include "AnnotationText.h" #include "Brain.h" #include "BrainOpenGLFixedPipeline.h" -#include "BrainOpenGLPrimitiveDrawing.h" -#include "BrainOpenGLShapeRing.h" #include "BrainOpenGLTextRenderInterface.h" -#include "BrainOpenGLTextureManager.h" #include "BrowserTabContent.h" #include "CaretAssert.h" #include "CaretColorEnum.h" @@ -54,6 +51,12 @@ #include "DisplayPropertiesAnnotation.h" #include "EventBrowserTabGet.h" #include "EventManager.h" +#include "EventOpenGLObjectToWindowTransform.h" +#include "GraphicsEngineDataOpenGL.h" +#include "GraphicsPrimitiveV3f.h" +#include "GraphicsPrimitiveV3fC4f.h" +#include "GraphicsPrimitiveV3fT3f.h" +#include "GraphicsShape.h" #include "IdentificationWithColor.h" #include "MathFunctions.h" #include "Matrix4x4.h" @@ -89,10 +92,10 @@ m_volumeSliceThickness(0.0) m_dummyAnnotationFile = new AnnotationFile(); m_dummyAnnotationFile->setFileName("DummyFileForDrawing" + DataFileTypeEnum::toFileExtension(DataFileTypeEnum::ANNOTATION)); - m_rotationHandleCircle = new BrainOpenGLShapeRing(20, - 0.7, - 1.0); + float unusedLineWidthMaximum(0); + BrainOpenGL::getMinMaxLineWidth(m_lineWidthMinimum, + unusedLineWidthMaximum); } /** @@ -100,7 +103,6 @@ m_volumeSliceThickness(0.0) */ BrainOpenGLAnnotationDrawingFixedPipeline::~BrainOpenGLAnnotationDrawingFixedPipeline() { - delete m_rotationHandleCircle; delete m_dummyAnnotationFile; } @@ -135,66 +137,72 @@ BrainOpenGLAnnotationDrawingFixedPipeline::viewportToOpenGLWindowCoordinate(cons } /** - * Get the window coordinate for display of the annotation. + * Get the drawing space coordinate for display of the annotation. * + * @param annotation + * The annotation. * @param coordinate * The annotation coordinate whose window coordinate is computed. + * @param annotationCoordSpace + * The annotation coordinate space. * @param surfaceDisplayed * Surface that is displayed (may be NULL !) - * @param windowXYZOut - * Output containing the window coordinate. + * @param xyzOut + * Output containing the drawing space coordinate. * @return - * True if the window coordinate is valid, else false. + * True if the drawing space coordinate is valid, else false. */ bool -BrainOpenGLAnnotationDrawingFixedPipeline::getAnnotationWindowCoordinate(const AnnotationCoordinate* coordinate, - const AnnotationCoordinateSpaceEnum::Enum annotationCoordSpace, - const Surface* surfaceDisplayed, - float windowXYZOut[3]) const +BrainOpenGLAnnotationDrawingFixedPipeline::getAnnotationDrawingSpaceCoordinate(const Annotation* annotation, + const AnnotationCoordinate* coordinate, + const Surface* surfaceDisplayed, + float xyzOut[3]) const { - float stereotaxicXYZ[3] = { 0.0, 0.0, 0.0 }; - bool stereotaxicXYZValid = false; + const AnnotationCoordinateSpaceEnum::Enum annotationCoordSpace = annotation->getCoordinateSpace(); - float windowXYZ[3] = { 0.0, 0.0, 0.0 }; - bool windowXYZValid = false; + float modelXYZ[3] = { 0.0, 0.0, 0.0 }; + bool modelXYZValid = false; + + float drawingSpaceXYZ[3] = { 0.0, 0.0, 0.0 }; + bool drawingSpaceXYZValid = false; float annotationXYZ[3]; coordinate->getXYZ(annotationXYZ); switch (annotationCoordSpace) { + case AnnotationCoordinateSpaceEnum::CHART: + modelXYZ[0] = annotationXYZ[0]; + modelXYZ[1] = annotationXYZ[1]; + modelXYZ[2] = annotationXYZ[2]; + modelXYZValid = true; + break; case AnnotationCoordinateSpaceEnum::STEREOTAXIC: - stereotaxicXYZ[0] = annotationXYZ[0]; - stereotaxicXYZ[1] = annotationXYZ[1]; - stereotaxicXYZ[2] = annotationXYZ[2]; - stereotaxicXYZValid = true; + modelXYZ[0] = annotationXYZ[0]; + modelXYZ[1] = annotationXYZ[1]; + modelXYZ[2] = annotationXYZ[2]; + modelXYZValid = true; if (m_volumeSpacePlaneValid) { float xyzFloat[3] = { - stereotaxicXYZ[0], - stereotaxicXYZ[1], - stereotaxicXYZ[2] + modelXYZ[0], + modelXYZ[1], + modelXYZ[2] }; const float distToPlaneAbs = std::fabs(m_volumeSpacePlane.signedDistanceToPlane(xyzFloat)); const float halfSliceThickness = ((m_volumeSliceThickness > 0.0) ? (m_volumeSliceThickness / 2.0) : 1.0); - if (distToPlaneAbs < halfSliceThickness) { //1.5) { - stereotaxicXYZValid = true; + if (distToPlaneAbs < halfSliceThickness) { + modelXYZValid = true; float projectedPoint[3]; - m_volumeSpacePlane.projectPointToPlane(stereotaxicXYZ, projectedPoint); + m_volumeSpacePlane.projectPointToPlane(modelXYZ, projectedPoint); } else { - stereotaxicXYZValid = false; + modelXYZValid = false; } } break; - case AnnotationCoordinateSpaceEnum::PIXELS: - windowXYZ[0] = annotationXYZ[0]; - windowXYZ[1] = annotationXYZ[1]; - windowXYZ[2] = annotationXYZ[2]; - windowXYZValid = true; - break; case AnnotationCoordinateSpaceEnum::SURFACE: if (surfaceDisplayed != NULL) { StructureEnum::Enum annotationStructure = StructureEnum::INVALID; @@ -217,31 +225,13 @@ BrainOpenGLAnnotationDrawingFixedPipeline::getAnnotationWindowCoordinate(const A float nodeXYZ[3]; surfaceDisplayed->getCoordinate(annotationNodeIndex, nodeXYZ); - stereotaxicXYZ[0] = nodeXYZ[0]; - stereotaxicXYZ[1] = nodeXYZ[1]; - stereotaxicXYZ[2] = nodeXYZ[2]; + modelXYZ[0] = nodeXYZ[0]; + modelXYZ[1] = nodeXYZ[1]; + modelXYZ[2] = nodeXYZ[2]; float offsetUnitVector[3] = { 0.0, 0.0, 0.0 }; -// const bool useNormalVectorForOffsetFlag = false; -// if (useNormalVectorForOffsetFlag) { -// const float* normalVector = surfaceDisplayed->getNormalVector(annotationNodeIndex); -// offsetUnitVector[0] = normalVector[0]; -// offsetUnitVector[1] = normalVector[1]; -// offsetUnitVector[2] = normalVector[2]; -// } -// else { -// BoundingBox boundingBox; -// surfaceDisplayed->getBounds(boundingBox); -// float surfaceCenter[3] = { 0.0, 0.0, 0.0 }; -// boundingBox.getCenter(surfaceCenter); -// -// MathFunctions::subtractVectors(nodeXYZ, -// surfaceCenter, -// offsetUnitVector); -// MathFunctions::normalizeVector(offsetUnitVector); -// } /* * For a flat surface, ALWAYS use the normal vector. @@ -274,66 +264,71 @@ BrainOpenGLAnnotationDrawingFixedPipeline::getAnnotationWindowCoordinate(const A break; } - stereotaxicXYZ[0] += (offsetUnitVector[0] * annotationOffsetLength); - stereotaxicXYZ[1] += (offsetUnitVector[1] * annotationOffsetLength); - stereotaxicXYZ[2] += (offsetUnitVector[2] * annotationOffsetLength); - stereotaxicXYZValid = true; + modelXYZ[0] += (offsetUnitVector[0] * annotationOffsetLength); + modelXYZ[1] += (offsetUnitVector[1] * annotationOffsetLength); + modelXYZ[2] += (offsetUnitVector[2] * annotationOffsetLength); + modelXYZValid = true; } } } break; case AnnotationCoordinateSpaceEnum::TAB: - viewportToOpenGLWindowCoordinate(annotationXYZ, windowXYZ); - windowXYZValid = true; -// { -// GLint viewport[4]; -// glGetIntegerv(GL_VIEWPORT, -// viewport); -// windowXYZ[0] = viewport[2] * (annotationXYZ[0] / 100.0); -// windowXYZ[1] = viewport[3] * (annotationXYZ[1] / 100.0); -//// windowXYZ[2] = annotationXYZ[2] / 100.0; -// windowXYZ[2] = toWindowZ(annotationXYZ[2]); -// windowXYZValid = true; -// } + viewportToOpenGLWindowCoordinate(annotationXYZ, drawingSpaceXYZ); + drawingSpaceXYZValid = true; + break; + case AnnotationCoordinateSpaceEnum::VIEWPORT: + { + drawingSpaceXYZ[0] = annotationXYZ[0]; + drawingSpaceXYZ[1] = annotationXYZ[1]; + drawingSpaceXYZ[2] = annotationXYZ[2]; + drawingSpaceXYZValid = true; + + const bool drawCrossFlag = false; + if (drawCrossFlag) { + const float redRGBA[4] = { 1.0f, 0.0, 0.0, 1.0f }; + std::unique_ptr crossShape = std::unique_ptr(GraphicsPrimitive::newPrimitiveV3f(GraphicsPrimitive::PrimitiveType::OPENGL_LINES, + redRGBA)); + crossShape->setLineWidth(GraphicsPrimitive::LineWidthType::PIXELS, 2.0f); + crossShape->addVertex(drawingSpaceXYZ[0], + drawingSpaceXYZ[1] - 10); + crossShape->addVertex(drawingSpaceXYZ[0], + drawingSpaceXYZ[1] + 10); + crossShape->addVertex(drawingSpaceXYZ[0] - 10, + drawingSpaceXYZ[1]); + crossShape->addVertex(drawingSpaceXYZ[0] + 10, + drawingSpaceXYZ[1]); + GraphicsEngineDataOpenGL::draw(crossShape.get()); + } + } break; case AnnotationCoordinateSpaceEnum::WINDOW: - viewportToOpenGLWindowCoordinate(annotationXYZ, windowXYZ); - windowXYZValid = true; -// { -// GLint viewport[4]; -// glGetIntegerv(GL_VIEWPORT, -// viewport); -// windowXYZ[0] = viewport[2] * (annotationXYZ[0] / 100.0); -// windowXYZ[1] = viewport[3] * (annotationXYZ[1] / 100.0); -//// windowXYZ[2] = annotationXYZ[2] / 100.0; -// windowXYZ[2] = toWindowZ(annotationXYZ[2]); -// windowXYZValid = true; -// } + viewportToOpenGLWindowCoordinate(annotationXYZ, drawingSpaceXYZ); + drawingSpaceXYZValid = true; break; } - if (stereotaxicXYZValid) { + if (modelXYZValid) { /* * Convert model space coordinates to window coordinates * as all annotations are drawn in window coordinates. */ - if (convertModelToWindowCoordinate(stereotaxicXYZ, windowXYZ)) { - stereotaxicXYZValid = false; - windowXYZValid = true; + if (convertModelToWindowCoordinate(modelXYZ, drawingSpaceXYZ)) { + modelXYZValid = false; + drawingSpaceXYZValid = true; } else { - CaretLogSevere("Failed to convert model coordinates to window coordinates for annotation drawing."); + CaretLogSevere("Failed to convert model coordinates to drawing space coordinates for annotation drawing."); } } - if (windowXYZValid) { - windowXYZOut[0] = windowXYZ[0]; - windowXYZOut[1] = windowXYZ[1]; - windowXYZOut[2] = windowXYZ[2]; + if (drawingSpaceXYZValid) { + xyzOut[0] = drawingSpaceXYZ[0]; + xyzOut[1] = drawingSpaceXYZ[1]; + xyzOut[2] = drawingSpaceXYZ[2]; } - return windowXYZValid; + return drawingSpaceXYZValid; } @@ -350,69 +345,12 @@ bool BrainOpenGLAnnotationDrawingFixedPipeline::convertModelToWindowCoordinate(const float modelXYZ[3], float windowXYZOut[3]) const { - /* - * Convert model space coordinates to window coordinates - * as all annotations are drawn in window coordinates. - */ - GLdouble modelDoubleXYZ[3] = { modelXYZ[0], modelXYZ[1], modelXYZ[2] }; - GLdouble windowX, windowY, windowZ; - if (gluProject(modelDoubleXYZ[0], modelDoubleXYZ[1], modelDoubleXYZ[2], - m_modelSpaceModelMatrix, m_modelSpaceProjectionMatrix, m_modelSpaceViewport, - &windowX, &windowY, &windowZ) == GL_TRUE) { - windowXYZOut[0] = windowX - m_modelSpaceViewport[0]; - windowXYZOut[1] = windowY - m_modelSpaceViewport[1]; - - /* - * From OpenGL Programming Guide 3rd Ed, p 133: - * - * If the near value is 1.0 and the far value is 3.0, - * objects must have z-coordinates between -1.0 and -3.0 in order to be visible. - * So, negate the Z-value to be negative. - */ - windowXYZOut[2] = -windowZ; - - if ((m_modelSpaceProjectionMatrix[0] != 0.0) - && (m_modelSpaceProjectionMatrix[5] != 0.0) - && (m_modelSpaceProjectionMatrix[10] != 0.0)) { - /* - * From http://lektiondestages.blogspot.com/2013/11/decompose-opengl-projection-matrix.html - */ - float nearValue = (1.0f + m_modelSpaceProjectionMatrix[14]) / m_modelSpaceProjectionMatrix[10]; - float farValue = -(1.0f - m_modelSpaceProjectionMatrix[14]) / m_modelSpaceProjectionMatrix[10]; - //GLfloat bottom = (1.0f - m_modelSpaceProjectionMatrix[13]) / m_modelSpaceProjectionMatrix[5]; - //GLfloat top = -(1.0f + m_modelSpaceProjectionMatrix[13]) / m_modelSpaceProjectionMatrix[5]; - float left = -(1.0f + m_modelSpaceProjectionMatrix[12]) / m_modelSpaceProjectionMatrix[0]; - float right = (1.0f - m_modelSpaceProjectionMatrix[12]) / m_modelSpaceProjectionMatrix[0]; - - /* - * Depending upon view, near may be positive and far negative - */ - const float farNearRange = std::fabs(farValue - nearValue); - if ((m_inputs->m_centerToEyeDistance > 0.0) - && (farNearRange > 0.0)) { - /* - * Using gluLookAt moves the eye away from the center which - * causes the window Z to also move. Thus, we need to remove - * this amount from the window's Z-coordinate. - */ - const float eyeAdjustment = (m_inputs->m_centerToEyeDistance / farNearRange); - if (m_volumeSpacePlaneValid) { - windowXYZOut[2] = -(windowZ - eyeAdjustment); - } - else if (left > right) { - windowXYZOut[2] = -(windowZ - eyeAdjustment); - } - else { - windowXYZOut[2] = -(windowZ + eyeAdjustment); - } -// std::cout << "Z adjusted for eye/near/far=" << windowXYZOut[2] << std::endl; - } - } - - return true; - } - - return false; + CaretAssert(m_transformEvent); + CaretAssert(m_transformEvent->isValid()); + m_transformEvent->transformPoint(modelXYZ, windowXYZOut); + windowXYZOut[0] -= m_modelSpaceViewport[0]; + windowXYZOut[1] -= m_modelSpaceViewport[1]; + return true; } /** @@ -441,10 +379,6 @@ BrainOpenGLAnnotationDrawingFixedPipeline::getAnnotationTwoDimShapeBounds(const { float viewportWidth = m_modelSpaceViewport[2]; float viewportHeight = m_modelSpaceViewport[3]; -// if (annotation2D->getCoordinateSpace() == AnnotationCoordinateSpaceEnum::STEREOTAXIC) { -// viewportWidth = m_inputs->m_tabViewport[2]; -// viewportHeight = m_inputs->m_tabViewport[3]; -// } /* * Only use text characters when the text is NOT empty @@ -461,7 +395,7 @@ BrainOpenGLAnnotationDrawingFixedPipeline::getAnnotationTwoDimShapeBounds(const if (textFlag) { m_brainOpenGLFixedPipeline->getTextRenderer()->getBoundsForTextAtViewportCoords(*textAnnotation, windowXYZ[0], windowXYZ[1], windowXYZ[2], - viewportHeight, + viewportWidth, viewportHeight, bottomLeftOut, bottomRightOut, topRightOut, topLeftOut); boundsValid = true; @@ -536,9 +470,11 @@ BrainOpenGLAnnotationDrawingFixedPipeline::drawModelSpaceAnnotationsOnVolumeSlic m_volumeSpacePlane = plane; m_volumeSpacePlaneValid = true; - std::vector colorBars; + std::vector emptyColorBars; + std::vector emptyNotInFileAnnotations; drawAnnotationsInternal(AnnotationCoordinateSpaceEnum::STEREOTAXIC, - colorBars, + emptyColorBars, + emptyNotInFileAnnotations, NULL, sliceThickness); } @@ -556,6 +492,8 @@ BrainOpenGLAnnotationDrawingFixedPipeline::drawModelSpaceAnnotationsOnVolumeSlic * Coordinate space of annotation that are drawn. * @param colorbars * Colorbars that will be drawn. + * @param notInFileAnnotations + * Annotations that are not in a file but need to be drawn. * @param surfaceDisplayed * In not NULL, surface no which annotations are drawn. */ @@ -563,6 +501,7 @@ void BrainOpenGLAnnotationDrawingFixedPipeline::drawAnnotations(Inputs* inputs, const AnnotationCoordinateSpaceEnum::Enum drawingCoordinateSpace, std::vector& colorBars, + std::vector& notInFileAnnotations, const Surface* surfaceDisplayed) { CaretAssert(inputs); @@ -570,9 +509,11 @@ BrainOpenGLAnnotationDrawingFixedPipeline::drawAnnotations(Inputs* inputs, m_volumeSpacePlaneValid = false; - const float sliceThickness = 0.0; + const float sliceThickness = 0.0; + drawAnnotationsInternal(drawingCoordinateSpace, colorBars, + notInFileAnnotations, surfaceDisplayed, sliceThickness); @@ -584,20 +525,21 @@ BrainOpenGLAnnotationDrawingFixedPipeline::drawAnnotations(Inputs* inputs, * * @param drawingCoordinateSpace * Coordinate space of annotation that are drawn. - * @param surfaceDisplayed - * Surface that is displayed. May be NULL in some instances. * @param colorbars * Colorbars that will be drawn. + * @param notInFileAnnotations + * Annotations that are not in a file but need to be drawn. + * @param surfaceDisplayed + * Surface that is displayed. May be NULL in some instances. * @param surfaceDisplayed * In not NULL, surface no which annotations are drawn. * @param sliceThickness * Thickness of volume slice - * @param centerToEyeDistance - * Distance from center to eye using in gluLookAt(). */ void BrainOpenGLAnnotationDrawingFixedPipeline::drawAnnotationsInternal(const AnnotationCoordinateSpaceEnum::Enum drawingCoordinateSpace, std::vector& colorBars, + std::vector& notInFileAnnotations, const Surface* surfaceDisplayed, const float sliceThickness) { @@ -605,10 +547,13 @@ BrainOpenGLAnnotationDrawingFixedPipeline::drawAnnotationsInternal(const Annotat return; } - const DisplayPropertiesAnnotation* dpa = m_inputs->m_brain->getDisplayPropertiesAnnotation(); - if ( ! dpa->isDisplayAnnotations()) { - return; + EventOpenGLObjectToWindowTransform::SpaceType spaceType = EventOpenGLObjectToWindowTransform::SpaceType::MODEL; + if (m_volumeSpacePlaneValid) { + spaceType = EventOpenGLObjectToWindowTransform::SpaceType::VOLUME_SLICE_MODEL; } + m_transformEvent.reset(new EventOpenGLObjectToWindowTransform(spaceType)); + EventManager::get()->sendEvent(m_transformEvent.get()->getPointer()); + CaretAssert(m_transformEvent->isValid()); m_volumeSliceThickness = sliceThickness; @@ -628,18 +573,20 @@ BrainOpenGLAnnotationDrawingFixedPipeline::drawAnnotationsInternal(const Annotat bool drawAnnotationsFromFilesFlag = true; + bool haveDisplayGroupFlag = true; switch (drawingCoordinateSpace) { - case AnnotationCoordinateSpaceEnum::STEREOTAXIC: + case AnnotationCoordinateSpaceEnum::CHART: break; - case AnnotationCoordinateSpaceEnum::PIXELS: - CaretAssertMessage(0, "Never draw annotations in pixel space."); - return; + case AnnotationCoordinateSpaceEnum::STEREOTAXIC: break; case AnnotationCoordinateSpaceEnum::SURFACE: break; case AnnotationCoordinateSpaceEnum::TAB: break; + case AnnotationCoordinateSpaceEnum::VIEWPORT: + break; case AnnotationCoordinateSpaceEnum::WINDOW: + haveDisplayGroupFlag = false; switch (m_inputs->m_windowDrawingMode) { case Inputs::WINDOW_DRAWING_NO: drawAnnotationsFromFilesFlag = false; @@ -647,18 +594,25 @@ BrainOpenGLAnnotationDrawingFixedPipeline::drawAnnotationsInternal(const Annotat case Inputs::WINDOW_DRAWING_YES: break; } -// if ( ! m_inputs->m_drawWindowAnnotationsFlag) { -// drawAnnotationsFromFilesFlag = false; -// } break; } + /* + * User may turn off display of all annotations. + * Color bars and other annotations not in a file are always + * drawn so continue processing. + */ + const DisplayPropertiesAnnotation* dpa = m_inputs->m_brain->getDisplayPropertiesAnnotation(); + if ( ! dpa->isDisplayAnnotations()) { + drawAnnotationsFromFilesFlag = false; + } + /* * Note: When window annotations are being drawn, the * tab index is invalid so it must be ignored. */ DisplayGroupEnum::Enum displayGroup = DisplayGroupEnum::DISPLAY_GROUP_A; - if (drawingCoordinateSpace != AnnotationCoordinateSpaceEnum::WINDOW) { + if (haveDisplayGroupFlag) { displayGroup = dpa->getDisplayGroupForTab(m_inputs->m_tabIndex); } @@ -720,7 +674,8 @@ BrainOpenGLAnnotationDrawingFixedPipeline::drawAnnotationsInternal(const Annotat /* * Draw annotations from all files. - * NOTE: iFile == numAnnFiles, the annotation colorbars are drawn + * NOTE: iFile == numAnnFiles, the annotation colorbars + * and annotation chart labels are drawn */ const int32_t numAnnFiles = static_cast(allAnnotationFiles.size()); for (int32_t iFile = 0; iFile <= numAnnFiles; iFile++) { @@ -734,12 +689,14 @@ BrainOpenGLAnnotationDrawingFixedPipeline::drawAnnotationsInternal(const Annotat annotationFile = m_dummyAnnotationFile; switch (drawingCoordinateSpace) { - case AnnotationCoordinateSpaceEnum::STEREOTAXIC: + case AnnotationCoordinateSpaceEnum::CHART: break; - case AnnotationCoordinateSpaceEnum::PIXELS: + case AnnotationCoordinateSpaceEnum::STEREOTAXIC: break; case AnnotationCoordinateSpaceEnum::SURFACE: break; + case AnnotationCoordinateSpaceEnum::VIEWPORT: + break; case AnnotationCoordinateSpaceEnum::TAB: case AnnotationCoordinateSpaceEnum::WINDOW: { @@ -796,6 +753,10 @@ BrainOpenGLAnnotationDrawingFixedPipeline::drawAnnotationsInternal(const Annotat } break; } + + annotationsFromFile.insert(annotationsFromFile.end(), + notInFileAnnotations.begin(), + notInFileAnnotations.end()); } else { CaretAssertVectorIndex(allAnnotationFiles, iFile); @@ -810,22 +771,15 @@ BrainOpenGLAnnotationDrawingFixedPipeline::drawAnnotationsInternal(const Annotat CaretAssert(annotation); bool drawItFlag = false; - if (annotation->getType() == AnnotationTypeEnum::COLOR_BAR) { - AnnotationColorBar* colorBar = dynamic_cast(annotation); - CaretAssert(colorBar); - drawItFlag = colorBar->isDisplayed(); - } - else { - switch (annotation->getItemDisplaySelected(displayGroup, m_inputs->m_tabIndex)) { - case TriStateSelectionStatusEnum::PARTIALLY_SELECTED: - CaretAssertMessage(0, "An annotation should never be partially selected"); - break; - case TriStateSelectionStatusEnum::SELECTED: - drawItFlag = true; - break; - case TriStateSelectionStatusEnum::UNSELECTED: - break; - } + switch (annotation->getItemDisplaySelected(displayGroup, m_inputs->m_tabIndex)) { + case TriStateSelectionStatusEnum::PARTIALLY_SELECTED: + CaretAssertMessage(0, "An annotation should never be partially selected"); + break; + case TriStateSelectionStatusEnum::SELECTED: + drawItFlag = true; + break; + case TriStateSelectionStatusEnum::UNSELECTED: + break; } if ( ! drawItFlag) { @@ -1020,9 +974,6 @@ BrainOpenGLAnnotationDrawingFixedPipeline::startOpenGLForDrawing(GLint* savedSha glPushMatrix(); glLoadIdentity(); - /* - * Enable anti-aliasing for lines and polygons - */ m_brainOpenGLFixedPipeline->enableLineAntiAliasing(); } @@ -1095,6 +1046,21 @@ BrainOpenGLAnnotationDrawingFixedPipeline::drawAnnotation(AnnotationFile* annota bool drawnFlag = false; + switch (annotation->getCoordinateSpace()) { + case AnnotationCoordinateSpaceEnum::CHART: + break; + case AnnotationCoordinateSpaceEnum::STEREOTAXIC: + break; + case AnnotationCoordinateSpaceEnum::SURFACE: + break; + case AnnotationCoordinateSpaceEnum::TAB: + break; + case AnnotationCoordinateSpaceEnum::VIEWPORT: + break; + case AnnotationCoordinateSpaceEnum::WINDOW: + break; + } + switch (annotation->getType()) { case AnnotationTypeEnum::BOX: drawnFlag = drawBox(annotationFile, @@ -1153,11 +1119,11 @@ BrainOpenGLAnnotationDrawingFixedPipeline::drawBox(AnnotationFile* annotationFil CaretAssert(box); CaretAssert(box->getType() == AnnotationTypeEnum::BOX); - float windowXYZ[3]; - if ( ! getAnnotationWindowCoordinate(box->getCoordinate(), - box->getCoordinateSpace(), + float annXYZ[3]; + if ( ! getAnnotationDrawingSpaceCoordinate(box, + box->getCoordinate(), surfaceDisplayed, - windowXYZ)) { + annXYZ)) { return false; } @@ -1165,37 +1131,32 @@ BrainOpenGLAnnotationDrawingFixedPipeline::drawBox(AnnotationFile* annotationFil float bottomRight[3]; float topRight[3]; float topLeft[3]; - if ( ! getAnnotationTwoDimShapeBounds(box, windowXYZ, + if ( ! getAnnotationTwoDimShapeBounds(box, annXYZ, bottomLeft, bottomRight, topRight, topLeft)) { return false; } const float selectionCenterXYZ[3] = { - (bottomLeft[0] + bottomRight[0] + topRight[0] + topLeft[0]) / 4.0, - (bottomLeft[1] + bottomRight[1] + topRight[1] + topLeft[1]) / 4.0, - (bottomLeft[2] + bottomRight[2] + topRight[2] + topLeft[2]) / 4.0 + (bottomLeft[0] + bottomRight[0] + topRight[0] + topLeft[0]) / 4.0f, + (bottomLeft[1] + bottomRight[1] + topRight[1] + topLeft[1]) / 4.0f, + (bottomLeft[2] + bottomRight[2] + topRight[2] + topLeft[2]) / 4.0f }; - const float outlineWidth = box->getLineWidth(); + if (box->getLineWidthPercentage() <= 0.0f) { + convertObsoleteLineWidthPixelsToPercentageWidth(box); + } - const bool depthTestFlag = isDrawnWithDepthTesting(box); + const bool depthTestFlag = isDrawnWithDepthTesting(box, + surfaceDisplayed); const bool savedDepthTestStatus = setDepthTestingStatus(depthTestFlag); - std::vector coords; - coords.insert(coords.end(), bottomLeft, bottomLeft + 3); - coords.insert(coords.end(), bottomRight, bottomRight + 3); - coords.insert(coords.end(), topRight, topRight + 3); - coords.insert(coords.end(), topLeft, topLeft + 3); - - std::vector dummyNormals; - - float backgroundRGBA[4]; + uint8_t backgroundRGBA[4]; box->getBackgroundColorRGBA(backgroundRGBA); - float foregroundRGBA[4]; + uint8_t foregroundRGBA[4]; box->getLineColorRGBA(foregroundRGBA); - const bool drawBackgroundFlag = (backgroundRGBA[3] > 0.0); - const bool drawForegroundFlag = (foregroundRGBA[3] > 0.0); + const bool drawBackgroundFlag = (backgroundRGBA[3] > 0); + const bool drawForegroundFlag = (foregroundRGBA[3] > 0); const bool drawAnnotationFlag = (drawBackgroundFlag || drawForegroundFlag); bool drawnFlag = false; @@ -1211,19 +1172,24 @@ BrainOpenGLAnnotationDrawingFixedPipeline::drawBox(AnnotationFile* annotationFil * since it is opaque and prevents "behind" annotations * from being selected */ - BrainOpenGLPrimitiveDrawing::drawPolygon(coords, - dummyNormals, - selectionColorRGBA); + GraphicsShape::drawBoxFilledByteColor(bottomLeft, + bottomRight, + topRight, + topLeft, + selectionColorRGBA); } else { /* * Drawing foreground as line will still allow user to * select annotation that are inside of the box */ - const float slightlyThicker = 2.0; - BrainOpenGLPrimitiveDrawing::drawLineLoop(coords, - selectionColorRGBA, - outlineWidth + slightlyThicker); + GraphicsShape::drawBoxOutlineByteColor(bottomLeft, + bottomRight, + topRight, + topLeft, + selectionColorRGBA, + GraphicsPrimitive::LineWidthType::PERCENTAGE_VIEWPORT_HEIGHT, + box->getLineWidthPercentage()); } m_selectionInfo.push_back(SelectionInfo(annotationFile, @@ -1233,25 +1199,22 @@ BrainOpenGLAnnotationDrawingFixedPipeline::drawBox(AnnotationFile* annotationFil } else { if (drawBackgroundFlag) { - BrainOpenGLPrimitiveDrawing::drawPolygon(coords, - dummyNormals, - backgroundRGBA); + GraphicsShape::drawBoxFilledByteColor(bottomLeft, + bottomRight, + topRight, + topLeft, + backgroundRGBA); drawnFlag = true; } if (drawForegroundFlag) { - const bool drawOutlineWithPolygonsFlag = true; - if (drawOutlineWithPolygonsFlag) { - BrainOpenGLPrimitiveDrawing::drawRectangleOutline(bottomLeft, bottomRight, topRight, topLeft, - outlineWidth, - foregroundRGBA); - } - else { - const float tempRGBA[4] = { 1.0, 1.0, 0.0, 1.0 }; - BrainOpenGLPrimitiveDrawing::drawLineLoop(coords, - tempRGBA, //foregroundRGBA, - outlineWidth); - } + GraphicsShape::drawBoxOutlineByteColor(bottomLeft, + bottomRight, + topRight, + topLeft, + foregroundRGBA, + GraphicsPrimitive::LineWidthType::PERCENTAGE_VIEWPORT_HEIGHT, + box->getLineWidthPercentage()); drawnFlag = true; } } @@ -1262,7 +1225,7 @@ BrainOpenGLAnnotationDrawingFixedPipeline::drawBox(AnnotationFile* annotationFil bottomRight, topRight, topLeft, - outlineWidth, + s_sizingHandleLineWidthInPixels, box->getRotationAngle()); } } @@ -1287,62 +1250,45 @@ BrainOpenGLAnnotationDrawingFixedPipeline::drawColorBar(AnnotationFile* annotati CaretAssert(colorBar); CaretAssert(colorBar->getType() == AnnotationTypeEnum::COLOR_BAR); - float windowXYZ[3]; - if ( ! getAnnotationWindowCoordinate(colorBar->getCoordinate(), - colorBar->getCoordinateSpace(), + float annXYZ[3]; + if ( ! getAnnotationDrawingSpaceCoordinate(colorBar, + colorBar->getCoordinate(), NULL, - windowXYZ)) { + annXYZ)) { return; } -// /* -// * NO SELECTION OF COLOR BAR ANNOTATIONS -// */ -// if (m_selectionModeFlag) { -// return; -// } - float bottomLeft[3]; float bottomRight[3]; float topRight[3]; float topLeft[3]; - if ( ! getAnnotationTwoDimShapeBounds(colorBar, windowXYZ, + if ( ! getAnnotationTwoDimShapeBounds(colorBar, annXYZ, bottomLeft, bottomRight, topRight, topLeft)) { return; } const float selectionCenterXYZ[3] = { - (bottomLeft[0] + bottomRight[0] + topRight[0] + topLeft[0]) / 4.0, - (bottomLeft[1] + bottomRight[1] + topRight[1] + topLeft[1]) / 4.0, - (bottomLeft[2] + bottomRight[2] + topRight[2] + topLeft[2]) / 4.0 + (bottomLeft[0] + bottomRight[0] + topRight[0] + topLeft[0]) / 4.0f, + (bottomLeft[1] + bottomRight[1] + topRight[1] + topLeft[1]) / 4.0f, + (bottomLeft[2] + bottomRight[2] + topRight[2] + topLeft[2]) / 4.0f }; - const float outlineWidth = colorBar->getLineWidth(); - - const bool depthTestFlag = isDrawnWithDepthTesting(colorBar); + const bool depthTestFlag = isDrawnWithDepthTesting(colorBar, + NULL); const bool savedDepthTestStatus = setDepthTestingStatus(depthTestFlag); - std::vector coords; - coords.insert(coords.end(), bottomLeft, bottomLeft + 3); - coords.insert(coords.end(), bottomRight, bottomRight + 3); - coords.insert(coords.end(), topRight, topRight + 3); - coords.insert(coords.end(), topLeft, topLeft + 3); - - std::vector dummyNormals; - float backgroundRGBA[4]; colorBar->getBackgroundColorRGBA(backgroundRGBA); float foregroundRGBA[4]; colorBar->getLineColorRGBA(foregroundRGBA); - const bool drawBackgroundFlag = (backgroundRGBA[3] > 0.0); + const bool drawBackgroundFlag = (backgroundRGBA[3] > 0.0f); if (m_selectionModeFlag) { uint8_t selectionColorRGBA[4]; getIdentificationColor(selectionColorRGBA); - BrainOpenGLPrimitiveDrawing::drawPolygon(coords, - dummyNormals, - selectionColorRGBA); + GraphicsShape::drawBoxFilledByteColor(bottomLeft, bottomRight, topRight, topLeft, + selectionColorRGBA); m_selectionInfo.push_back(SelectionInfo(annotationFile, colorBar, AnnotationSizingHandleTypeEnum::ANNOTATION_SIZING_HANDLE_NONE, @@ -1368,9 +1314,8 @@ BrainOpenGLAnnotationDrawingFixedPipeline::drawColorBar(AnnotationFile* annotati bgCoords.insert(bgCoords.end(), bgTopRight, bgTopRight + 3); bgCoords.insert(bgCoords.end(), bgTopLeft, bgTopLeft + 3); - BrainOpenGLPrimitiveDrawing::drawPolygon(bgCoords, - dummyNormals, - backgroundRGBA); + GraphicsShape::drawBoxFilledFloatColor(bgBottomLeft, bgBottomRight, bgTopRight, bgTopLeft, + backgroundRGBA); } /* @@ -1387,8 +1332,8 @@ BrainOpenGLAnnotationDrawingFixedPipeline::drawColorBar(AnnotationFile* annotati float ticksMarksHeightPercent = totalHeightPercent * 0.10; float sectionsHeightPercent = totalHeightPercent - (ticksMarksHeightPercent - textHeightPercent); - if (sectionsHeightPercent <= 0.0) { - sectionsHeightPercent = totalHeightPercent * 0.10; + if (sectionsHeightPercent <= 0.0f) { + sectionsHeightPercent = totalHeightPercent * 0.10f; textHeightPercent = totalHeightPercent - sectionsHeightPercent; } @@ -1398,12 +1343,12 @@ BrainOpenGLAnnotationDrawingFixedPipeline::drawColorBar(AnnotationFile* annotati * Text is aligned at the top of the characters */ const float totalHeightPixels = (viewportHeight - * (totalHeightPercent / 100.0)); + * (totalHeightPercent / 100.0f)); const float textOffsetFromTopPixels = 2; const float textHeightPixels = (viewportHeight - * (textHeightPercent / 100.0)); + * (textHeightPercent / 100.0f)); const float tickMarksHeightPixels = (viewportHeight - * (ticksMarksHeightPercent / 100.0)); + * (ticksMarksHeightPercent / 100.0f)); const float sectionsHeightPixels = (totalHeightPixels - (textHeightPixels + textOffsetFromTopPixels @@ -1455,7 +1400,7 @@ BrainOpenGLAnnotationDrawingFixedPipeline::drawColorBar(AnnotationFile* annotati bottomRight, topRight, topLeft, - outlineWidth, + s_sizingHandleLineWidthInPixels, colorBar->getRotationAngle()); } @@ -1532,29 +1477,20 @@ BrainOpenGLAnnotationDrawingFixedPipeline::drawColorBarTickMarks(const Annotatio const float xTopRight = (xBottomRight + (bottomToTopUnitVector[0] * tickMarksHeightInPixels)); const float yTopRight = (yBottomRight + (bottomToTopUnitVector[1] * tickMarksHeightInPixels)); - std::vector colorBarLines; - float rgba[4]; colorBar->getTextColorRGBA(rgba); const float z = 0.0; - + std::unique_ptr linesPrimitive(GraphicsPrimitive::newPrimitiveV3f(GraphicsPrimitive::PrimitiveType::POLYGONAL_LINES, + rgba)); /* * Horizontal line at top of tick marks */ const bool showHorizontalLineFlag = false; if (showHorizontalLineFlag) { - std::vector lineCoords; - lineCoords.push_back(xTopLeft); - lineCoords.push_back(yTopLeft); - lineCoords.push_back(z); - lineCoords.push_back(xTopRight); - lineCoords.push_back(yTopRight); - lineCoords.push_back(z); - - colorBarLines.push_back(ColorBarLine(lineCoords, - rgba)); + linesPrimitive->addVertex(xTopLeft, yTopLeft, z); + linesPrimitive->addVertex(xTopRight, yTopRight, z); } float leftToRightVector[3]; @@ -1578,16 +1514,8 @@ BrainOpenGLAnnotationDrawingFixedPipeline::drawColorBarTickMarks(const Annotatio const float tickBottomX = xBottomLeft + (dx * scalar); const float tickBottomY = yBottomLeft + (dy * scalar); - std::vector lineCoords; - lineCoords.push_back(tickTopX); - lineCoords.push_back(tickTopY); - lineCoords.push_back(z); - lineCoords.push_back(tickBottomX); - lineCoords.push_back(tickBottomY); - lineCoords.push_back(z); - - colorBarLines.push_back(ColorBarLine(lineCoords, - rgba)); + linesPrimitive->addVertex(tickTopX, tickTopY, z); + linesPrimitive->addVertex(tickBottomX, tickBottomY, z); } /* @@ -1595,14 +1523,8 @@ BrainOpenGLAnnotationDrawingFixedPipeline::drawColorBarTickMarks(const Annotatio */ glPolygonOffset(1.0, 1.0); glEnable(GL_POLYGON_OFFSET_LINE); - for (std::vector::iterator iter = colorBarLines.begin(); - iter != colorBarLines.end(); - iter++) { - CaretAssert(iter->m_lineCoords.size() == 6); - BrainOpenGLPrimitiveDrawing::drawLines(iter->m_lineCoords, - iter->m_rgba, - tickThickness); - } + linesPrimitive->setLineWidth(GraphicsPrimitive::LineWidthType::PIXELS, tickThickness); + GraphicsEngineDataOpenGL::draw(linesPrimitive.get()); glDisable(GL_POLYGON_OFFSET_LINE); } @@ -1631,13 +1553,6 @@ BrainOpenGLAnnotationDrawingFixedPipeline::drawColorBarSections(const Annotation const float topLeft[3], const float sectionsHeightInPixels) { - std::vector normals; - for (int32_t i = 0; i < 4; i++) { - normals.push_back(0.0); - normals.push_back(0.0); - normals.push_back(1.0); - } - std::vector colorBarLines; float bottomToTopUnitVector[3]; @@ -1657,8 +1572,8 @@ BrainOpenGLAnnotationDrawingFixedPipeline::drawColorBarSections(const Annotation const float xBottomLeft = bottomLeft[0]; const float yBottomLeft = bottomLeft[1]; - float minScalar = 0.0; - float maxScalar = 0.0; + float minScalar = 0.0f; + float maxScalar = 0.0f; colorBar->getScalarMinimumAndMaximumValues(minScalar, maxScalar); const float dScalar = maxScalar - minScalar; @@ -1668,8 +1583,12 @@ BrainOpenGLAnnotationDrawingFixedPipeline::drawColorBarSections(const Annotation std::cout << qPrintable(QString("minScalar %1, maxScalar %2, dScalar %3").arg(minScalar).arg(maxScalar).arg(dScalar)) << std::endl; } - const float z = 0.0; + const float z = 0.0f; + std::unique_ptr linesPrimitive(GraphicsPrimitive::newPrimitiveV3fC4f(GraphicsPrimitive::PrimitiveType::POLYGONAL_LINES)); + linesPrimitive->setLineWidth(GraphicsPrimitive::LineWidthType::PIXELS, 1.0f); + std::unique_ptr trianglesPrimitive(GraphicsPrimitive::newPrimitiveV3fC4f(GraphicsPrimitive::PrimitiveType::OPENGL_TRIANGLES)); + const int32_t numSections = colorBar->getNumberOfSections(); for (int32_t iSect = 0; iSect < numSections; iSect++) { @@ -1691,16 +1610,10 @@ BrainOpenGLAnnotationDrawingFixedPipeline::drawColorBarSections(const Annotation const float tlY = yTopLeft + (dy * startNormalizedScalar); if (startScalar == endScalar) { - std::vector lineCoords; - lineCoords.push_back(blX); - lineCoords.push_back(blY); - lineCoords.push_back(z); - lineCoords.push_back(tlX); - lineCoords.push_back(tlY); - lineCoords.push_back(z); + const float* rgba = section->getStartRGBA(); + linesPrimitive->addVertex(blX, blY, z, rgba); + linesPrimitive->addVertex(tlX, tlY, z, rgba); - colorBarLines.push_back(ColorBarLine(lineCoords, - section->getStartRGBA())); } else { const float brX = xBottomLeft + (dx * endNormalizedScalar); @@ -1708,32 +1621,15 @@ BrainOpenGLAnnotationDrawingFixedPipeline::drawColorBarSections(const Annotation const float trX = xTopLeft + (dx * endNormalizedScalar); const float trY = yTopLeft + (dy * endNormalizedScalar); - - std::vector coords; - coords.push_back(blX); - coords.push_back(blY); - coords.push_back(z); - coords.push_back(brX); - coords.push_back(brY); - coords.push_back(z); - coords.push_back(trX); - coords.push_back(trY); - coords.push_back(z); - coords.push_back(tlX); - coords.push_back(tlY); - coords.push_back(z); - const float* rgbaLeft = section->getStartRGBA(); const float* rgbaRight = section->getEndRGBA(); - std::vector rgbaColors; - rgbaColors.insert(rgbaColors.end(), rgbaLeft, rgbaLeft + 4); - rgbaColors.insert(rgbaColors.end(), rgbaRight, rgbaRight + 4); - rgbaColors.insert(rgbaColors.end(), rgbaRight, rgbaRight + 4); - rgbaColors.insert(rgbaColors.end(), rgbaLeft, rgbaLeft + 4); - BrainOpenGLPrimitiveDrawing::drawQuads(coords, - normals, - rgbaColors); + trianglesPrimitive->addVertex(blX, blY, z, rgbaLeft); + trianglesPrimitive->addVertex(brX, brY, z, rgbaRight); + trianglesPrimitive->addVertex(tlX, tlY, z, rgbaLeft); + trianglesPrimitive->addVertex(tlX, tlY, z, rgbaLeft); + trianglesPrimitive->addVertex(brX, brY, z, rgbaRight); + trianglesPrimitive->addVertex(trX, trY, z, rgbaRight); if (printDebugFlag) { const AString msg("Section (" @@ -1762,16 +1658,14 @@ BrainOpenGLAnnotationDrawingFixedPipeline::drawColorBarSections(const Annotation * Lines need to be drawn OVER any quads (otherwise the quads * would obscure the lines */ + /* + * Lines need to be drawn OVER any quads (otherwise the quads + * would obscure the lines + */ + GraphicsEngineDataOpenGL::draw(trianglesPrimitive.get()); glPolygonOffset(1.0, 1.0); glEnable(GL_POLYGON_OFFSET_LINE); - for (std::vector::iterator iter = colorBarLines.begin(); - iter != colorBarLines.end(); - iter++) { - CaretAssert(iter->m_lineCoords.size() == 6); - BrainOpenGLPrimitiveDrawing::drawLines(iter->m_lineCoords, - iter->m_rgba, - 1.0); - } + GraphicsEngineDataOpenGL::draw(linesPrimitive.get()); glDisable(GL_POLYGON_OFFSET_LINE); } @@ -1802,17 +1696,12 @@ BrainOpenGLAnnotationDrawingFixedPipeline::drawColorBarText(const AnnotationColo const float textHeightInPixels, const float offsetFromTopInPixels) { - DisplayPropertiesAnnotation* dpa = m_inputs->m_brain->getDisplayPropertiesAnnotation(); - if ( ! dpa->isDisplayTextAnnotations()) { - return; - } - const float textPercentageHeight = (textHeightInPixels / m_modelSpaceViewport[3]) * 100.0; AnnotationPercentSizeText annText(AnnotationAttributesDefaultTypeEnum::NORMAL); annText.setVerticalAlignment(AnnotationTextAlignVerticalEnum::TOP); annText.setFont(colorBar->getFont()); - annText.setFontPercentViewportSize(textPercentageHeight); //colorBar->getFontPercentViewportSize()); + annText.setFontPercentViewportSize(textPercentageHeight); annText.setTextColor(CaretColorEnum::CUSTOM); float rgba[4]; colorBar->getTextColorRGBA(rgba); @@ -1841,6 +1730,7 @@ BrainOpenGLAnnotationDrawingFixedPipeline::drawColorBarText(const AnnotationColo const float windowZ = (bottomLeft[2] + bottomRight[2] + topRight[2] + topLeft[2]) / 4.0; const int32_t numText = colorBar->getNumberOfNumericText(); + bool fontTooSmallFlag = false; for (int32_t i = 0; i < numText; i++) { const AnnotationColorBarNumericText* numericText = colorBar->getNumericText(i); const float scalar = numericText->getScalar(); @@ -1855,7 +1745,13 @@ BrainOpenGLAnnotationDrawingFixedPipeline::drawColorBarText(const AnnotationColo windowY, windowZ, annText); + + if (annText.isFontTooSmallWhenLastDrawn()) { + fontTooSmallFlag = true; + } } + + colorBar->setFontTooSmallWhenLastDrawn(fontTooSmallFlag); } @@ -1879,12 +1775,12 @@ BrainOpenGLAnnotationDrawingFixedPipeline::drawOval(AnnotationFile* annotationFi CaretAssert(oval); CaretAssert(oval->getType() == AnnotationTypeEnum::OVAL); - float windowXYZ[3]; + float annXYZ[3]; - if ( ! getAnnotationWindowCoordinate(oval->getCoordinate(), - oval->getCoordinateSpace(), + if ( ! getAnnotationDrawingSpaceCoordinate(oval, + oval->getCoordinate(), surfaceDisplayed, - windowXYZ)) { + annXYZ)) { return false; } @@ -1892,23 +1788,27 @@ BrainOpenGLAnnotationDrawingFixedPipeline::drawOval(AnnotationFile* annotationFi float bottomRight[3]; float topRight[3]; float topLeft[3]; - if ( ! getAnnotationTwoDimShapeBounds(oval, windowXYZ, + if ( ! getAnnotationTwoDimShapeBounds(oval, annXYZ, bottomLeft, bottomRight, topRight, topLeft)) { return false; } - const float majorAxis = ((oval->getWidth() / 100.0) * (m_modelSpaceViewport[2] / 2.0)); - const float minorAxis = ((oval->getHeight() / 100.0) * (m_modelSpaceViewport[3] / 2.0)); + const float majorAxis = ((oval->getWidth() / 100.0f) * (m_modelSpaceViewport[2] / 2.0f)); + const float minorAxis = ((oval->getHeight() / 100.0f) * (m_modelSpaceViewport[3] / 2.0f)); const float rotationAngle = oval->getRotationAngle(); - const float outlineWidth = oval->getLineWidth(); + + if (oval->getLineWidthPercentage() <= 0.0) { + convertObsoleteLineWidthPixelsToPercentageWidth(oval); + } const float selectionCenterXYZ[3] = { - (bottomLeft[0] + bottomRight[0] + topRight[0] + topLeft[0]) / 4.0, - (bottomLeft[1] + bottomRight[1] + topRight[1] + topLeft[1]) / 4.0, - (bottomLeft[2] + bottomRight[2] + topRight[2] + topLeft[2]) / 4.0 + (bottomLeft[0] + bottomRight[0] + topRight[0] + topLeft[0]) / 4.0f, + (bottomLeft[1] + bottomRight[1] + topRight[1] + topLeft[1]) / 4.0f, + (bottomLeft[2] + bottomRight[2] + topRight[2] + topLeft[2]) / 4.0f }; - const bool depthTestFlag = isDrawnWithDepthTesting(oval); + const bool depthTestFlag = isDrawnWithDepthTesting(oval, + surfaceDisplayed); const bool savedDepthTestStatus = setDepthTestingStatus(depthTestFlag); uint8_t backgroundRGBA[4]; @@ -1916,17 +1816,17 @@ BrainOpenGLAnnotationDrawingFixedPipeline::drawOval(AnnotationFile* annotationFi uint8_t foregroundRGBA[4]; oval->getLineColorRGBA(foregroundRGBA); - const bool drawBackgroundFlag = (backgroundRGBA[3] > 0.0); - const bool drawForegroundFlag = (foregroundRGBA[3] > 0.0); + const bool drawBackgroundFlag = (backgroundRGBA[3] > 0); + const bool drawForegroundFlag = (foregroundRGBA[3] > 0); const bool drawAnnotationFlag = (drawBackgroundFlag || drawForegroundFlag); bool drawnFlag = false; if (drawAnnotationFlag) { glPushMatrix(); - glTranslatef(windowXYZ[0], windowXYZ[1], windowXYZ[2]); + glTranslatef(annXYZ[0], annXYZ[1], annXYZ[2]); if (rotationAngle != 0.0) { - glRotatef(-rotationAngle, 0.0, 0.0, 1.0); + glRotatef(-rotationAngle, 0.0f, 0.0f, 1.0f); } if (m_selectionModeFlag) { @@ -1939,20 +1839,20 @@ BrainOpenGLAnnotationDrawingFixedPipeline::drawOval(AnnotationFile* annotationFi * since it is opaque and prevents "behind" annotations * from being selected */ - m_brainOpenGLFixedPipeline->drawEllipseFilled(selectionColorRGBA, - majorAxis, - minorAxis); + GraphicsShape::drawEllipseFilledByteColor(majorAxis * 2.0f, + minorAxis * 2.0f, + selectionColorRGBA); } else { /* * Drawing foreground as line will still allow user to * select annotation that are inside of the box */ - const float slightlyThicker = 2.0; - m_brainOpenGLFixedPipeline->drawEllipseOutline(selectionColorRGBA, - majorAxis, - minorAxis, - outlineWidth + slightlyThicker); + GraphicsShape::drawEllipseOutlineByteColor(majorAxis * 2.0f, + minorAxis * 2.0f, + selectionColorRGBA, + GraphicsPrimitive::LineWidthType::PERCENTAGE_VIEWPORT_HEIGHT, + oval->getLineWidthPercentage()); } m_selectionInfo.push_back(SelectionInfo(annotationFile, @@ -1962,17 +1862,18 @@ BrainOpenGLAnnotationDrawingFixedPipeline::drawOval(AnnotationFile* annotationFi } else { if (drawBackgroundFlag) { - m_brainOpenGLFixedPipeline->drawEllipseFilled(backgroundRGBA, - majorAxis, - minorAxis); + GraphicsShape::drawEllipseFilledByteColor(majorAxis * 2.0f, + minorAxis * 2.0f, + backgroundRGBA); drawnFlag = true; } if (drawForegroundFlag) { - m_brainOpenGLFixedPipeline->drawEllipseOutline(foregroundRGBA, - majorAxis, - minorAxis, - outlineWidth); + GraphicsShape::drawEllipseOutlineByteColor(majorAxis * 2.0f, + minorAxis * 2.0f, + foregroundRGBA, + GraphicsPrimitive::LineWidthType::PERCENTAGE_VIEWPORT_HEIGHT, + oval->getLineWidthPercentage()); drawnFlag = true; } } @@ -1985,7 +1886,7 @@ BrainOpenGLAnnotationDrawingFixedPipeline::drawOval(AnnotationFile* annotationFi bottomRight, topRight, topLeft, - outlineWidth, + s_sizingHandleLineWidthInPixels, oval->getRotationAngle()); } } @@ -2012,9 +1913,11 @@ BrainOpenGLAnnotationDrawingFixedPipeline::getTextLineToBrainordinateLineCoordin const float bottomRight[3], const float topRight[3], const float topLeft[3], - std::vector& lineCoordinatesOut) const + std::vector& lineCoordinatesOut, + std::vector& arrowCoordinatesOut) const { lineCoordinatesOut.clear(); + arrowCoordinatesOut.clear(); if (text->isConnectToBrainordinateValid()) { bool showLineFlag = false; @@ -2058,15 +1961,15 @@ BrainOpenGLAnnotationDrawingFixedPipeline::getTextLineToBrainordinateLineCoordin AnnotationSurfaceOffsetVectorTypeEnum::CENTROID_THRU_VERTEX); float brainordinateXYZ[3]; - if (getAnnotationWindowCoordinate(&noOffsetCoord, - text->getCoordinateSpace(), + if (getAnnotationDrawingSpaceCoordinate(text, + &noOffsetCoord, surfaceDisplayed, brainordinateXYZ)) { - float windowXYZ[3]; - if (getAnnotationWindowCoordinate(coord, - text->getCoordinateSpace(), + float annXYZ[3]; + if (getAnnotationDrawingSpaceCoordinate(text, + coord, surfaceDisplayed, - windowXYZ)) { + annXYZ)) { const bool clipAtBoxFlag = true; if (clipAtBoxFlag) { @@ -2075,15 +1978,23 @@ BrainOpenGLAnnotationDrawingFixedPipeline::getTextLineToBrainordinateLineCoordin topRight, topLeft, brainordinateXYZ, - windowXYZ); + annXYZ); } - createLineCoordinates(windowXYZ, + if (text->getLineWidthPercentage() <= 0.0) { + convertObsoleteLineWidthPixelsToPercentageWidth(text); + } + const float lineWidth = getLineWidthFromPercentageHeight(text->getLineWidthPercentage()); + std::vector unusedArrowCoordinates; + createLineCoordinates(annXYZ, brainordinateXYZ, - text->getLineWidth(), + lineWidth, false, showArrowFlag, - lineCoordinatesOut); + lineCoordinatesOut, + unusedArrowCoordinates, + arrowCoordinatesOut); + } } } @@ -2145,12 +2056,21 @@ BrainOpenGLAnnotationDrawingFixedPipeline::clipLineAtTextBox(const float bottomL break; } + /* + * perform a 2D intersection test + */ float intersection[3] = { 0.0, 0.0, 0.0 }; const bool yesFlag = MathFunctions::lineIntersection2D(p1, p2, startXYZ, endXYZ, tol, intersection); if (yesFlag) { + /* + * Intersection is TWO-D so must set + * the correct Z-coordinate + */ + const float averageZ = ((p1[2] + p2[2]) / 2.0f); + intersection[2] = averageZ; const float dist = MathFunctions::distance3D(startXYZ, intersection); if ( ( ! clippedValid) @@ -2188,19 +2108,25 @@ BrainOpenGLAnnotationDrawingFixedPipeline::drawText(AnnotationFile* annotationFi AnnotationText* text, const Surface* surfaceDisplayed) { + CaretAssert(text); + CaretAssert(text->getType() == AnnotationTypeEnum::TEXT); + + /* + * Annotations with "DISPLAY_GROUP" propery may be turned on/off by user. + */ DisplayPropertiesAnnotation* dpa = m_inputs->m_brain->getDisplayPropertiesAnnotation(); - if ( ! dpa->isDisplayTextAnnotations()) { - return false; + if (text->testProperty(Annotation::Property::DISPLAY_GROUP)) { + if ( ! dpa->isDisplayTextAnnotations()) { + return false; + } } - CaretAssert(text); - CaretAssert(text->getType() == AnnotationTypeEnum::TEXT); - float windowXYZ[3]; - if ( ! getAnnotationWindowCoordinate(text->getCoordinate(), - text->getCoordinateSpace(), + float annXYZ[3]; + if ( ! getAnnotationDrawingSpaceCoordinate(text, + text->getCoordinate(), surfaceDisplayed, - windowXYZ)) { + annXYZ)) { return false; } @@ -2221,49 +2147,19 @@ BrainOpenGLAnnotationDrawingFixedPipeline::drawText(AnnotationFile* annotationFi float savedFontPercentViewportHeight = -1.0; const bool modifiedStatus = text->isModified(); - const bool allowDifferentHeightModesFlag = false; - if (allowDifferentHeightModesFlag) { - switch (m_inputs->m_textHeightMode) { - case Inputs::TEXT_HEIGHT_USE_OPENGL_VIEWPORT_HEIGHT: - break; - case Inputs::TEXT_HEIGHT_USE_TAB_VIEWPORT_HEIGHT: - if (m_inputs->m_tabViewport[3] != m_modelSpaceViewport[3]) { - percentSizeText = dynamic_cast(text); - if (percentSizeText != NULL) { - savedFontPercentViewportHeight = percentSizeText->getFontPercentViewportSize(); - if (savedFontPercentViewportHeight > 0.0) { - CaretAssert(m_modelSpaceViewport[3]); - const float heightScaling = m_inputs->m_tabViewport[3] / m_modelSpaceViewport[3]; - percentSizeText->setFontPercentViewportSize(savedFontPercentViewportHeight - * heightScaling); - } - } - } - break; - } - } - m_brainOpenGLFixedPipeline->getTextRenderer()->getBoundsForTextAtViewportCoords(*text, - windowXYZ[0], windowXYZ[1], windowXYZ[2], - //textDrawingViewportHeight, - m_modelSpaceViewport[3], + annXYZ[0], annXYZ[1], annXYZ[2], + m_modelSpaceViewport[2], m_modelSpaceViewport[3], bottomLeft, bottomRight, topRight, topLeft); - - std::vector coords; - coords.insert(coords.end(), bottomLeft, bottomLeft + 3); - coords.insert(coords.end(), bottomRight, bottomRight + 3); - coords.insert(coords.end(), topRight, topRight + 3); - coords.insert(coords.end(), topLeft, topLeft + 3); - - std::vector dummyNormals; const float selectionCenterXYZ[3] = { - (bottomLeft[0] + bottomRight[0] + topRight[0] + topLeft[0]) / 4.0, - (bottomLeft[1] + bottomRight[1] + topRight[1] + topLeft[1]) / 4.0, - (bottomLeft[2] + bottomRight[2] + topRight[2] + topLeft[2]) / 4.0 + (bottomLeft[0] + bottomRight[0] + topRight[0] + topLeft[0]) / 4.0f, + (bottomLeft[1] + bottomRight[1] + topRight[1] + topLeft[1]) / 4.0f, + (bottomLeft[2] + bottomRight[2] + topRight[2] + topLeft[2]) / 4.0f }; - const bool depthTestFlag = isDrawnWithDepthTesting(text); + const bool depthTestFlag = isDrawnWithDepthTesting(text, + surfaceDisplayed); const bool savedDepthTestStatus = setDepthTestingStatus(depthTestFlag); float backgroundRGBA[4]; @@ -2273,8 +2169,8 @@ BrainOpenGLAnnotationDrawingFixedPipeline::drawText(AnnotationFile* annotationFi uint8_t textColorRGBA[4]; text->getTextColorRGBA(textColorRGBA); - const bool drawTextFlag = (textColorRGBA[3] > 0.0); - const bool drawBackgroundFlag = (backgroundRGBA[3] > 0.0); + const bool drawTextFlag = (textColorRGBA[3] > 0); + const bool drawBackgroundFlag = (backgroundRGBA[3] > 0.0f); const bool drawAnnotationFlag = (drawBackgroundFlag || drawTextFlag); bool drawnFlag = false; @@ -2283,9 +2179,8 @@ BrainOpenGLAnnotationDrawingFixedPipeline::drawText(AnnotationFile* annotationFi if (m_selectionModeFlag) { uint8_t selectionColorRGBA[4]; getIdentificationColor(selectionColorRGBA); - BrainOpenGLPrimitiveDrawing::drawPolygon(coords, - dummyNormals, - selectionColorRGBA); + GraphicsShape::drawBoxFilledByteColor(bottomLeft, bottomRight, topRight, topLeft, + selectionColorRGBA); m_selectionInfo.push_back(SelectionInfo(annotationFile, text, AnnotationSizingHandleTypeEnum::ANNOTATION_SIZING_HANDLE_NONE, @@ -2293,6 +2188,7 @@ BrainOpenGLAnnotationDrawingFixedPipeline::drawText(AnnotationFile* annotationFi } else { std::vector connectLineCoordinates; + std::vector arrowCoordinates; getTextLineToBrainordinateLineCoordinates(text, surfaceDisplayed, @@ -2300,18 +2196,24 @@ BrainOpenGLAnnotationDrawingFixedPipeline::drawText(AnnotationFile* annotationFi bottomRight, topRight, topLeft, - connectLineCoordinates); + connectLineCoordinates, + arrowCoordinates); if ( ! connectLineCoordinates.empty()) { - BrainOpenGLPrimitiveDrawing::drawLines(connectLineCoordinates, - textColorRGBA, - text->getLineWidth()); - } - - if (drawBackgroundFlag) { - BrainOpenGLPrimitiveDrawing::drawPolygon(coords, - dummyNormals, - backgroundRGBA); + if (text->getLineWidthPercentage() <= 0.0) { + convertObsoleteLineWidthPixelsToPercentageWidth(text); + } + + GraphicsShape::drawLinesByteColor(connectLineCoordinates, + textColorRGBA, + GraphicsPrimitive::LineWidthType::PERCENTAGE_VIEWPORT_HEIGHT, + text->getLineWidthPercentage()); + if ( ! arrowCoordinates.empty()) { + GraphicsShape::drawLineStripMiterJoinByteColor(arrowCoordinates, + textColorRGBA, + GraphicsPrimitive::LineWidthType::PERCENTAGE_VIEWPORT_HEIGHT, + text->getLineWidthPercentage()); + } } if (drawTextFlag) { @@ -2321,15 +2223,15 @@ BrainOpenGLAnnotationDrawingFixedPipeline::drawText(AnnotationFile* annotationFi AString msg("Drawing Text: " + text->getText() + " DepthTest=" + AString::fromBool(depthTestFlag)); const float* xyz = text->getCoordinate()->getXYZ(); msg.appendWithNewLine(" Stereotaxic: " + AString::fromNumbers(xyz, 3, ",")); - msg.appendWithNewLine(" Window: " + AString::fromNumbers(windowXYZ, 3, ",")); + msg.appendWithNewLine(" Drawing Space: " + AString::fromNumbers(annXYZ, 3, ",")); std::cout << qPrintable(msg) << std::endl; } } if (depthTestFlag) { - m_brainOpenGLFixedPipeline->getTextRenderer()->drawTextAtViewportCoords(windowXYZ[0], - windowXYZ[1], - windowXYZ[2], + m_brainOpenGLFixedPipeline->getTextRenderer()->drawTextAtViewportCoords(annXYZ[0], + annXYZ[1], + annXYZ[2], *text); drawnFlag = true; } @@ -2344,23 +2246,16 @@ BrainOpenGLAnnotationDrawingFixedPipeline::drawText(AnnotationFile* annotationFi float br[3]; float tr[3]; float tl[3]; - getAnnotationTwoDimShapeBounds(text, windowXYZ, bl, br, tr, tl); - - std::vector boxCoords; - boxCoords.insert(boxCoords.end(), bl, bl + 3); - boxCoords.insert(boxCoords.end(), br, br + 3); - boxCoords.insert(boxCoords.end(), tr, tr + 3); - boxCoords.insert(boxCoords.end(), tl, tl + 3); + getAnnotationTwoDimShapeBounds(text, annXYZ, bl, br, tr, tl); - if (boxCoords.size() == 12) { - BrainOpenGLPrimitiveDrawing::drawLineLoop(boxCoords, - foregroundRGBA, - text->getLineWidth()); - } + GraphicsShape::drawBoxOutlineByteColor(bl, br, tr, tl, + foregroundRGBA, + GraphicsPrimitive::LineWidthType::PIXELS, + 2.0f); } else { - m_brainOpenGLFixedPipeline->getTextRenderer()->drawTextAtViewportCoords(windowXYZ[0], - windowXYZ[1], + m_brainOpenGLFixedPipeline->getTextRenderer()->drawTextAtViewportCoords(annXYZ[0], + annXYZ[1], *text); drawnFlag = true; } @@ -2371,20 +2266,40 @@ BrainOpenGLAnnotationDrawingFixedPipeline::drawText(AnnotationFile* annotationFi } if (text->isSelectedForEditing(m_inputs->m_windowIndex)) { - const float outlineWidth = 2.0; drawAnnotationTwoDimSizingHandles(annotationFile, text, bottomLeft, bottomRight, topRight, topLeft, - outlineWidth, + s_sizingHandleLineWidthInPixels, text->getRotationAngle()); + + /* + * Cross can be used to show the text annotation's coordinate + * to assist with placement of the annotation + */ + const bool drawCrossFlag = false; + if (drawCrossFlag) { + const float redRGBA[4] = { 1.0f, 0.0f, 0.0f, 1.0f }; + std::unique_ptr crossShape = std::unique_ptr(GraphicsPrimitive::newPrimitiveV3f(GraphicsPrimitive::PrimitiveType::OPENGL_LINES, + redRGBA)); + crossShape->addVertex(annXYZ[0], + annXYZ[1] - 10); + crossShape->addVertex(annXYZ[0], + annXYZ[1] + 10); + crossShape->addVertex(annXYZ[0] - 10, + annXYZ[1]); + crossShape->addVertex(annXYZ[0] + 10, + annXYZ[1]); + crossShape->setLineWidth(GraphicsPrimitive::LineWidthType::PIXELS, 2.0f); + GraphicsEngineDataOpenGL::draw(crossShape.get()); + } } } if (percentSizeText != NULL) { - if (savedFontPercentViewportHeight > 0.0) { + if (savedFontPercentViewportHeight > 0.0f) { percentSizeText->setFontPercentViewportSize(savedFontPercentViewportHeight); if ( ! modifiedStatus) { percentSizeText->clearModified(); @@ -2427,12 +2342,12 @@ BrainOpenGLAnnotationDrawingFixedPipeline::drawImage(AnnotationFile* annotationF CaretLogWarning("Attempt to draw invalid image annotation."); } - float windowXYZ[3]; + float annXYZ[3]; - if ( ! getAnnotationWindowCoordinate(image->getCoordinate(), - image->getCoordinateSpace(), + if ( ! getAnnotationDrawingSpaceCoordinate(image, + image->getCoordinate(), surfaceDisplayed, - windowXYZ)) { + annXYZ)) { return false; } @@ -2440,26 +2355,19 @@ BrainOpenGLAnnotationDrawingFixedPipeline::drawImage(AnnotationFile* annotationF float bottomRight[3]; float topRight[3]; float topLeft[3]; - if ( ! getAnnotationTwoDimShapeBounds(image, windowXYZ, + if ( ! getAnnotationTwoDimShapeBounds(image, annXYZ, bottomLeft, bottomRight, topRight, topLeft)) { return false; } - std::vector coords; - coords.insert(coords.end(), bottomLeft, bottomLeft + 3); - coords.insert(coords.end(), bottomRight, bottomRight + 3); - coords.insert(coords.end(), topRight, topRight + 3); - coords.insert(coords.end(), topLeft, topLeft + 3); - - std::vector dummyNormals; - const float selectionCenterXYZ[3] = { - (bottomLeft[0] + bottomRight[0] + topRight[0] + topLeft[0]) / 4.0, - (bottomLeft[1] + bottomRight[1] + topRight[1] + topLeft[1]) / 4.0, - (bottomLeft[2] + bottomRight[2] + topRight[2] + topLeft[2]) / 4.0 + (bottomLeft[0] + bottomRight[0] + topRight[0] + topLeft[0]) / 4.0f, + (bottomLeft[1] + bottomRight[1] + topRight[1] + topLeft[1]) / 4.0f, + (bottomLeft[2] + bottomRight[2] + topRight[2] + topLeft[2]) / 4.0f }; - const bool depthTestFlag = isDrawnWithDepthTesting(image); + const bool depthTestFlag = isDrawnWithDepthTesting(image, + surfaceDisplayed); const bool savedDepthTestStatus = setDepthTestingStatus(depthTestFlag); const bool drawAnnotationFlag = true; @@ -2474,9 +2382,8 @@ BrainOpenGLAnnotationDrawingFixedPipeline::drawImage(AnnotationFile* annotationF if (m_selectionModeFlag) { uint8_t selectionColorRGBA[4]; getIdentificationColor(selectionColorRGBA); - BrainOpenGLPrimitiveDrawing::drawPolygon(coords, - dummyNormals, - selectionColorRGBA); + GraphicsShape::drawBoxFilledByteColor(bottomLeft, bottomRight, topRight, topLeft, + selectionColorRGBA); m_selectionInfo.push_back(SelectionInfo(annotationFile, image, AnnotationSizingHandleTypeEnum::ANNOTATION_SIZING_HANDLE_NONE, @@ -2489,39 +2396,43 @@ BrainOpenGLAnnotationDrawingFixedPipeline::drawImage(AnnotationFile* annotationF AString msg("Drawing Image: DepthTest=" + AString::fromBool(depthTestFlag)); const float* xyz = image->getCoordinate()->getXYZ(); msg.appendWithNewLine(" Stereotaxic: " + AString::fromNumbers(xyz, 3, ",")); - msg.appendWithNewLine(" Window: " + AString::fromNumbers(windowXYZ, 3, ",")); + msg.appendWithNewLine(" Drawing Space: " + AString::fromNumbers(annXYZ, 3, ",")); std::cout << qPrintable(msg) << std::endl; } } - drawImageBytesWithTexture(image->getDrawWithOpenGLTextureInfo(), - bottomLeft, - bottomRight, - topRight, - topLeft, - imageRgbaBytes, - imageWidth, - imageHeight); - drawnFlag = true; - + image->setVertexBounds(bottomLeft, + bottomRight, + topRight, + topLeft); + GraphicsPrimitiveV3fT3f* primitive = image->getGraphicsPrimitive(); + if (primitive != NULL) { + if (primitive->isValid()) { + GraphicsEngineDataOpenGL::draw(primitive); + } + drawnFlag = true; + } + if (drawForegroundFlag) { - BrainOpenGLPrimitiveDrawing::drawLineLoop(coords, - foregroundRGBA, - image->getLineWidth()); + if (image->getLineWidthPercentage() <= 0.0f) { + convertObsoleteLineWidthPixelsToPercentageWidth(image); + } + GraphicsShape::drawBoxOutlineFloatColor(bottomLeft, bottomRight, topRight, topLeft, + foregroundRGBA, + GraphicsPrimitive::LineWidthType::PERCENTAGE_VIEWPORT_HEIGHT, image->getLineWidthPercentage()); } setDepthTestingStatus(depthTestFlag); } if (image->isSelectedForEditing(m_inputs->m_windowIndex)) { - const float outlineWidth = 2.0; drawAnnotationTwoDimSizingHandles(annotationFile, image, bottomLeft, bottomRight, topRight, topLeft, - outlineWidth, + s_sizingHandleLineWidthInPixels, image->getRotationAngle()); } } @@ -2531,167 +2442,6 @@ BrainOpenGLAnnotationDrawingFixedPipeline::drawImage(AnnotationFile* annotationF return drawnFlag; } - -/** - * Draw image annoation using a textured quad. - * - * @param textureInfo - * Information for OpenGL texture usage. - * @param bottomLeft - * Bottom left corner of annotation. - * @param bottomRight - * Bottom right corner of annotation. - * @param topRight - * Top right corner of annotation. - * @param topLeft - * Top left corner of annotation. - * @param imageBytesRGBA - * Bytes containing the image data. - * @param imageWidth - * Width of the actual image. - * @param imageHeight - * Height of the image. - */ -void -BrainOpenGLAnnotationDrawingFixedPipeline::drawImageBytesWithTexture(DrawnWithOpenGLTextureInfo* textureInfo, - const float bottomLeft[3], - const float bottomRight[3], - const float topRight[3], - const float topLeft[3], - const uint8_t* imageBytesRGBA, - const int32_t imageWidth, - const int32_t imageHeight) -{ - m_brainOpenGLFixedPipeline->checkForOpenGLError(NULL, ("At beginning of annotation drawImageBytesWithTexture()")); - - /* - * Saves glPixelStore parameters - */ - glPushClientAttrib(GL_CLIENT_PIXEL_STORE_BIT); - - static bool useMipMapFlag = true; - - BrainOpenGLTextureManager* textureManager = m_brainOpenGLFixedPipeline->getTextureManager(); - CaretAssert(textureManager); - - GLuint textureName = 0; - bool newTextureNameFlag = false; - textureManager->getTextureName(textureInfo, - textureName, - newTextureNameFlag); - - if (newTextureNameFlag) { - glBindTexture(GL_TEXTURE_2D, textureName); - if (useMipMapFlag) { - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP); - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP); - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR); - const int errorCode = gluBuild2DMipmaps(GL_TEXTURE_2D, // MUST BE GL_TEXTURE_2D - GL_RGBA, // number of components - imageWidth, // width of image - imageHeight, // height of image - GL_RGBA, // format of the pixel data - GL_UNSIGNED_BYTE, // data type of pixel data - imageBytesRGBA); // pointer to image data - if (errorCode != 0) { - useMipMapFlag = false; - - const GLubyte* errorChars = gluErrorString(errorCode); - if (errorChars != NULL) { - const QString errorText = ("ERROR building mipmaps for annotation image: " - + AString((char*)errorChars)); - CaretLogSevere(errorText); - } - } - } - - if ( ! useMipMapFlag) { - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP); - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP); - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); - - glTexImage2D(GL_TEXTURE_2D, // MUST BE GL_TEXTURE_2D - 0, // level of detail 0=base, n is nth mipmap reduction - GL_RGBA, // number of components - imageWidth, // width of image - imageHeight, // height of image - 0, // border - GL_RGBA, // format of the pixel data - GL_UNSIGNED_BYTE, // data type of pixel data - imageBytesRGBA); // pointer to image data - } - } - - glEnable(GL_TEXTURE_2D); - glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE); - glBindTexture(GL_TEXTURE_2D, textureName); - - glBegin(GL_QUADS); - glTexCoord2f(0.0, 0.0); - glVertex3fv(bottomLeft); - glTexCoord2f(1.0, 0.0); - glVertex3fv(bottomRight); - glTexCoord2f(1.0, 1.0); - glVertex3fv(topRight); - glTexCoord2f(0.0, 1.0); - glVertex3fv(topLeft); - glEnd(); - - /* - * - */ - glBindTexture(GL_TEXTURE_2D, 0); - - glDisable(GL_TEXTURE_2D); - - glPopClientAttrib(); - - m_brainOpenGLFixedPipeline->checkForOpenGLError(NULL, ("At end of annotation drawImageBytesWithTexture()")); -} - -/** - * Draw an image texture by drawing pixels. - * DOES NOT WORK IF BOTTOM LEFT CORNER OF IMAGE HAS A NEGATIVE VALUE !! - */ -void -BrainOpenGLAnnotationDrawingFixedPipeline::drawImageBytes(const float windowX, - const float windowY, - const float windowZ, - const uint8_t* imageBytesRGBA, - const int32_t imageWidth, - const int32_t imageHeight) -{ - /* - * Reset orthographic projection to viewport size - */ - glMatrixMode(GL_MODELVIEW); - glPushMatrix(); - glLoadIdentity(); - - const float drawX = windowX - (imageWidth / 2.0); - const float drawY = windowY - (imageHeight / 2.0); - - /* - * Saves glPixelStore parameters - */ - glPushClientAttrib(GL_CLIENT_PIXEL_STORE_BIT); - - // - // Draw image near far clipping plane - // - glRasterPos3f(drawX, drawY, windowZ); //-500.0); // set Z so image behind surface - - glDrawPixels(imageWidth, imageHeight, - GL_RGBA, GL_UNSIGNED_BYTE, - (GLvoid*)imageBytesRGBA); - - glPopClientAttrib(); - - glPopMatrix(); -} - /** * Create the coordinates for drawing a line with optional arrows at the end points. * @@ -2711,104 +2461,111 @@ BrainOpenGLAnnotationDrawingFixedPipeline::drawImageBytes(const float windowX, void BrainOpenGLAnnotationDrawingFixedPipeline::createLineCoordinates(const float lineHeadXYZ[3], const float lineTailXYZ[3], - const float lineThickness, + const float lineThicknessPixels, const bool validStartArrow, const bool validEndArrow, - std::vector& coordinatesOut) const + std::vector& lineCoordinatesOut, + std::vector& startArrowCoordinatesOut, + std::vector& endArrowCoordinatesOut) const { - coordinatesOut.clear(); + lineCoordinatesOut.clear(); + startArrowCoordinatesOut.clear(); + endArrowCoordinatesOut.clear(); /* * Length of arrow's tips is function of line thickness */ - const float tipScale = 3.0; - const float tipLength = lineThickness * tipScale; + const float lineLength = MathFunctions::distance3D(lineHeadXYZ, + lineTailXYZ); + const float thirdLineLength = lineLength / 3.0f; /* - * Point on arrow's line that is between the arrow's left and right arrow tips + * Vector from Tail to Head of line */ - float headToTailVector[3]; - MathFunctions::createUnitVector(lineHeadXYZ, lineTailXYZ, headToTailVector); - const float startArrowTipsOnLine[3] = { - lineHeadXYZ[0] + (headToTailVector[0] * tipLength), - lineHeadXYZ[1] + (headToTailVector[1] * tipLength), - lineHeadXYZ[2] + (headToTailVector[2] * tipLength) - }; - const float tailArrowTipsOnLine[3] = { - lineTailXYZ[0] - (headToTailVector[0] * tipLength), - lineTailXYZ[1] - (headToTailVector[1] * tipLength), - lineTailXYZ[2] - (headToTailVector[2] * tipLength) - }; + float tailToHeadUnitVector[3]; + MathFunctions::createUnitVector(lineTailXYZ, + lineHeadXYZ, + tailToHeadUnitVector); + /* - * Vector for arrow tip's on left and right - * * Create a perpendicular vector by swapping first two elements * and negating the second element. */ - float headLeftRightTipOffset[3] = { - headToTailVector[0], - headToTailVector[1], - headToTailVector[2] + const float leftToRightUnitVector[3] = { + tailToHeadUnitVector[1], + -tailToHeadUnitVector[0], + tailToHeadUnitVector[2] }; - MathFunctions::normalizeVector(headLeftRightTipOffset); - std::swap(headLeftRightTipOffset[0], - headLeftRightTipOffset[1]); - headLeftRightTipOffset[1] *= -1; - headLeftRightTipOffset[0] *= tipLength; - headLeftRightTipOffset[1] *= tipLength; - headLeftRightTipOffset[2] *= tipLength; /* - * Tip of arrow's head pointer on the right + * Left to right vector moves the arrows tips away from the line + * in a direction perpendicular to the line */ - const float headRightTipEnd[3] = { - startArrowTipsOnLine[0] - headLeftRightTipOffset[0], - startArrowTipsOnLine[1] - headLeftRightTipOffset[1], - startArrowTipsOnLine[2] - headLeftRightTipOffset[2] + const float awayFromLineScale = 3.0f; + const float awayFromLineDistance = lineThicknessPixels * awayFromLineScale; + const float leftToRightVector[3] = { + leftToRightUnitVector[0] * awayFromLineDistance, + leftToRightUnitVector[1] * awayFromLineDistance, + leftToRightUnitVector[2] }; /* - * Tip of arrow's head pointer on the left + * Away from tail vector moves the arrows tips away from the tail + * of the line in a direction along the line */ - const float headLeftTipEnd[3] = { - startArrowTipsOnLine[0] + headLeftRightTipOffset[0], - startArrowTipsOnLine[1] + headLeftRightTipOffset[1], - startArrowTipsOnLine[2] + headLeftRightTipOffset[2] + const float awayFromTipScale = 3.0f; + const float alongLineDistanceFromTip = std::min((lineThicknessPixels * awayFromTipScale), + (thirdLineLength)); + + const float awayFromTailVector[3] = { + (tailToHeadUnitVector[0] * alongLineDistanceFromTip), + (tailToHeadUnitVector[1] * alongLineDistanceFromTip), + (tailToHeadUnitVector[2]) }; - /* - * Tip of arrow tail pointer on the right - */ - const float tailRightTipEnd[3] = { - tailArrowTipsOnLine[0] + headLeftRightTipOffset[0], - tailArrowTipsOnLine[1] + headLeftRightTipOffset[1], - tailArrowTipsOnLine[2] + headLeftRightTipOffset[2] - }; - - /* - * Tip of arrow tail pointer on the right - */ - const float tailLeftTipEnd[3] = { - tailArrowTipsOnLine[0] - headLeftRightTipOffset[0], - tailArrowTipsOnLine[1] - headLeftRightTipOffset[1], - tailArrowTipsOnLine[2] - headLeftRightTipOffset[2] - }; + if (validEndArrow) { + const float tailTipRightXYZ[3] = { + lineTailXYZ[0] + leftToRightVector[0] + awayFromTailVector[0], + lineTailXYZ[1] + leftToRightVector[1] + awayFromTailVector[1], + lineTailXYZ[2] + leftToRightVector[2] + awayFromTailVector[2] + }; + const float tailTipLeftXYZ[3] = { + lineTailXYZ[0] - leftToRightVector[0] + awayFromTailVector[0], + lineTailXYZ[1] - leftToRightVector[1] + awayFromTailVector[1], + lineTailXYZ[2] + leftToRightVector[2] + awayFromTailVector[2] + }; + + endArrowCoordinatesOut.insert(endArrowCoordinatesOut.end(), + tailTipLeftXYZ, tailTipLeftXYZ + 3); + endArrowCoordinatesOut.insert(endArrowCoordinatesOut.end(), + lineTailXYZ, lineTailXYZ + 3); + endArrowCoordinatesOut.insert(endArrowCoordinatesOut.end(), + tailTipRightXYZ, tailTipRightXYZ + 3); + } - coordinatesOut.insert(coordinatesOut.end(), lineHeadXYZ, lineHeadXYZ + 3); - coordinatesOut.insert(coordinatesOut.end(), lineTailXYZ, lineTailXYZ + 3); if (validStartArrow) { - coordinatesOut.insert(coordinatesOut.end(), lineHeadXYZ, lineHeadXYZ + 3); - coordinatesOut.insert(coordinatesOut.end(), headRightTipEnd, headRightTipEnd + 3); - coordinatesOut.insert(coordinatesOut.end(), lineHeadXYZ, lineHeadXYZ + 3); - coordinatesOut.insert(coordinatesOut.end(), headLeftTipEnd, headLeftTipEnd + 3); - } - if (validEndArrow) { - coordinatesOut.insert(coordinatesOut.end(), lineTailXYZ, lineTailXYZ + 3); - coordinatesOut.insert(coordinatesOut.end(), tailRightTipEnd, tailRightTipEnd + 3); - coordinatesOut.insert(coordinatesOut.end(), lineTailXYZ, lineTailXYZ + 3); - coordinatesOut.insert(coordinatesOut.end(), tailLeftTipEnd, tailLeftTipEnd + 3); + const float headTipRightXYZ[3] = { + lineHeadXYZ[0] + leftToRightVector[0] - awayFromTailVector[0], + lineHeadXYZ[1] + leftToRightVector[1] - awayFromTailVector[1], + lineHeadXYZ[2] + leftToRightVector[2] - awayFromTailVector[2] + }; + const float headTipLeftXYZ[3] = { + lineHeadXYZ[0] - leftToRightVector[0] - awayFromTailVector[0], + lineHeadXYZ[1] - leftToRightVector[1] - awayFromTailVector[1], + lineHeadXYZ[2] + leftToRightVector[2] - awayFromTailVector[2] + }; + + startArrowCoordinatesOut.insert(startArrowCoordinatesOut.end(), + headTipLeftXYZ, headTipLeftXYZ + 3); + startArrowCoordinatesOut.insert(startArrowCoordinatesOut.end(), + lineHeadXYZ, lineHeadXYZ + 3); + startArrowCoordinatesOut.insert(startArrowCoordinatesOut.end(), + headTipRightXYZ, headTipRightXYZ + 3); } + + lineCoordinatesOut.insert(lineCoordinatesOut.end(), lineHeadXYZ, lineHeadXYZ + 3); + lineCoordinatesOut.insert(lineCoordinatesOut.end(), lineTailXYZ, lineTailXYZ + 3); } /** @@ -2834,42 +2591,50 @@ BrainOpenGLAnnotationDrawingFixedPipeline::drawLine(AnnotationFile* annotationFi float lineHeadXYZ[3]; float lineTailXYZ[3]; - if ( ! getAnnotationWindowCoordinate(line->getStartCoordinate(), - line->getCoordinateSpace(), + if ( ! getAnnotationDrawingSpaceCoordinate(line, + line->getStartCoordinate(), surfaceDisplayed, lineHeadXYZ)) { return false; } - if ( ! getAnnotationWindowCoordinate(line->getEndCoordinate(), - line->getCoordinateSpace(), + if ( ! getAnnotationDrawingSpaceCoordinate(line, + line->getEndCoordinate(), surfaceDisplayed, lineTailXYZ)) { return false; } - const float lineWidth = line->getLineWidth(); - const float backgroundLineWidth = lineWidth + 4; + + if (line->getLineWidthPercentage() <= 0.0) { + convertObsoleteLineWidthPixelsToPercentageWidth(line); + } + const float lineWidth = getLineWidthFromPercentageHeight(line->getLineWidthPercentage()); const float selectionCenterXYZ[3] = { - (lineHeadXYZ[0] + lineTailXYZ[0]) / 2.0, - (lineHeadXYZ[1] + lineTailXYZ[1]) / 2.0, - (lineHeadXYZ[2] + lineTailXYZ[2]) / 2.0 + (lineHeadXYZ[0] + lineTailXYZ[0]) / 2.0f, + (lineHeadXYZ[1] + lineTailXYZ[1]) / 2.0f, + (lineHeadXYZ[2] + lineTailXYZ[2]) / 2.0f }; - const bool depthTestFlag = isDrawnWithDepthTesting(line); + const bool depthTestFlag = isDrawnWithDepthTesting(line, + surfaceDisplayed); const bool savedDepthTestStatus = setDepthTestingStatus(depthTestFlag); - std::vector coords; + std::vector lineCoordinates; + std::vector startArrowCoordinates; + std::vector endArrowCoordinates; createLineCoordinates(lineHeadXYZ, lineTailXYZ, - line->getLineWidth(), + lineWidth, line->isDisplayStartArrow(), line->isDisplayEndArrow(), - coords); + lineCoordinates, + startArrowCoordinates, + endArrowCoordinates); uint8_t foregroundRGBA[4]; line->getLineColorRGBA(foregroundRGBA); - const bool drawForegroundFlag = (foregroundRGBA[3] > 0.0); + const bool drawForegroundFlag = (foregroundRGBA[3] > 0.0f); bool drawnFlag = false; @@ -2877,9 +2642,23 @@ BrainOpenGLAnnotationDrawingFixedPipeline::drawLine(AnnotationFile* annotationFi if (m_selectionModeFlag) { uint8_t selectionColorRGBA[4]; getIdentificationColor(selectionColorRGBA); - BrainOpenGLPrimitiveDrawing::drawLines(coords, - selectionColorRGBA, - backgroundLineWidth); + + GraphicsShape::drawLinesByteColor(lineCoordinates, + selectionColorRGBA, + GraphicsPrimitive::LineWidthType::PERCENTAGE_VIEWPORT_HEIGHT, + line->getLineWidthPercentage()); + if ( ! startArrowCoordinates.empty()) { + GraphicsShape::drawLineStripMiterJoinByteColor(startArrowCoordinates, + selectionColorRGBA, + GraphicsPrimitive::LineWidthType::PERCENTAGE_VIEWPORT_HEIGHT, + line->getLineWidthPercentage()); + } + if ( ! endArrowCoordinates.empty()) { + GraphicsShape::drawLineStripMiterJoinByteColor(endArrowCoordinates, + selectionColorRGBA, + GraphicsPrimitive::LineWidthType::PERCENTAGE_VIEWPORT_HEIGHT, + line->getLineWidthPercentage()); + } m_selectionInfo.push_back(SelectionInfo(annotationFile, line, AnnotationSizingHandleTypeEnum::ANNOTATION_SIZING_HANDLE_NONE, @@ -2887,9 +2666,22 @@ BrainOpenGLAnnotationDrawingFixedPipeline::drawLine(AnnotationFile* annotationFi } else { if (drawForegroundFlag) { - BrainOpenGLPrimitiveDrawing::drawLines(coords, - foregroundRGBA, - lineWidth); + GraphicsShape::drawLinesByteColor(lineCoordinates, + foregroundRGBA, + GraphicsPrimitive::LineWidthType::PERCENTAGE_VIEWPORT_HEIGHT, + line->getLineWidthPercentage()); + if ( ! startArrowCoordinates.empty()) { + GraphicsShape::drawLineStripMiterJoinByteColor(startArrowCoordinates, + foregroundRGBA, + GraphicsPrimitive::LineWidthType::PERCENTAGE_VIEWPORT_HEIGHT, + line->getLineWidthPercentage()); + } + if ( ! endArrowCoordinates.empty()) { + GraphicsShape::drawLineStripMiterJoinByteColor(endArrowCoordinates, + foregroundRGBA, + GraphicsPrimitive::LineWidthType::PERCENTAGE_VIEWPORT_HEIGHT, + line->getLineWidthPercentage()); + } drawnFlag = true; } } @@ -2899,7 +2691,7 @@ BrainOpenGLAnnotationDrawingFixedPipeline::drawLine(AnnotationFile* annotationFi line, lineHeadXYZ, lineTailXYZ, - lineWidth); + s_sizingHandleLineWidthInPixels); } } @@ -2939,22 +2731,11 @@ BrainOpenGLAnnotationDrawingFixedPipeline::drawSizingHandle(const AnnotationSizi glRotatef(-rotationAngle, 0.0, 0.0, 1.0); } - std::vector coords; - coords.push_back(-halfWidthHeight); - coords.push_back(-halfWidthHeight); - coords.push_back(0.0); - coords.push_back( halfWidthHeight); - coords.push_back(-halfWidthHeight); - coords.push_back(0.0); - coords.push_back( halfWidthHeight); - coords.push_back( halfWidthHeight); - coords.push_back(0.0); - coords.push_back(-halfWidthHeight); - coords.push_back( halfWidthHeight); - coords.push_back(0.0); - - std::vector dummyNormals; - + const float bottomLeft[3] = { -halfWidthHeight, -halfWidthHeight, 0.0f }; + const float bottomRight[3] = { halfWidthHeight, -halfWidthHeight, 0.0f }; + const float topRight[3] = { halfWidthHeight, halfWidthHeight, 0.0f }; + const float topLeft[3] = { -halfWidthHeight, halfWidthHeight, 0.0f }; + bool drawFilledCircleFlag = false; bool drawOutlineCircleFlag = false; bool drawSquareFlag = false; @@ -3000,9 +2781,11 @@ BrainOpenGLAnnotationDrawingFixedPipeline::drawSizingHandle(const AnnotationSizi if (m_selectionModeFlag) { uint8_t identificationRGBA[4]; getIdentificationColor(identificationRGBA); - BrainOpenGLPrimitiveDrawing::drawPolygon(coords, - dummyNormals, - identificationRGBA); + GraphicsShape::drawBoxFilledByteColor(bottomLeft, + bottomRight, + topRight, + topLeft, + identificationRGBA); m_selectionInfo.push_back(SelectionInfo(annotationFile, annotation, handleType, @@ -3010,19 +2793,25 @@ BrainOpenGLAnnotationDrawingFixedPipeline::drawSizingHandle(const AnnotationSizi } else { if (drawFilledCircleFlag) { - m_brainOpenGLFixedPipeline->drawCircleFilled(m_selectionBoxRGBA, - halfWidthHeight); + GraphicsShape::drawCircleFilled(NULL, + m_selectionBoxRGBA, + halfWidthHeight * 2); } else if (drawSquareFlag) { - BrainOpenGLPrimitiveDrawing::drawPolygon(coords, - dummyNormals, - m_selectionBoxRGBA); + GraphicsShape::drawBoxFilledByteColor(bottomLeft, + bottomRight, + topRight, + topLeft, + m_selectionBoxRGBA); } else if (drawOutlineCircleFlag) { const float diameter = halfWidthHeight; glPushMatrix(); - glScaled(diameter, diameter, 1.0); - m_rotationHandleCircle->draw(m_selectionBoxRGBA); + glScaled(diameter, diameter, 1.0f); + GraphicsShape::drawRing(NULL, + m_selectionBoxRGBA, + 0.7f, + 1.0f); glPopMatrix(); } } @@ -3106,9 +2895,9 @@ BrainOpenGLAnnotationDrawingFixedPipeline::drawAnnotationOneDimSizingHandles(Ann if (annotation->isSizeHandleValid(AnnotationSizingHandleTypeEnum::ANNOTATION_SIZING_HANDLE_ROTATION)) { const float midPointXYZ[3] = { - (firstPoint[0] + secondPoint[0]) / 2.0, - (firstPoint[1] + secondPoint[1]) / 2.0, - (firstPoint[2] + secondPoint[2]) / 2.0 + (firstPoint[0] + secondPoint[0]) / 2.0f, + (firstPoint[1] + secondPoint[1]) / 2.0f, + (firstPoint[2] + secondPoint[2]) / 2.0f }; drawSizingHandle(AnnotationSizingHandleTypeEnum::ANNOTATION_SIZING_HANDLE_ROTATION, annotationFile, @@ -3205,7 +2994,7 @@ BrainOpenGLAnnotationDrawingFixedPipeline::drawAnnotationTwoDimSizingHandles(Ann MathFunctions::subtractVectors(topLeft, bottomLeft, heightVector); MathFunctions::normalizeVector(heightVector); - const float innerSpacing = 2.0 + (lineThickness / 2.0); + const float innerSpacing = 2.0f + (lineThickness / 2.0f); float handleTopLeft[3]; float handleTopRight[3]; float handleBottomRight[3]; @@ -3219,39 +3008,32 @@ BrainOpenGLAnnotationDrawingFixedPipeline::drawAnnotationTwoDimSizingHandles(Ann expandBox(handleBottomLeft, handleBottomRight, handleTopRight, handleTopLeft, innerSpacing, innerSpacing); if (! m_selectionModeFlag) { - std::vector coords; - coords.insert(coords.end(), handleBottomLeft, handleBottomLeft + 3); - coords.insert(coords.end(), handleBottomRight, handleBottomRight + 3); - coords.insert(coords.end(), handleTopRight, handleTopRight + 3); - coords.insert(coords.end(), handleTopLeft, handleTopLeft + 3); - - BrainOpenGLPrimitiveDrawing::drawLineLoop(coords, - m_selectionBoxRGBA, - 2.0); + GraphicsShape::drawBoxOutlineByteColor(handleBottomLeft, handleBottomRight, handleTopRight, handleTopLeft, + m_selectionBoxRGBA, GraphicsPrimitive::LineWidthType::PIXELS, 2.0f); } const float handleLeft[3] = { - (handleBottomLeft[0] + handleTopLeft[0]) / 2.0, - (handleBottomLeft[1] + handleTopLeft[1]) / 2.0, - (handleBottomLeft[2] + handleTopLeft[2]) / 2.0, + (handleBottomLeft[0] + handleTopLeft[0]) / 2.0f, + (handleBottomLeft[1] + handleTopLeft[1]) / 2.0f, + (handleBottomLeft[2] + handleTopLeft[2]) / 2.0f, }; const float handleRight[3] = { - (handleBottomRight[0] + handleTopRight[0]) / 2.0, - (handleBottomRight[1] + handleTopRight[1]) / 2.0, - (handleBottomRight[2] + handleTopRight[2]) / 2.0, + (handleBottomRight[0] + handleTopRight[0]) / 2.0f, + (handleBottomRight[1] + handleTopRight[1]) / 2.0f, + (handleBottomRight[2] + handleTopRight[2]) / 2.0f, }; const float handleBottom[3] = { - (handleBottomLeft[0] + handleBottomRight[0]) / 2.0, - (handleBottomLeft[1] + handleBottomRight[1]) / 2.0, - (handleBottomLeft[2] + handleBottomRight[2]) / 2.0, + (handleBottomLeft[0] + handleBottomRight[0]) / 2.0f, + (handleBottomLeft[1] + handleBottomRight[1]) / 2.0f, + (handleBottomLeft[2] + handleBottomRight[2]) / 2.0f, }; const float handleTop[3] = { - (handleTopLeft[0] + handleTopRight[0]) / 2.0, - (handleTopLeft[1] + handleTopRight[1]) / 2.0, - (handleTopLeft[2] + handleTopRight[2]) / 2.0, + (handleTopLeft[0] + handleTopRight[0]) / 2.0f, + (handleTopLeft[1] + handleTopRight[1]) / 2.0f, + (handleTopLeft[2] + handleTopRight[2]) / 2.0f, }; const float sizeHandleSize = 5.0; @@ -3361,9 +3143,9 @@ BrainOpenGLAnnotationDrawingFixedPipeline::drawAnnotationTwoDimSizingHandles(Ann }; const float handleRotationLineEnd[3] = { - handleOffset[0] + (rotationOffset * 0.75 * heightVector[0] ), - handleOffset[1] + (rotationOffset * 0.75 * heightVector[1]), - handleOffset[2] + (rotationOffset * 0.75 * heightVector[2]) + handleOffset[0] + (rotationOffset * 0.75f * heightVector[0] ), + handleOffset[1] + (rotationOffset * 0.75f * heightVector[1]), + handleOffset[2] + (rotationOffset * 0.75f * heightVector[2]) }; /* @@ -3372,9 +3154,8 @@ BrainOpenGLAnnotationDrawingFixedPipeline::drawAnnotationTwoDimSizingHandles(Ann std::vector coords; coords.insert(coords.end(), handleRotationLineEnd, handleRotationLineEnd + 3); coords.insert(coords.end(), handleOffset, handleOffset + 3); - BrainOpenGLPrimitiveDrawing::drawLines(coords, - m_selectionBoxRGBA, - 2.0); + GraphicsShape::drawLinesByteColor(coords, m_selectionBoxRGBA, + GraphicsPrimitive::LineWidthType::PIXELS, 2.0f); drawSizingHandle(AnnotationSizingHandleTypeEnum::ANNOTATION_SIZING_HANDLE_ROTATION, annotationFile, annotation, @@ -3429,34 +3210,44 @@ BrainOpenGLAnnotationDrawingFixedPipeline::setSelectionBoxColor() * * @param annotation * Annotation that will be drawn. + * @param surface + * Surface that is being drawn. * @return * True if the annotation is drawn with depth testing, else false. */ bool -BrainOpenGLAnnotationDrawingFixedPipeline::isDrawnWithDepthTesting(const Annotation* /*annotation*/) +BrainOpenGLAnnotationDrawingFixedPipeline::isDrawnWithDepthTesting(const Annotation* annotation, + const Surface* surface) { - return true; - -/* - bool depthTestFlag = false; - + bool testFlatSurfaceFlag = false; switch (annotation->getCoordinateSpace()) { - case AnnotationCoordinateSpaceEnum::STEREOTAXIC: - depthTestFlag = true; + case AnnotationCoordinateSpaceEnum::CHART: break; - case AnnotationCoordinateSpaceEnum::PIXELS: + case AnnotationCoordinateSpaceEnum::STEREOTAXIC: + testFlatSurfaceFlag = true; break; case AnnotationCoordinateSpaceEnum::SURFACE: - depthTestFlag = true; + testFlatSurfaceFlag = true; break; case AnnotationCoordinateSpaceEnum::TAB: break; + case AnnotationCoordinateSpaceEnum::VIEWPORT: + break; case AnnotationCoordinateSpaceEnum::WINDOW: break; } + bool depthTestFlag = true; + + if (testFlatSurfaceFlag) { + if (surface != NULL) { + if (surface->getSurfaceType() == SurfaceTypeEnum::FLAT) { + depthTestFlag = false; + } + } + } + return depthTestFlag; -*/ } /** @@ -3479,5 +3270,42 @@ BrainOpenGLAnnotationDrawingFixedPipeline::setDepthTestingStatus(const bool newD return (savedStatus == GL_TRUE); } +/** + * Convert a percentage height to a line width in pixels + * + * @param percentageHeight + * Percentage of viewport height. + * @return + * Line width in pixels clamped to valid range + */ +float +BrainOpenGLAnnotationDrawingFixedPipeline::getLineWidthFromPercentageHeight(const float percentageHeight) const +{ + float widthPixels = (percentageHeight / 100.0f) * m_modelSpaceViewport[3]; + + if (widthPixels < m_lineWidthMinimum) { + widthPixels = m_lineWidthMinimum; + } + + return widthPixels; +} + +/** + * Convert the annotation's obsolete line width that was in pixels to a percentage of viewport height. + * Prior to late July, 2017, a line was specified in pixels. + * + * First, the annotation's percentage width is examined. If it is valid (greater than zero), then + * no conversion is needed. Otherwise, use the viewport height to convert the pixel width to a + * percentage and set the annotation's percentage line width. + * + * @param annotation + * The annotation. + */ +void +BrainOpenGLAnnotationDrawingFixedPipeline::convertObsoleteLineWidthPixelsToPercentageWidth(const Annotation* annotation) const +{ + annotation->convertObsoleteLineWidthPixelsToPercentageWidth(m_modelSpaceViewport[3]); +} + diff --git a/src/Brain/BrainOpenGLAnnotationDrawingFixedPipeline.h b/src/Brain/BrainOpenGLAnnotationDrawingFixedPipeline.h index 6d04143383425f839b5aa03bc914a01877a237de..4280d5195058fdb89216e6aa173f9ba0c4619449 100644 --- a/src/Brain/BrainOpenGLAnnotationDrawingFixedPipeline.h +++ b/src/Brain/BrainOpenGLAnnotationDrawingFixedPipeline.h @@ -21,6 +21,7 @@ */ /*LICENSE_END*/ +#include #include #include "AnnotationCoordinateSpaceEnum.h" @@ -44,10 +45,8 @@ namespace caret { class AnnotationText; class AnnotationTwoDimensionalShape; class Brain; - class BrainOpenGLFixedPipeline; - class BrainOpenGLShapeRing; - class DrawnWithOpenGLTextureInfo; + class EventOpenGLObjectToWindowTransform; class Surface; class BrainOpenGLAnnotationDrawingFixedPipeline : public CaretObject { @@ -55,25 +54,6 @@ namespace caret { public: class Inputs { public: - /** - * Viewport height used for setting text height - */ - enum TextHeightMode { - /** - * Use the viewport height from OpenGL drawing (last set by call to glViewport()). - * This is the viewport that is active when annotation drawing is executed and in - * which the model is drawn. - */ - TEXT_HEIGHT_USE_OPENGL_VIEWPORT_HEIGHT, - /** - * Use the tab viewport height from the "Inputs". - * This mode is primarily used when drawing a surface montage and results in - * the same text height for a stereotaxic or surface annotation in both - * surface montage and single surface mode. - */ - TEXT_HEIGHT_USE_TAB_VIEWPORT_HEIGHT - }; - enum WindowDrawingMode { WINDOW_DRAWING_NO, WINDOW_DRAWING_YES @@ -82,31 +62,22 @@ namespace caret { Inputs(Brain* brain, const BrainOpenGLFixedPipeline::Mode drawingMode, const float centerToEyeDistance, - const int32_t tabViewport[4], const int32_t windowIndex, const int32_t tabIndex, - const TextHeightMode textHeightMode, const WindowDrawingMode windowDrawingMode) : m_brain(brain), m_drawingMode(drawingMode), m_centerToEyeDistance(centerToEyeDistance), m_windowIndex(windowIndex), m_tabIndex(tabIndex), - m_textHeightMode(textHeightMode), m_windowDrawingMode(windowDrawingMode) { - m_tabViewport[0] = tabViewport[0]; - m_tabViewport[1] = tabViewport[1]; - m_tabViewport[2] = tabViewport[2]; - m_tabViewport[3] = tabViewport[3]; } Brain* m_brain; const BrainOpenGLFixedPipeline::Mode m_drawingMode; const float m_centerToEyeDistance; - int32_t m_tabViewport[4]; const int32_t m_windowIndex; const int32_t m_tabIndex; - const TextHeightMode m_textHeightMode; const WindowDrawingMode m_windowDrawingMode; }; @@ -117,6 +88,7 @@ namespace caret { void drawAnnotations(Inputs* inputs, const AnnotationCoordinateSpaceEnum::Enum drawingCoordinateSpace, std::vector& colorBars, + std::vector& notInFileAnnotations, const Surface* surfaceDisplayed); void drawModelSpaceAnnotationsOnVolumeSlice(Inputs* inputs, @@ -166,19 +138,34 @@ namespace caret { float m_rgba[4]; }; + /** + * Used to save viewport, model view, projection + */ + class TransformationInfo { + public: + void save(); + void restore(); + + GLint m_viewport[4]; + GLdouble m_modelViewMatrix[16]; + GLdouble m_projectionMatrix[16]; + bool m_valid = false; + }; + BrainOpenGLAnnotationDrawingFixedPipeline(const BrainOpenGLAnnotationDrawingFixedPipeline&); BrainOpenGLAnnotationDrawingFixedPipeline& operator=(const BrainOpenGLAnnotationDrawingFixedPipeline&); void drawAnnotationsInternal(const AnnotationCoordinateSpaceEnum::Enum drawingCoordinateSpace, std::vector& colorBars, + std::vector& viewportAnnotations, const Surface* surfaceDisplayed, const float sliceThickness); - bool getAnnotationWindowCoordinate(const AnnotationCoordinate* coordinate, - const AnnotationCoordinateSpaceEnum::Enum annotationCoordSpace, - const Surface* surfaceDisplayed, - float windowXYZOut[3]) const; + bool getAnnotationDrawingSpaceCoordinate(const Annotation* annotation, + const AnnotationCoordinate* coordinate, + const Surface* surfaceDisplayed, + float xyzOut[3]) const; bool getAnnotationTwoDimShapeBounds(const AnnotationTwoDimensionalShape* annotation2D, const float windowXYZ[3], @@ -198,33 +185,17 @@ namespace caret { Annotation* annotation, const Surface* surfaceDisplayed); + void drawColorBar(AnnotationFile* annotationFile, + AnnotationColorBar* colorBar); + bool drawBox(AnnotationFile* annotationFile, AnnotationBox* box, const Surface* surfaceDisplayed); - void drawColorBar(AnnotationFile* annotationFile, - AnnotationColorBar* colorBar); - bool drawImage(AnnotationFile* annotationFile, AnnotationImage* image, const Surface* surfaceDisplayed); - void drawImageBytes(const float windowX, - const float windowY, - const float windowZ, - const uint8_t* imageBytesRGBA, - const int32_t imageWidth, - const int32_t imageHeight); - - void drawImageBytesWithTexture(DrawnWithOpenGLTextureInfo* textureInfo, - const float bottomLeft[3], - const float bottomRight[3], - const float topRight[3], - const float topLeft[3], - const uint8_t* imageBytesRGBA, - const int32_t imageWidth, - const int32_t imageHeight); - bool drawLine(AnnotationFile* annotationFile, AnnotationLine* line, const Surface* surfaceDisplayed); @@ -282,7 +253,8 @@ namespace caret { const float secondPoint[3], const float lineThickness); - bool isDrawnWithDepthTesting(const Annotation* annotation); + bool isDrawnWithDepthTesting(const Annotation* annotation, + const Surface* surface); bool setDepthTestingStatus(const bool newDepthTestingStatus); @@ -299,7 +271,9 @@ namespace caret { const float lineThickness, const bool validStartArrow, const bool validEndArrow, - std::vector& coordinatesOut) const; + std::vector& lineCoordinatesOut, + std::vector& startArrowCoordinatesOut, + std::vector& endArrowCoordinatesOut) const; void getTextLineToBrainordinateLineCoordinates(const AnnotationText* text, const Surface* surfaceDisplayed, @@ -307,7 +281,8 @@ namespace caret { const float bottomRight[3], const float topRight[3], const float topLeft[3], - std::vector& lineCoordinatesOut) const; + std::vector& lineCoordinatesOut, + std::vector& arrowCoordinatesOut) const; static void expandBox(float bottomLeft[3], float bottomRight[3], @@ -331,6 +306,10 @@ namespace caret { const float startXYZ[3], float endXYZ[3]) const; + void convertObsoleteLineWidthPixelsToPercentageWidth(const Annotation* annotation) const; + + float getLineWidthFromPercentageHeight(const float percentageHeight) const; + BrainOpenGLFixedPipeline* m_brainOpenGLFixedPipeline; Inputs* m_inputs; @@ -357,9 +336,6 @@ namespace caret { /** OpenGL Viewport */ GLint m_modelSpaceViewport[4]; - /** Browser tab's viewport */ - //GLint m_tabViewport[4]; - /** volume space plane */ Plane m_volumeSpacePlane; @@ -372,8 +348,11 @@ namespace caret { /** Color for selection box and sizing handles */ uint8_t m_selectionBoxRGBA[4]; - /** Used for rotation hanlde circle */ - BrainOpenGLShapeRing* m_rotationHandleCircle; + float m_lineWidthMinimum = 1.0f; + + std::unique_ptr m_transformEvent; + + static constexpr float s_sizingHandleLineWidthInPixels = 2.0f; // ADD_NEW_MEMBERS_HERE diff --git a/src/Brain/BrainOpenGLChartDrawingFixedPipeline.cxx b/src/Brain/BrainOpenGLChartDrawingFixedPipeline.cxx index c420657bc6bc9e50e958acf35a6055eb28250639..961e662fe1effb97b9c451a1592852fbaafc0d61 100644 --- a/src/Brain/BrainOpenGLChartDrawingFixedPipeline.cxx +++ b/src/Brain/BrainOpenGLChartDrawingFixedPipeline.cxx @@ -180,13 +180,13 @@ BrainOpenGLChartDrawingFixedPipeline::drawCartesianChart(Brain* brain, Margins margins(marginSize); double width, height; - estimateCartesianChartAxisLegendsWidthHeight(textRenderer, vpHeight, cartesianChart->getLeftAxis(), width, height); + estimateCartesianChartAxisLegendsWidthHeight(textRenderer, vpWidth, vpHeight, cartesianChart->getLeftAxis(), width, height); margins.m_left = std::max(margins.m_left, width); - estimateCartesianChartAxisLegendsWidthHeight(textRenderer, vpHeight, cartesianChart->getRightAxis(), width, height); + estimateCartesianChartAxisLegendsWidthHeight(textRenderer, vpWidth, vpHeight, cartesianChart->getRightAxis(), width, height); margins.m_right = std::max(margins.m_right, width); - estimateCartesianChartAxisLegendsWidthHeight(textRenderer, vpHeight, cartesianChart->getTopAxis(), width, height); + estimateCartesianChartAxisLegendsWidthHeight(textRenderer, vpWidth, vpHeight, cartesianChart->getTopAxis(), width, height); margins.m_top = std::max(margins.m_top, height); - estimateCartesianChartAxisLegendsWidthHeight(textRenderer, vpHeight, cartesianChart->getBottomAxis(), width, height); + estimateCartesianChartAxisLegendsWidthHeight(textRenderer, vpWidth, vpHeight, cartesianChart->getBottomAxis(), width, height); margins.m_bottom = std::max(margins.m_bottom, height); if (margins.m_left > marginSize) margins.m_left += 10; @@ -658,6 +658,8 @@ BrainOpenGLChartDrawingFixedPipeline::drawChartAxisCartesian(const float vpX, * Text rendering. * @param viewportHeight * Height of viewport. + * @param viewportHeight + * Height of viewport. * @param axis * The axis. * @param widthOut @@ -667,6 +669,7 @@ BrainOpenGLChartDrawingFixedPipeline::drawChartAxisCartesian(const float vpX, */ void BrainOpenGLChartDrawingFixedPipeline::estimateCartesianChartAxisLegendsWidthHeight(BrainOpenGLTextRenderInterface* textRenderer, + const float viewportWidth, const float viewportHeight, ChartAxis* axis, double& widthOut, @@ -702,7 +705,7 @@ BrainOpenGLChartDrawingFixedPipeline::estimateCartesianChartAxisLegendsWidthHeig annotationText.setText(text); double textWidth = 0.0; double textHeight = 0.0; - textRenderer->getTextWidthHeightInPixels(annotationText, viewportHeight, textWidth, textHeight); + textRenderer->getTextWidthHeightInPixels(annotationText, viewportWidth, viewportHeight, textWidth, textHeight); widthOut = std::max(widthOut, textWidth); heightOut = std::max(heightOut, textHeight); @@ -940,9 +943,9 @@ BrainOpenGLChartDrawingFixedPipeline::drawChartGraphicsMatrix(const int32_t view uint8_t highlightRGBByte[3]; prefs->getBackgroundAndForegroundColors()->getColorForegroundChartView(highlightRGBByte); const float highlightRGB[3] = { - highlightRGBByte[0] / 255.0, - highlightRGBByte[1] / 255.0, - highlightRGBByte[2] / 255.0 + highlightRGBByte[0] / 255.0f, + highlightRGBByte[1] / 255.0f, + highlightRGBByte[2] / 255.0f }; int32_t numberOfRows = 0; diff --git a/src/Brain/BrainOpenGLChartDrawingFixedPipeline.h b/src/Brain/BrainOpenGLChartDrawingFixedPipeline.h index 10dc85e84e0ab58a43ea1a4181eaf9b25dae9455..6567b029bb691d11f60959898bbce05c1e64cecc 100644 --- a/src/Brain/BrainOpenGLChartDrawingFixedPipeline.h +++ b/src/Brain/BrainOpenGLChartDrawingFixedPipeline.h @@ -119,6 +119,7 @@ namespace caret { const float rgb[3]); void estimateCartesianChartAxisLegendsWidthHeight(BrainOpenGLTextRenderInterface* textRenderer, + const float viewportWidth, const float viewportHeight, ChartAxis* axis, double& widthOut, diff --git a/src/Brain/BrainOpenGLChartTwoDrawingFixedPipeline.cxx b/src/Brain/BrainOpenGLChartTwoDrawingFixedPipeline.cxx new file mode 100644 index 0000000000000000000000000000000000000000..725b416aa9677031103eaee49b44eb8137eb3f51 --- /dev/null +++ b/src/Brain/BrainOpenGLChartTwoDrawingFixedPipeline.cxx @@ -0,0 +1,2723 @@ + +/*LICENSE_START*/ +/* + * Copyright (C) 2017 Washington University School of Medicine + * + * 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 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ +/*LICENSE_END*/ + +#define __BRAIN_OPEN_G_L_CHART_TWO_DRAWING_FIXED_PIPELINE_DECLARE__ +#include "BrainOpenGLChartTwoDrawingFixedPipeline.h" +#undef __BRAIN_OPEN_G_L_CHART_TWO_DRAWING_FIXED_PIPELINE_DECLARE__ + +#include + +#include "AnnotationCoordinate.h" +#include "AnnotationColorBar.h" +#include "AnnotationPercentSizeText.h" +#include "AnnotationPointSizeText.h" +#include "Brain.h" +#include "BrainOpenGLFixedPipeline.h" +#include "BrainOpenGLTextRenderInterface.h" +#include "BrainOpenGLViewportContent.h" +#include "BrowserTabContent.h" +#include "CaretAssert.h" +#include "CaretLogger.h" +#include "CaretPreferences.h" +#include "ChartTwoCartesianAxis.h" +#include "ChartTwoDataCartesian.h" +#include "ChartTwoLineSeriesHistory.h" +#include "ChartTwoMatrixDisplayProperties.h" +#include "ChartTwoOverlay.h" +#include "ChartTwoOverlaySet.h" +#include "ChartTwoTitle.h" +#include "ChartableTwoFileDelegate.h" +#include "ChartableTwoFileHistogramChart.h" +#include "ChartableTwoFileMatrixChart.h" +#include "ChartableTwoFileLineSeriesChart.h" +#include "CiftiMappableConnectivityMatrixDataFile.h" +#include "DeveloperFlagsEnum.h" +#include "FastStatistics.h" +#include "GraphicsEngineDataOpenGL.h" +#include "GraphicsPrimitiveV3f.h" +#include "GraphicsPrimitiveV3fC4f.h" +#include "GraphicsPrimitiveV3fC4ub.h" +#include "GraphicsShape.h" +#include "IdentificationWithColor.h" +#include "MathFunctions.h" +#include "ModelChartTwo.h" +#include "NodeAndVoxelColoring.h" +#include "PaletteColorMapping.h" +#include "SessionManager.h" +#include "SelectionItemAnnotation.h" +#include "SelectionItemChartTwoHistogram.h" +#include "SelectionItemChartTwoLabel.h" +#include "SelectionItemChartTwoLineSeries.h" +#include "SelectionItemChartTwoMatrix.h" +#include "SelectionManager.h" + +using namespace caret; + +static bool debugFlag = false; + +/** + * \class caret::BrainOpenGLChartTwoDrawingFixedPipeline + * \brief Drawing of version two charts. + * \ingroup Brain + */ + +/** + * Constructor. + * + * @param viewportContent + * The content of the viewport. + */ +BrainOpenGLChartTwoDrawingFixedPipeline::BrainOpenGLChartTwoDrawingFixedPipeline(const BrainOpenGLViewportContent* viewportContent) +: BrainOpenGLChartTwoDrawingInterface(), +m_viewportContent(viewportContent) +{ + m_preferences = SessionManager::get()->getCaretPreferences(); + + BrainOpenGL::getMinMaxLineWidth(s_minimumLineWidthOpenGL, + s_maximumLineWidthOpenGL); +} + +/** + * Destructor. + */ +BrainOpenGLChartTwoDrawingFixedPipeline::~BrainOpenGLChartTwoDrawingFixedPipeline() +{ +} + +/** + * Draw charts from a chart overlay. + * + * @param brain + * Brain. + * @param chartTwoModel + * The chart two model. + * @param fixedPipelineDrawing + * The fixed pipeline OpenGL drawing. + * @param selectionItemDataType + * Selected data type. + * @param viewport + * Viewport for the chart. + * @param annotationsOut + * Output containing annotations that will be drawing by annotation drawing code. + */ +void +BrainOpenGLChartTwoDrawingFixedPipeline::drawChartOverlaySet(Brain* brain, + ModelChartTwo* chartTwoModel, + BrainOpenGLFixedPipeline* fixedPipelineDrawing, + const SelectionItemDataTypeEnum::Enum selectionItemDataType, + const int32_t viewport[4], + std::vector& annotationsOut) +{ + annotationsOut.clear(); + m_annotationsForDrawingOutput.clear(); + + CaretAssert(m_viewportContent); + BrowserTabContent* browserTabContent = m_viewportContent->getBrowserTabContent(); + CaretAssert(browserTabContent); + CaretAssert(brain); + CaretAssert(browserTabContent); + CaretAssert(chartTwoModel); + CaretAssert(fixedPipelineDrawing); + + m_brain = brain; + m_chartTwoModel = chartTwoModel; + m_fixedPipelineDrawing = fixedPipelineDrawing; + m_textRenderer = fixedPipelineDrawing->getTextRenderer(); + m_browserTabContent = browserTabContent; + m_translation[0] = 0.0; + m_translation[1] = 0.0; + m_translation[2] = 0.0; + m_browserTabContent->getTranslation(m_translation); + m_tabIndex = m_browserTabContent->getTabNumber(); + m_zooming = m_browserTabContent->getScaling(); + m_chartOverlaySet = m_chartTwoModel->getChartTwoOverlaySet(m_tabIndex); + m_selectionItemDataType = selectionItemDataType; + m_viewport[0] = viewport[0]; + m_viewport[1] = viewport[1]; + m_viewport[2] = viewport[2]; + m_viewport[3] = viewport[3]; + + m_selectionItemAnnotation = m_brain->getSelectionManager()->getAnnotationIdentification(); + m_selectionItemHistogram = m_brain->getSelectionManager()->getChartTwoHistogramIdentification(); + m_selectionItemLineSeries = m_brain->getSelectionManager()->getChartTwoLineSeriesIdentification(); + m_selectionItemMatrix = m_brain->getSelectionManager()->getChartTwoMatrixIdentification(); + m_selectionItemChartLabel = m_brain->getSelectionManager()->getChartTwoLabelIdentification(); + + m_fixedPipelineDrawing->disableLighting(); + + /* + * Find color bars for this tab and decrease height of + * viewport so chart is above color bars. + */ + std::vector colorBars; + m_browserTabContent->getAnnotationColorBars(colorBars); + if ( ! colorBars.empty()) { + int heightOfAllColorBars = 0; + + for (auto cb : colorBars) { + bool useItFlag = false; + switch (cb->getCoordinateSpace()) { + case AnnotationCoordinateSpaceEnum::CHART: + break; + case AnnotationCoordinateSpaceEnum::STEREOTAXIC: + break; + case AnnotationCoordinateSpaceEnum::SURFACE: + break; + case AnnotationCoordinateSpaceEnum::TAB: + useItFlag = true; + break; + case AnnotationCoordinateSpaceEnum::VIEWPORT: + break; + case AnnotationCoordinateSpaceEnum::WINDOW: + break; + } + + if (useItFlag) { + float bottomLeft[3], bottomRight[3], topRight[3], topLeft[3]; + float viewportXYZ[3] = { 5.0, 5.0, 0.0 }; + const bool boundsValid = cb->getShapeBounds(m_viewport[2], m_viewport[3], + viewportXYZ, + bottomLeft, bottomRight, topRight, topLeft); + if (boundsValid) { + heightOfAllColorBars += (topRight[1] - bottomRight[1]); + } + } + } + + if (heightOfAllColorBars > 0) { + const int32_t extraSpace = 5; + heightOfAllColorBars += extraSpace; + if (heightOfAllColorBars < viewport[3]) { + m_viewport[1] += heightOfAllColorBars; + m_viewport[3] -= heightOfAllColorBars; + } + } + } + + bool drawHistogramFlag = true; + bool drawLineSeriesFlag = true; + bool drawMatrixFlag = true; + + /* + * Check for a 'selection' type mode + */ + m_identificationModeFlag = false; + switch (m_fixedPipelineDrawing->mode) { + case BrainOpenGLFixedPipeline::MODE_DRAWING: + break; + case BrainOpenGLFixedPipeline::MODE_IDENTIFICATION: + if ( (! m_selectionItemHistogram->isEnabledForSelection()) + && (! m_selectionItemAnnotation->isEnabledForSelection()) + && (! m_selectionItemChartLabel->isEnabledForSelection()) ) { + drawHistogramFlag = false; + } + if ( (! m_selectionItemLineSeries->isEnabledForSelection()) + && (! m_selectionItemAnnotation->isEnabledForSelection()) + && (! m_selectionItemChartLabel->isEnabledForSelection()) ) { + drawLineSeriesFlag = false; + } + if ( (! m_selectionItemMatrix->isEnabledForSelection()) + && (! m_selectionItemAnnotation->isEnabledForSelection()) ) { + drawMatrixFlag = false; + } + if (drawHistogramFlag + || drawLineSeriesFlag + || drawMatrixFlag) { + m_identificationModeFlag = true; + glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); + } + else { + return; + } + break; + case BrainOpenGLFixedPipeline::MODE_PROJECTION: + return; + break; + } + + saveStateOfOpenGL(); + + if (m_chartOverlaySet != NULL) { + const int32_t numberOfOverlays = m_chartOverlaySet->getNumberOfDisplayedOverlays(); + if (numberOfOverlays > 0) { + ChartTwoOverlay* topOverlay = m_chartOverlaySet->getOverlay(0); + CaretAssert(topOverlay); + CaretMappableDataFile* mapFile = NULL; + ChartTwoOverlay::SelectedIndexType selectedIndexType = ChartTwoOverlay::SelectedIndexType::INVALID; + int32_t selectedIndex = -1; + topOverlay->getSelectionData(mapFile, + selectedIndexType, + selectedIndex); + if (mapFile != NULL) { + const ChartTwoDataTypeEnum::Enum chartDataType = topOverlay->getChartTwoDataType(); + switch (chartDataType) { + case ChartTwoDataTypeEnum::CHART_DATA_TYPE_INVALID: + break; + case ChartTwoDataTypeEnum::CHART_DATA_TYPE_HISTOGRAM: + if (drawHistogramFlag) { + drawHistogramOrLineSeriesChart(ChartTwoDataTypeEnum::CHART_DATA_TYPE_HISTOGRAM); + } + break; + case ChartTwoDataTypeEnum::CHART_DATA_TYPE_LINE_SERIES: + if (drawLineSeriesFlag) { + drawHistogramOrLineSeriesChart(ChartTwoDataTypeEnum::CHART_DATA_TYPE_LINE_SERIES); + } + break; + case ChartTwoDataTypeEnum::CHART_DATA_TYPE_MATRIX: + if (drawMatrixFlag) { + drawMatrixChart(); + } + break; + } + } + } + } + + restoreStateOfOpenGL(); + + annotationsOut = m_annotationsForDrawingOutput; +} + +/** + * Draw a histogram or line series chart. + * + * @param chartDataType + * Type of chart to draw. + */ +void +BrainOpenGLChartTwoDrawingFixedPipeline::drawHistogramOrLineSeriesChart(const ChartTwoDataTypeEnum::Enum chartDataType) +{ + bool drawHistogramFlag = false; + bool drawLineSeriesFlag = false; + switch (chartDataType) { + case ChartTwoDataTypeEnum::CHART_DATA_TYPE_INVALID: + CaretAssert(0); + return; + break; + case ChartTwoDataTypeEnum::CHART_DATA_TYPE_HISTOGRAM: + drawHistogramFlag = true; + break; + case ChartTwoDataTypeEnum::CHART_DATA_TYPE_LINE_SERIES: + drawLineSeriesFlag = true; + break; + case ChartTwoDataTypeEnum::CHART_DATA_TYPE_MATRIX: + CaretAssert(0); + return; + break; + } + + const int32_t tabViewportX = m_viewport[0]; + const int32_t tabViewportY = m_viewport[1]; + const int32_t tabViewportWidth = m_viewport[2]; + const int32_t tabViewportHeight = m_viewport[3]; + + int32_t chartGraphicsDrawingViewport[4] = { + tabViewportX, + tabViewportY, + tabViewportWidth, + tabViewportHeight + }; + + const int32_t numberOfOverlays = m_chartOverlaySet->getNumberOfDisplayedOverlays(); + CaretAssert(numberOfOverlays > 0); + const ChartTwoOverlay* topOverlay = m_chartOverlaySet->getOverlay(0); + const ChartTwoCompoundDataType cdt = topOverlay->getChartTwoCompoundDataType(); + if (drawHistogramFlag) { + CaretAssert(cdt.getChartTwoDataType() == ChartTwoDataTypeEnum::CHART_DATA_TYPE_HISTOGRAM); + } + else if (drawLineSeriesFlag) { + CaretAssert(cdt.getChartTwoDataType() == ChartTwoDataTypeEnum::CHART_DATA_TYPE_LINE_SERIES); + } + else { + CaretAssert(0); + } + + std::vector> histogramDrawingInfo; + std::deque lineSeriesChartsToDraw; + + /* + * Get the histogram drawing information and overall extent + */ + float xMinBottom = std::numeric_limits::max(); + float xMaxBottom = -std::numeric_limits::max(); + float xMinTop = std::numeric_limits::max(); + float xMaxTop = -std::numeric_limits::max(); + float yMinLeft = std::numeric_limits::max(); + float yMaxLeft = -std::numeric_limits::max(); + float yMinRight = std::numeric_limits::max(); + float yMaxRight = -std::numeric_limits::max(); + + /* + * Find histograms or line-series charts for drawing + * and also find min/max coordinates on axes + */ + for (int32_t iOverlay = (numberOfOverlays - 1); iOverlay >= 0; iOverlay--) { + ChartTwoOverlay* chartOverlay = m_chartOverlaySet->getOverlay(iOverlay); + if ( ! chartOverlay->isEnabled()) { + continue; + } + + CaretMappableDataFile* mapFile = NULL; + ChartTwoOverlay::SelectedIndexType selectedIndexType = ChartTwoOverlay::SelectedIndexType::INVALID; + int32_t selectedIndex = -1; + chartOverlay->getSelectionData(mapFile, + selectedIndexType, + selectedIndex); + if (mapFile == NULL) { + continue; + } + + ChartableTwoFileDelegate* chartDelegate = mapFile->getChartingDelegate(); + + if (drawHistogramFlag) { + ChartableTwoFileHistogramChart* histogramChart = chartDelegate->getHistogramCharting(); + + if (histogramChart->isValid()) { + CaretAssert(selectedIndexType == ChartTwoOverlay::SelectedIndexType::MAP); + AString errorMessage; + histogramDrawingInfo.push_back(std::unique_ptr(new HistogramChartDrawingInfo(histogramChart, + selectedIndex, + chartOverlay->getCartesianVerticalAxisLocation(), + (chartOverlay->isAllMapsSupported() + && chartOverlay->isAllMapsSelected())))); + const Histogram* histogram = histogramChart->getHistogramForChartDrawing(selectedIndex, + (chartOverlay->isAllMapsSupported() + && chartOverlay->isAllMapsSelected())); + if (histogram == NULL) { + histogramDrawingInfo.pop_back(); + continue; + } + + float histogramMinX = 0.0, histogramMaxX = 0.0, histogramMaxY = 0.0; + histogram->getRangeAndMaxDisplayHeight(histogramMinX, histogramMaxX, histogramMaxY); + if (histogramMaxX > histogramMinX) { + xMinBottom = std::min(xMinBottom, histogramMinX); + xMaxBottom = std::max(xMaxBottom, histogramMaxX); + xMinTop = std::min(xMinTop, histogramMinX); + xMaxTop = std::max(xMaxTop, histogramMaxX); + + switch (chartOverlay->getCartesianVerticalAxisLocation()) { + case ChartAxisLocationEnum::CHART_AXIS_LOCATION_TOP: + CaretAssertMessage(0, "TOP axis not allowed for vertical axis"); + break; + case ChartAxisLocationEnum::CHART_AXIS_LOCATION_RIGHT: + yMinRight = std::min(yMinRight, 0.0f); + yMaxRight = std::max(yMaxRight, histogramMaxY); + break; + case ChartAxisLocationEnum::CHART_AXIS_LOCATION_LEFT: + yMinLeft = std::min(yMinLeft, 0.0f); + yMaxLeft = std::max(yMaxLeft, histogramMaxY); + break; + case ChartAxisLocationEnum::CHART_AXIS_LOCATION_BOTTOM: + CaretAssertMessage(0, "BOTTOM axis not allowed for vertical axis"); + break; + } + } + } + } + + if (drawLineSeriesFlag) { + const ChartableTwoFileLineSeriesChart* lineSeriesChart = chartDelegate->getLineSeriesCharting(); + const ChartTwoLineSeriesHistory* lineSeriesHistory = lineSeriesChart->getHistory(); + const int32_t numHistory = lineSeriesHistory->getHistoryCount(); + for (int32_t iHistory = (numHistory - 1); iHistory >= 0; iHistory--) { + const ChartTwoDataCartesian* data = lineSeriesHistory->getHistoryItem(iHistory); + CaretAssert(data); + if (data->isSelected()) { + BoundingBox boundingBox; + if (data->getBounds(boundingBox)) { + xMinBottom = std::min(xMinBottom, boundingBox.getMinX()); + xMaxBottom = std::max(xMaxBottom, boundingBox.getMaxX()); + xMinTop = std::min(xMinTop, boundingBox.getMinX()); + xMaxTop = std::max(xMaxTop, boundingBox.getMaxX()); + + switch (chartOverlay->getCartesianVerticalAxisLocation()) { + case ChartAxisLocationEnum::CHART_AXIS_LOCATION_TOP: + CaretAssertMessage(0, "TOP axis not allowed for vertical axis"); + break; + case ChartAxisLocationEnum::CHART_AXIS_LOCATION_RIGHT: + yMinRight = std::min(yMinRight, boundingBox.getMinY()); + yMaxRight = std::max(yMaxRight, boundingBox.getMaxY()); + break; + case ChartAxisLocationEnum::CHART_AXIS_LOCATION_LEFT: + yMinLeft = std::min(yMinLeft, boundingBox.getMinY()); + yMaxLeft = std::max(yMaxLeft, boundingBox.getMaxY()); + break; + case ChartAxisLocationEnum::CHART_AXIS_LOCATION_BOTTOM: + CaretAssertMessage(0, "BOTTOM axis not allowed for vertical axis"); + break; + } + + lineSeriesChartsToDraw.push_back(LineSeriesChartDrawingInfo(lineSeriesChart, + data, + chartOverlay->getCartesianVerticalAxisLocation())); + } + } + } + } + } + + /* + * Bounds valid? + * For X, maximum value must be greater than minimum value + * For Y, maximum value must be greater than or equal to minimum value since + * all data points may be zero. + */ + const bool xBottomValid = (xMinBottom < xMaxBottom); + const bool xTopValid = (xMinTop < xMaxTop); + const bool xValid = (xBottomValid || xTopValid); + const bool yLeftValid = (yMinLeft <= yMaxLeft); + const bool yRightValid = (yMinRight <= yMaxRight); + const bool yValid = (yLeftValid || yRightValid); + if (xValid && yValid) { + /* + * Make invalid ranges zero + */ + if ( ! yLeftValid) { + yMinLeft = 0.0; + yMaxLeft = 0.0; + } + if ( ! yRightValid) { + yMinRight = 0.0; + yMaxRight = 0.0; + } + if ( ! xTopValid) { + xMinTop = 0.0; + xMaxTop = 0.0; + } + if ( ! xBottomValid) { + xMinBottom = 0.0; + xMaxBottom = 0.0; + } + + ChartTwoCartesianAxis* leftAxis = NULL; + ChartTwoCartesianAxis* rightAxis = NULL; + ChartTwoCartesianAxis* bottomAxis = NULL; + ChartTwoCartesianAxis* topAxis = NULL; + + std::vector displayedAxes; + m_chartOverlaySet->getDisplayedChartAxes(displayedAxes); + for (auto axis : displayedAxes) { + CaretAssert(axis); + switch (axis->getAxisLocation()) { + case ChartAxisLocationEnum::CHART_AXIS_LOCATION_BOTTOM: + bottomAxis = axis; + break; + case ChartAxisLocationEnum::CHART_AXIS_LOCATION_LEFT: + leftAxis = axis; + break; + case ChartAxisLocationEnum::CHART_AXIS_LOCATION_RIGHT: + rightAxis = axis; + break; + case ChartAxisLocationEnum::CHART_AXIS_LOCATION_TOP: + topAxis = axis; + break; + } + } + + ChartTwoTitle* chartTitle = m_chartOverlaySet->getChartTitle(); + + GLint vp[4]; + glGetIntegerv(GL_VIEWPORT, vp); + + glViewport(tabViewportX, + tabViewportY, + tabViewportWidth, + tabViewportHeight); + + glMatrixMode(GL_PROJECTION); + glLoadIdentity(); + glOrtho(0, tabViewportWidth, 0, tabViewportHeight, -1.0, 1.0); + + glMatrixMode(GL_MODELVIEW); + glLoadIdentity(); + + const float lineWidthPercentage = m_chartOverlaySet->getAxisLineThickness(); + + /* + * Estimate size of title + */ + TitleDrawingInfo titleInfo(m_textRenderer, + m_viewport, + chartTitle); + + /* + * Estimate axes sizes but at this point we can only find the + * size one dimension (x or y) + */ + AxisDrawingInfo leftAxisInfo(m_textRenderer, + m_viewport, + xMinBottom, + xMaxBottom, + yMinLeft, + yMaxLeft, + ChartAxisLocationEnum::CHART_AXIS_LOCATION_LEFT, + leftAxis, + m_chartOverlaySet->getAxisLabel(leftAxis), + lineWidthPercentage); + AxisDrawingInfo rightAxisInfo(m_textRenderer, + m_viewport, + xMinTop, + xMaxTop, + yMinRight, + yMaxRight, + ChartAxisLocationEnum::CHART_AXIS_LOCATION_RIGHT, + rightAxis, + m_chartOverlaySet->getAxisLabel(rightAxis), + lineWidthPercentage); + AxisDrawingInfo bottomAxisInfo(m_textRenderer, + m_viewport, + xMinBottom, + xMaxBottom, + yMinLeft, + yMaxLeft, + ChartAxisLocationEnum::CHART_AXIS_LOCATION_BOTTOM, + bottomAxis, + m_chartOverlaySet->getAxisLabel(bottomAxis), + lineWidthPercentage); + AxisDrawingInfo topAxisInfo(m_textRenderer, + m_viewport, + xMinTop, + xMaxTop, + yMinLeft, + yMaxLeft, + ChartAxisLocationEnum::CHART_AXIS_LOCATION_TOP, + topAxis, + m_chartOverlaySet->getAxisLabel(topAxis), + lineWidthPercentage); + + float topTitleHeight = titleInfo.m_titleHeight; + const float topAxisHeight = topAxisInfo.m_axisHeight; + if (titleInfo.m_titleDisplayedFlag) { + topAxisInfo.m_axisHeight = 0.0f; + } + else { + topTitleHeight = 0.0f; + } + const float bottomAxisHeight = bottomAxisInfo.m_axisHeight; + const float leftAxisWidth = leftAxisInfo.m_axisWidth; + const float rightAxisWidth = rightAxisInfo.m_axisWidth; + + /* + * The constructors were only able to find width or height but not both. + * Now we can find the other value. + */ + titleInfo.setTitleViewport(leftAxisWidth, + rightAxisWidth); + leftAxisInfo.setAxisViewport(topTitleHeight, + bottomAxisHeight, + topAxisHeight, + leftAxisWidth, + rightAxisWidth); + rightAxisInfo.setAxisViewport(topTitleHeight, + bottomAxisHeight, + topAxisHeight, + leftAxisWidth, + rightAxisWidth); + bottomAxisInfo.setAxisViewport(topTitleHeight, + bottomAxisHeight, + topAxisHeight, + leftAxisWidth, + rightAxisWidth); + topAxisInfo.setAxisViewport(topTitleHeight, + bottomAxisHeight, + topAxisHeight, + leftAxisWidth, + rightAxisWidth); + + const float foregroundRGBA[4] = { + m_fixedPipelineDrawing->m_foregroundColorFloat[0], + m_fixedPipelineDrawing->m_foregroundColorFloat[1], + m_fixedPipelineDrawing->m_foregroundColorFloat[2], + 1.0f + }; + + const float graphicsBoxLineThickness = convertPercentageOfViewportToOpenGLLineWidth(m_chartOverlaySet->getAxisLineThickness(), + tabViewportHeight); + leftAxisInfo.setLabelAndNumericsCoordinates(foregroundRGBA, + graphicsBoxLineThickness); + rightAxisInfo.setLabelAndNumericsCoordinates(foregroundRGBA, + graphicsBoxLineThickness); + bottomAxisInfo.setLabelAndNumericsCoordinates(foregroundRGBA, + graphicsBoxLineThickness); + topAxisInfo.setLabelAndNumericsCoordinates(foregroundRGBA, + graphicsBoxLineThickness); + + const bool drawAxesFlag = createChartDrawingViewport(tabViewportX, + tabViewportY, + tabViewportWidth, + tabViewportHeight, + m_chartOverlaySet->getAxisLineThickness(), + topTitleHeight, + bottomAxisHeight, + topAxisHeight, + leftAxisWidth, + rightAxisWidth, + chartGraphicsDrawingViewport); + + if (drawAxesFlag) { + titleInfo.drawTitle(foregroundRGBA); + leftAxisInfo.drawAxis(this, + m_chartOverlaySet, + m_fixedPipelineDrawing->mouseX, + m_fixedPipelineDrawing->mouseY, + foregroundRGBA, + yMinLeft, + yMaxLeft); + rightAxisInfo.drawAxis(this, + m_chartOverlaySet, + m_fixedPipelineDrawing->mouseX, + m_fixedPipelineDrawing->mouseY, + foregroundRGBA, + yMinRight, + yMaxRight); + topAxisInfo.drawAxis(this, + m_chartOverlaySet, + m_fixedPipelineDrawing->mouseX, + m_fixedPipelineDrawing->mouseY, + foregroundRGBA, + xMinTop, + xMaxTop); + bottomAxisInfo.drawAxis(this, + m_chartOverlaySet, + m_fixedPipelineDrawing->mouseX, + m_fixedPipelineDrawing->mouseY, + foregroundRGBA, + xMinBottom, + xMaxBottom); + + drawChartGraphicsBoxAndSetViewport(tabViewportX, + tabViewportY, + tabViewportWidth, + tabViewportHeight, + m_chartOverlaySet->getAxisLineThickness(), + topTitleHeight, + bottomAxisHeight, + topAxisHeight, + leftAxisWidth, + rightAxisWidth, + true, /* draw the box */ + chartGraphicsDrawingViewport); + } + else { + drawChartGraphicsBoxAndSetViewport(tabViewportX, + tabViewportY, + tabViewportWidth, + tabViewportHeight, + m_chartOverlaySet->getAxisLineThickness(), + topTitleHeight, + 0.0f, // bottomAxisHeight, + 0.0f, //topAxisHeight, + 0.0f, //leftAxisWidth, + 0.0f, //rightAxisWidth, + true, /* draw the box */ + chartGraphicsDrawingViewport); + } + + /* + * When the user is editing an axis minimum or maximum value, + * their difference may become zero which will cause + * a problem with the orthographic projection. So + * in this rare instance, make the maximum value + * slightly larger than the minimum value. + */ + const float smallRange = 0.01; + if (xMinBottom >= xMaxBottom) { + xMaxBottom = xMinBottom + smallRange; + } + if (xMinTop >= xMaxTop) { + xMaxTop = xMinBottom + smallRange; + } + if (yMinLeft >= yMaxLeft) { + yMaxLeft = yMinLeft + smallRange; + } + if (yMinRight >= yMaxRight) { + yMaxRight = yMinRight + smallRange; + } + + glViewport(chartGraphicsDrawingViewport[0], + chartGraphicsDrawingViewport[1], + chartGraphicsDrawingViewport[2], + chartGraphicsDrawingViewport[3]); + + + if (drawHistogramFlag) { + /* + * Draw the bars for all histogram and then draw the envelopes + * so the envelopes are not obscured by the bars + */ + for (auto& drawInfo : histogramDrawingInfo) { + if (drawInfo->m_histogramChart->isValid()) { + const CaretMappableDataFile* cmdf = drawInfo->m_histogramChart->getCaretMappableDataFile(); + CaretAssert(cmdf); + const PaletteColorMapping* paletteColorMapping = cmdf->getMapPaletteColorMapping(drawInfo->m_mapIndex); + const bool drawBarsFlag = paletteColorMapping->isHistogramBarsVisible(); + const bool drawEnvelopeFlag = paletteColorMapping->isHistogramEnvelopeVisible(); + + if (drawBarsFlag + || drawEnvelopeFlag) { + bool leftVerticalAxisFlag = true; + switch (drawInfo->m_verticalAxisLocation) { + case ChartAxisLocationEnum::CHART_AXIS_LOCATION_BOTTOM: + break; + case ChartAxisLocationEnum::CHART_AXIS_LOCATION_LEFT: + break; + case ChartAxisLocationEnum::CHART_AXIS_LOCATION_RIGHT: + leftVerticalAxisFlag = false; + break; + case ChartAxisLocationEnum::CHART_AXIS_LOCATION_TOP: + break; + } + + glMatrixMode(GL_PROJECTION); + glLoadIdentity(); + CaretAssert(xMinBottom < xMaxBottom); + if (leftVerticalAxisFlag) { + glOrtho(xMinBottom, xMaxBottom, + yMinLeft, yMaxLeft, + -10.0, 10.0); + } + else { + glOrtho(xMinBottom, xMaxBottom, + yMinRight, yMaxRight, + -10.0, 10.0); + } + + glMatrixMode(GL_MODELVIEW); + glLoadIdentity(); + + bool applyTransformationsFlag = false; + if (applyTransformationsFlag) { + glTranslatef(m_translation[0], + m_translation[1], + 0.0); + + const float chartWidth = chartGraphicsDrawingViewport[2]; + const float chartHeight = chartGraphicsDrawingViewport[3]; + const float halfWidth = chartWidth / 2.0; + const float halfHeight = chartHeight / 2.0; + glTranslatef(halfWidth, + halfHeight, + 0.0); + glScalef(m_zooming, + m_zooming, + 1.0); + glTranslatef(-halfWidth, + -halfHeight, + 0.0); + } + + /* + * Save the transformation matrices and the viewport + * If there is more than one line chart, this code will be executed + * several times but since the top overlay is drawn last, the contents + * of the top overlay will be used. + */ + updateViewportContentForCharting(chartGraphicsDrawingViewport); + + ChartableTwoFileHistogramChart::HistogramPrimitives* histogramPrimitives = + drawInfo->m_histogramChart->getMapHistogramDrawingPrimitives(drawInfo->m_mapIndex, + drawInfo->m_allMapsSelected); + + if (histogramPrimitives != NULL) { + const float lineWidthPercentage = histogramPrimitives->getEnvelopeLineWidthPercentage(); + + if (m_identificationModeFlag) { + int32_t primitiveIndex = -1; + float primitiveDepth = 0.0; + + if (drawBarsFlag) { + /* + * Scale histogram bars in Y-axis so that identification + * will function when mouse is above a bar. This is helpful + * for identification of bars with a very small height. + */ + glPushMatrix(); + glScalef(1.0, 1000.0, 1.0); + GraphicsEngineDataOpenGL::drawWithSelection(histogramPrimitives->getBarsPrimitive(), + m_fixedPipelineDrawing->mouseX, + m_fixedPipelineDrawing->mouseY, + primitiveIndex, + primitiveDepth); + + /* + * Each bar is drawn using two triangles + */ + CaretAssert(histogramPrimitives->getBarsPrimitive()->getPrimitiveType() + == GraphicsPrimitive::PrimitiveType::OPENGL_TRIANGLES); + if (primitiveIndex > 0) { + primitiveIndex /= 2; + } + glPopMatrix(); + } + else if (drawEnvelopeFlag) { + /* + * Increase line width for identification + */ + histogramPrimitives->getEnvelopePrimitive()->setLineWidth(GraphicsPrimitive::LineWidthType::PERCENTAGE_VIEWPORT_HEIGHT, + (lineWidthPercentage + 0.5)); + GraphicsEngineDataOpenGL::drawWithSelection(histogramPrimitives->getEnvelopePrimitive(), + m_fixedPipelineDrawing->mouseX, + m_fixedPipelineDrawing->mouseY, + primitiveIndex, + primitiveDepth); + } + + if (primitiveIndex >= 0) { + if (m_selectionItemHistogram->isOtherScreenDepthCloserToViewer(primitiveDepth)) { + m_selectionItemHistogram->setHistogramChart(const_cast(drawInfo->m_histogramChart), + drawInfo->m_mapIndex, + primitiveIndex, + drawInfo->m_allMapsSelected); + } + } + } + else { + drawPrimitivePrivate(histogramPrimitives->getThresholdPrimitive()); + if (drawBarsFlag) { + drawPrimitivePrivate(histogramPrimitives->getBarsPrimitive()); + } + if (drawEnvelopeFlag) { + histogramPrimitives->getEnvelopePrimitive()->setLineWidth(GraphicsPrimitive::LineWidthType::PERCENTAGE_VIEWPORT_HEIGHT, + lineWidthPercentage); + drawPrimitivePrivate(histogramPrimitives->getEnvelopePrimitive()); + } + } + } + } + } + } + } + if (drawLineSeriesFlag) { + for (const auto lineChart : lineSeriesChartsToDraw) { + bool leftVerticalAxisFlag = true; + switch (lineChart.m_verticalAxisLocation) { + case ChartAxisLocationEnum::CHART_AXIS_LOCATION_BOTTOM: + break; + case ChartAxisLocationEnum::CHART_AXIS_LOCATION_LEFT: + break; + case ChartAxisLocationEnum::CHART_AXIS_LOCATION_RIGHT: + leftVerticalAxisFlag = false; + break; + case ChartAxisLocationEnum::CHART_AXIS_LOCATION_TOP: + break; + } + + glMatrixMode(GL_PROJECTION); + glLoadIdentity(); + CaretAssert(xMinBottom < xMaxBottom); + if (leftVerticalAxisFlag) { + CaretAssert(yMinLeft < yMaxLeft); + glOrtho(xMinBottom, xMaxBottom, + yMinLeft, yMaxLeft, + -10.0, 10.0); + } + else { + CaretAssert(yMinRight < yMaxRight); + glOrtho(xMinBottom, xMaxBottom, + yMinRight, yMaxRight, + -10.0, 10.0); + } + + glMatrixMode(GL_MODELVIEW); + glLoadIdentity(); + + bool applyTransformationsFlag = false; + if (applyTransformationsFlag) { + glTranslatef(m_translation[0], + m_translation[1], + 0.0f); + + const float chartWidth = chartGraphicsDrawingViewport[2]; + const float chartHeight = chartGraphicsDrawingViewport[3]; + const float halfWidth = chartWidth / 2.0f; + const float halfHeight = chartHeight / 2.0f; + glTranslatef(halfWidth, + halfHeight, + 0.0f); + glScalef(m_zooming, + m_zooming, + 1.0f); + glTranslatef(-halfWidth, + -halfHeight, + 0.0f); + } + + if (m_identificationModeFlag) { + int32_t primitiveIndex = -1; + float primitiveDepth = 0.0f; + + GraphicsEngineDataOpenGL::drawWithSelection(lineChart.m_chartTwoCartesianData->getGraphicsPrimitive(), + m_fixedPipelineDrawing->mouseX, + m_fixedPipelineDrawing->mouseY, + primitiveIndex, + primitiveDepth); + + if (primitiveIndex >= 0) { + if (m_selectionItemLineSeries->isOtherScreenDepthCloserToViewer(primitiveDepth)) { + m_selectionItemLineSeries->setLineSeriesChart(const_cast(lineChart.m_lineSeriesChart), + const_cast(lineChart.m_chartTwoCartesianData), + primitiveIndex); + } + } + } + else { + GraphicsEngineDataOpenGL::draw(lineChart.m_chartTwoCartesianData->getGraphicsPrimitive()); + } + + /* + * Save the transformation matrices and the viewport + * If there is more than one line chart, this code will be executed + * several times but since the top overlay is drawn last, the contents + * of the top overlay will be used. + */ + updateViewportContentForCharting(chartGraphicsDrawingViewport); + } + } + } +} + +/** + * Draw a matrix chart. + */ +void +BrainOpenGLChartTwoDrawingFixedPipeline::drawMatrixChart() +{ + const int32_t tabViewportX = m_viewport[0]; + const int32_t tabViewportY = m_viewport[1]; + const int32_t tabViewportWidth = m_viewport[2]; + const int32_t tabViewportHeight = m_viewport[3]; + + int32_t chartGraphicsDrawingViewport[4] = { + tabViewportX, + tabViewportY, + tabViewportWidth, + tabViewportHeight + }; + + /* + * Estimate size of title + */ + float titleHeight = 0.0f; + ChartTwoTitle* chartTitle = m_chartOverlaySet->getChartTitle(); + TitleDrawingInfo titleInfo(m_textRenderer, + m_viewport, + chartTitle); + + float bottomMargin = 3.0f; + float topMargin = 3.0f; + float leftMargin = 3.0f; + float rightMargin = 3.0f; + if (titleInfo.m_titleDisplayedFlag) { + topMargin = 0.0f; + titleHeight = titleInfo.m_titleHeight; + } + + titleInfo.setTitleViewport(leftMargin, + rightMargin); + + glViewport(tabViewportX, + tabViewportY, + tabViewportWidth, + tabViewportHeight); + + const float foregroundRGBA[4] = { + m_fixedPipelineDrawing->m_foregroundColorFloat[0], + m_fixedPipelineDrawing->m_foregroundColorFloat[1], + m_fixedPipelineDrawing->m_foregroundColorFloat[2], + 1.0f + }; + titleInfo.drawTitle(foregroundRGBA); + + drawChartGraphicsBoxAndSetViewport(tabViewportX, + tabViewportY, + tabViewportWidth, + tabViewportHeight, + m_chartOverlaySet->getAxisLineThickness(), + titleHeight, + bottomMargin, + topMargin, + leftMargin, + rightMargin, + false, /* do not draw box */ + chartGraphicsDrawingViewport); + + glViewport(chartGraphicsDrawingViewport[0], + chartGraphicsDrawingViewport[1], + chartGraphicsDrawingViewport[2], + chartGraphicsDrawingViewport[3]); + + bool applyTransformationsFlag = true; + float cellWidth = 1.0; + float cellHeight = 1.0; + + /* + * Setup width/height of area in which matrix is drawn with a + * small margin along all of the edges + */ + float margin = 10.0; + if ((m_viewport[2] < (margin * 3.0)) + || (m_viewport[3] < (margin * 3.0))) { + margin = 0.0; + } + const float graphicsWidth = chartGraphicsDrawingViewport[2]; + const float graphicsHeight = chartGraphicsDrawingViewport[3]; + + /* + * First overlay is ALWAYS ON and since all matrices must have + * same number of rows/columns, use first matrix for rows/columns + */ + const int32_t numberOfOverlays = m_chartOverlaySet->getNumberOfDisplayedOverlays(); + CaretAssert(numberOfOverlays > 0); + const ChartTwoOverlay* topOverlay = m_chartOverlaySet->getOverlay(0); + const ChartTwoCompoundDataType cdt = topOverlay->getChartTwoCompoundDataType(); + CaretAssert(cdt.getChartTwoDataType() == ChartTwoDataTypeEnum::CHART_DATA_TYPE_MATRIX); + const int32_t numberOfRows = cdt.getMatrixNumberOfRows(); + const int32_t numberOfCols = cdt.getMatrixNumberOfColumns(); + if ((numberOfRows > 0) + && (numberOfCols > 0)) { + cellWidth = graphicsWidth / numberOfCols; + cellHeight = graphicsHeight / numberOfRows; + } + else { + return; + } + + /* + * Set the width and neight of each matrix cell. + */ + const ChartTwoMatrixDisplayProperties* matrixProperties = m_browserTabContent->getChartTwoMatrixDisplayProperties(); + CaretAssert(matrixProperties); + const float cellWidthZoom = matrixProperties->getCellPercentageZoomWidth() / 100.0; + const float cellHeightZoom = matrixProperties->getCellPercentageZoomHeight() / 100.0; + if ((cellWidthZoom > 0.0) + && (cellHeightZoom > 0.0)) { + cellWidth *= cellWidthZoom; + cellHeight *= cellHeightZoom; + } + + /* + * Set the coordinates for the area in which the matrix is drawn. + */ + margin = 0.0; + const float xMin = -margin; + const float xMax = graphicsWidth + margin; + const float yMin = -margin; + const float yMax = graphicsHeight + margin; + + glMatrixMode(GL_PROJECTION); + glLoadIdentity(); + glOrtho(xMin, xMax, + yMin, yMax, + -1.0, 1.0); + + glMatrixMode(GL_MODELVIEW); + glLoadIdentity(); + + if (applyTransformationsFlag) { + glTranslatef(m_translation[0], + m_translation[1], + 0.0); + + const float chartWidth = cellWidth * numberOfCols; + const float chartHeight = cellHeight * numberOfRows; + const float halfWidth = chartWidth / 2.0; + const float halfHeight = chartHeight / 2.0; + glTranslatef(halfWidth, + halfHeight, + 0.0); + glScalef(m_zooming, + m_zooming, + 1.0); + glTranslatef(-halfWidth, + -halfHeight, + 0.0); + } + + /* + * Save the transformation matrices and the viewport + * If there is more than one line chart, this code will be executed + * several times but since the top overlay is drawn last, the contents + * of the top overlay will be used. + */ + updateViewportContentForCharting(chartGraphicsDrawingViewport); + + std::vector rowColumnHighlighting; + + for (int32_t iOverlay = (numberOfOverlays - 1); iOverlay >= 0; iOverlay--) { + ChartTwoOverlay* chartOverlay = m_chartOverlaySet->getOverlay(iOverlay); + if ( ! chartOverlay->isEnabled()) { + continue; + } + + CaretMappableDataFile* mapFile = NULL; + ChartTwoOverlay::SelectedIndexType selectedIndexType = ChartTwoOverlay::SelectedIndexType::INVALID; + int32_t selectedIndex = -1; + chartOverlay->getSelectionData(mapFile, + selectedIndexType, + selectedIndex); + if (mapFile == NULL) { + continue; + } + + const ChartableTwoFileMatrixChart* matrixChart = mapFile->getChartingDelegate()->getMatrixCharting(); + int32_t overlayRows = 0; + int32_t overlayColumns = 0; + matrixChart->getMatrixDimensions(overlayRows, + overlayColumns); + + /* + * All matrices must have same rows/columns + */ + if ((overlayRows == numberOfRows) + && (overlayColumns == numberOfCols)) { + drawMatrixChartContent(matrixChart, + chartOverlay->getMatrixTriangularViewingMode(), + cellWidth, + cellHeight, + m_zooming, + rowColumnHighlighting); + } + } + + /* + * Row/column highlighting must be drawn after matrices to that + * the highlights are over any and all displayed matrices + */ + const int32_t numHighlights = static_cast(rowColumnHighlighting.size()); + if (numHighlights > 0) { + for (int32_t ctr = 0; ctr < numHighlights; ctr++) { + MatrixRowColumnHighight* mrch = rowColumnHighlighting[ctr]; + CaretAssert(mrch); + + glPushMatrix(); + glLoadMatrixf(mrch->m_modelViewMatrix); + + drawPrimitivePrivate(mrch->m_graphicsPrimitive.get()); + delete mrch; + + glPopMatrix(); + } + rowColumnHighlighting.clear(); + } +} + +/* + * Draw a matrix chart content. + * + * @param matrixChart + * Matrix chart that is drawn. + * @param chartViewingType + * Type of chart viewing. + * @param cellWidth + * Width of cell. + * @param cellHeight + * Height of cell. + * @param zooming + * Current zooming. + */ +void +BrainOpenGLChartTwoDrawingFixedPipeline::drawMatrixChartContent(const ChartableTwoFileMatrixChart* matrixChart, + const ChartTwoMatrixTriangularViewingModeEnum::Enum chartViewingType, + const float cellWidth, + const float cellHeight, + const float /*zooming*/, + std::vector& rowColumnHighlightingOut) +{ + GraphicsPrimitiveV3fC4f* matrixPrimitive = matrixChart->getMatrixChartingGraphicsPrimitive(chartViewingType, + CiftiMappableDataFile::MatrixGridMode::FILLED); + if (matrixPrimitive == NULL) { + return; + } + + if (m_identificationModeFlag) { + glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); + } + + glPushMatrix(); + glScalef(cellWidth, cellHeight, 1.0); + /* + * Enable alpha blending so voxels that are not drawn from higher layers + * allow voxels from lower layers to be seen. + */ + glEnable(GL_BLEND); + glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); + + if (m_identificationModeFlag) { + int32_t primitiveIndex = -1; + float primitiveDepth = 0.0; + GraphicsEngineDataOpenGL::drawWithSelection(matrixPrimitive, + m_fixedPipelineDrawing->mouseX, + m_fixedPipelineDrawing->mouseY, + primitiveIndex, + primitiveDepth); + if (primitiveIndex >= 0) { + /* + * Two triangles per cell so divided the primitive index by two + */ + CaretAssert(matrixPrimitive->getPrimitiveType() == GraphicsPrimitive::PrimitiveType::OPENGL_TRIANGLES); + primitiveIndex /= 2; + + int32_t numberOfRows = 0; + int32_t numberOfColumns = 0; + matrixChart->getMatrixDimensions(numberOfRows, + numberOfColumns); + + const int32_t rowIndex = primitiveIndex / numberOfColumns; + const int32_t colIndex = primitiveIndex % numberOfColumns; + + if (m_selectionItemMatrix->isOtherScreenDepthCloserToViewer(primitiveDepth)) { + m_selectionItemMatrix->setMatrixChart(const_cast(matrixChart), + rowIndex, + colIndex); + } + } + } + else { + drawPrimitivePrivate(matrixPrimitive); + + const ChartTwoMatrixDisplayProperties* matrixProperties = m_browserTabContent->getChartTwoMatrixDisplayProperties(); + CaretAssert(matrixProperties); + + if (matrixProperties->isGridLinesDisplayed()) { + GraphicsPrimitiveV3fC4f* matrixGridPrimitive = matrixChart->getMatrixChartingGraphicsPrimitive(chartViewingType, + CiftiMappableDataFile::MatrixGridMode::OUTLINE); + drawPrimitivePrivate(matrixGridPrimitive); + } + + int32_t numberOfRows = 0; + int32_t numberOfColumns = 0; + matrixChart->getMatrixDimensions(numberOfRows, + numberOfColumns); + + if (matrixProperties->isSelectedRowColumnHighlighted()) { + uint8_t highlightRGBByte[3]; + m_preferences->getBackgroundAndForegroundColors()->getColorForegroundChartView(highlightRGBByte); + const float highlightRGBA[4] = { + highlightRGBByte[0] / 255.0f, + highlightRGBByte[1] / 255.0f, + highlightRGBByte[2] / 255.0f, + 1.0f + }; + + std::vector selectedColumnIndices; + std::vector selectedRowIndices; + ChartTwoMatrixLoadingDimensionEnum::Enum selectedRowColumnDimension; + matrixChart->getSelectedRowColumnIndices(m_tabIndex, + selectedRowColumnDimension, + selectedRowIndices, + selectedColumnIndices); + + /* + * If true, selected row highlighting will be limited + * to a upper or lower triangular cells when + * triangular display is selected + * Disabled by WB-741 + */ + const bool limitSelectionToTriangularFlag = false; + const float lineWidthPercentageHeight = 1.0f; + + for (auto rowIndex : selectedRowIndices) { + float minX = 0; + float maxX = numberOfColumns; + const float minY = numberOfRows - rowIndex - 1; + const float maxY = minY + 1.0f; + + if (limitSelectionToTriangularFlag) { + switch (chartViewingType) { + case ChartTwoMatrixTriangularViewingModeEnum::MATRIX_VIEW_FULL: + break; + case ChartTwoMatrixTriangularViewingModeEnum::MATRIX_VIEW_FULL_NO_DIAGONAL: + break; + case ChartTwoMatrixTriangularViewingModeEnum::MATRIX_VIEW_LOWER_NO_DIAGONAL: + maxX = rowIndex; /* matrix is symmetric */ + break; + case ChartTwoMatrixTriangularViewingModeEnum::MATRIX_VIEW_UPPER_NO_DIAGONAL: + minX = rowIndex + 1; /* matrix is symmetric */ + break; + } + } + + GraphicsPrimitiveV3fC4f* rowOutlineData = GraphicsPrimitiveV3fC4f::newPrimitiveV3fC4f(GraphicsPrimitive::PrimitiveType::POLYGONAL_LINE_LOOP_MITER_JOIN); + rowOutlineData->reserveForNumberOfVertices(4); + rowOutlineData->addVertex(minX, minY, highlightRGBA); + rowOutlineData->addVertex(maxX, minY, highlightRGBA); + rowOutlineData->addVertex(maxX, maxY, highlightRGBA); + rowOutlineData->addVertex(minX, maxY, highlightRGBA); + rowOutlineData->setLineWidth(GraphicsPrimitive::LineWidthType::PERCENTAGE_VIEWPORT_HEIGHT, + lineWidthPercentageHeight); + + float modelViewMatrix[16]; + glGetFloatv(GL_MODELVIEW_MATRIX, modelViewMatrix); + rowColumnHighlightingOut.push_back(new MatrixRowColumnHighight(rowOutlineData, + modelViewMatrix)); + } + + for (auto columnIndex : selectedColumnIndices) { + const float minX = columnIndex; + const float maxX = columnIndex + 1; + float minY = 0; + float maxY = numberOfRows; + + if (limitSelectionToTriangularFlag) { + switch (chartViewingType) { + case ChartTwoMatrixTriangularViewingModeEnum::MATRIX_VIEW_FULL: + break; + case ChartTwoMatrixTriangularViewingModeEnum::MATRIX_VIEW_FULL_NO_DIAGONAL: + break; + case ChartTwoMatrixTriangularViewingModeEnum::MATRIX_VIEW_LOWER_NO_DIAGONAL: + maxY = columnIndex; /* matrix is symmetric */ + break; + case ChartTwoMatrixTriangularViewingModeEnum::MATRIX_VIEW_UPPER_NO_DIAGONAL: + minY = columnIndex + 1; /* matrix is symmetric */ + break; + } + } + +// const float highlightLineWidth = std::max(((zooming) * 0.20), 3.0); + + GraphicsPrimitiveV3fC4f* columnOutlineData = GraphicsPrimitiveV3fC4f::newPrimitiveV3fC4f(GraphicsPrimitive::PrimitiveType::POLYGONAL_LINE_LOOP_MITER_JOIN); + columnOutlineData->reserveForNumberOfVertices(4); + columnOutlineData->addVertex(minX, minY, highlightRGBA); + columnOutlineData->addVertex(maxX, minY, highlightRGBA); + columnOutlineData->addVertex(maxX, maxY, highlightRGBA); + columnOutlineData->addVertex(minX, maxY, highlightRGBA); + columnOutlineData->setLineWidth(GraphicsPrimitive::LineWidthType::PERCENTAGE_VIEWPORT_HEIGHT, + lineWidthPercentageHeight); + + float modelViewMatrix[16]; + glGetFloatv(GL_MODELVIEW_MATRIX, modelViewMatrix); + rowColumnHighlightingOut.push_back(new MatrixRowColumnHighight(columnOutlineData, + modelViewMatrix)); + } + } + } + + glDisable(GL_BLEND); + glPopMatrix(); +} + +/** + * Save the state of OpenGL. + * Copied from Qt's qgl.cpp, qt_save_gl_state(). + */ +void +BrainOpenGLChartTwoDrawingFixedPipeline::saveStateOfOpenGL() +{ + glPushClientAttrib(GL_CLIENT_ALL_ATTRIB_BITS); + glPushAttrib(GL_ALL_ATTRIB_BITS); + glMatrixMode(GL_TEXTURE); + glPushMatrix(); + glLoadIdentity(); + glMatrixMode(GL_PROJECTION); + glPushMatrix(); + glMatrixMode(GL_MODELVIEW); + glPushMatrix(); + + glShadeModel(GL_FLAT); + glDisable(GL_LIGHTING); + glDisable(GL_STENCIL_TEST); + glDisable(GL_DEPTH_TEST); + glDisable(GL_BLEND); +} + +/** + * Restore the state of OpenGL. + * Copied from Qt's qgl.cpp, qt_restore_gl_state(). + */ +void +BrainOpenGLChartTwoDrawingFixedPipeline::restoreStateOfOpenGL() +{ + glMatrixMode(GL_TEXTURE); + glPopMatrix(); + glMatrixMode(GL_PROJECTION); + glPopMatrix(); + glMatrixMode(GL_MODELVIEW); + glPopMatrix(); + glPopAttrib(); + glPopClientAttrib(); + +} + +/** + * Convert a percentage of viewport width or height to an OpenGL Line Width. + * When percentage width or height is zero, zero will be returned. + * Otherwise the returned value will ALWAYS be at least one. + * + * + * @param percentageWidthOrHeight + * The percentage of width or height (ranges 0.0 to 100.0). + * @param viewportWidthOrHeight + * The viewport width or height. + * @return + * Pixels size value. + */ +float +BrainOpenGLChartTwoDrawingFixedPipeline::convertPercentageOfViewportToOpenGLLineWidth(const float percentageWidthOrHeight, + const float viewportWidthOrHeight) +{ + if (percentageWidthOrHeight <= 0.0f) { + return 0.0f; + } + + float pixelsWidthOrHeight = ((percentageWidthOrHeight / 100.0f) * viewportWidthOrHeight); + + if (pixelsWidthOrHeight < s_minimumLineWidthOpenGL) { + pixelsWidthOrHeight = s_minimumLineWidthOpenGL; + } +// if (pixelsWidthOrHeight > s_maximumLineWidthOpenGL) { +// pixelsWidthOrHeight = s_maximumLineWidthOpenGL; +// } + return pixelsWidthOrHeight; +} + +/** + * Convert a percentage of viewport width or height to pixels. + * When percentage width or height is zero, zero will be returned. + * Otherwise the returned value will ALWAYS be at least one. + * + * @param percentageWidthOrHeight + * The percentage of width or height (ranges 0.0 to 100.0). + * @param viewportWidthOrHeight + * The viewport width or height. + * @return + * Pixels size value. + */ +float +BrainOpenGLChartTwoDrawingFixedPipeline::convertPercentageOfViewportToPixels(const float percentageWidthOrHeight, + const float viewportWidthOrHeight) +{ + if (percentageWidthOrHeight <= 0.0f) { + return 0.0f; + } + + float pixelsWidthOrHeight = ((percentageWidthOrHeight / 100.0f) * viewportWidthOrHeight); + + return pixelsWidthOrHeight; +} + +/** + * Draw the chart graphics surrounding box and set the graphics viewport. + * + * @param vpX + * Viewport X + * @param vpY + * Viewport Y + * @param vpWidth + * Viewport width + * @param vpHeight + * Viewport height + * @param lineThicknessPercentage + * Thickness of lines in percentage of viewport height + * @param titleHeight + * Height of the title + * @param bottomAxisHeight + * Height of bottom axis + * @param topAxisHeight + * Height of top axis + * @param leftAxisWidth + * Width of left axis + * @param rightAxisWidth + * Width of right axis + * @param drawBoxFlag + * Controls drawing of box. + * @param chartGraphicsDrawingViewportOut + * Output containing viewport for drawing chart graphics within + * the box/grid that is adjusted for the box's line thickness. + */ +void +BrainOpenGLChartTwoDrawingFixedPipeline::drawChartGraphicsBoxAndSetViewport(const float vpX, + const float vpY, + const float vpWidth, + const float vpHeight, + const float lineThicknessPercentage, + const float titleHeight, + const float bottomAxisHeight, + const float topAxisHeight, + const float leftAxisWidth, + const float rightAxisWidth, + const bool drawBoxFlag, + int32_t chartGraphicsDrawingViewportOut[4]) +{ + const float lineThicknessPixels = convertPercentageOfViewportToOpenGLLineWidth(lineThicknessPercentage, + vpHeight); + const float halfGridLineWidth = lineThicknessPixels / 2.0; + + glViewport(vpX, + vpY, + vpWidth, + vpHeight); + + if (drawBoxFlag) { + /* + * For box, set coordinates for the middle of the line + */ + const float boxLeft = leftAxisWidth + halfGridLineWidth; + const float boxRight = vpWidth - rightAxisWidth - halfGridLineWidth; + const float boxBottom = bottomAxisHeight + halfGridLineWidth; + const float boxTop = vpHeight - topAxisHeight - titleHeight - halfGridLineWidth; + + /* + * We adjust the horizontal lines by half the line width. + * Otherwise, there will be 'corner gaps' where the horizontal + * and vertical lines are joined + */ + std::unique_ptr boxData + = std::unique_ptr(GraphicsPrimitive::newPrimitiveV3f(GraphicsPrimitive::PrimitiveType::POLYGONAL_LINE_LOOP_MITER_JOIN, + m_fixedPipelineDrawing->m_foregroundColorFloat)); + boxData->reserveForNumberOfVertices(4); + boxData->addVertex(boxLeft, boxBottom); + boxData->addVertex(boxRight, boxBottom); + boxData->addVertex(boxRight, boxTop); + boxData->addVertex(boxLeft, boxTop); + boxData->setLineWidth(GraphicsPrimitive::LineWidthType::PERCENTAGE_VIEWPORT_HEIGHT, + lineThicknessPercentage); + drawPrimitivePrivate(boxData.get()); + } + + /* + * Region INSIDE the grid's box so that any data plots + * are WITHIN the box + */ + const float graphicsLeft = vpX + leftAxisWidth + lineThicknessPixels; + const float graphicsRight = vpX + vpWidth - rightAxisWidth - lineThicknessPixels; + const float graphicsBottom = vpY + bottomAxisHeight + lineThicknessPixels; + const float graphicsTop = vpY + vpHeight - topAxisHeight - titleHeight - lineThicknessPixels; + + const int32_t graphicsWidth = graphicsRight - graphicsLeft; + const int32_t graphicsHeight = graphicsTop - graphicsBottom; + chartGraphicsDrawingViewportOut[0] = graphicsLeft; + chartGraphicsDrawingViewportOut[1] = graphicsBottom; + chartGraphicsDrawingViewportOut[2] = graphicsWidth; + chartGraphicsDrawingViewportOut[3] = graphicsHeight; + + CaretAssert(graphicsWidth > 0); + CaretAssert(graphicsHeight > 0); +} + +/** + * Create the graphics viewport. + * + * @param vpX + * Viewport X + * @param vpY + * Viewport Y + * @param vpWidth + * Viewport width + * @param vpHeight + * Viewport height + * @param lineThicknessPercentage + * Thickness of lines in percentage of viewport height + * @param titleHeight + * Height of the title + * @param bottomAxisHeight + * Height of bottom axis + * @param topAxisHeight + * Height of top axis + * @param leftAxisWidth + * Width of left axis + * @param rightAxisWidth + * Width of right axis + * @param chartGraphicsDrawingViewportOut + * Output containing viewport for drawing chart graphics within + * the box/grid that is adjusted for the box's line thickness. + * @return + * True if axes should be drawn, otherwise false. When chart + * gets too small, axes are not drawn. + */ +bool +BrainOpenGLChartTwoDrawingFixedPipeline::createChartDrawingViewport(const float vpX, + const float vpY, + const float vpWidth, + const float vpHeight, + const float lineThicknessPercentage, + const float titleHeight, + const float bottomAxisHeight, + const float topAxisHeight, + const float leftAxisWidth, + const float rightAxisWidth, + int32_t chartGraphicsDrawingViewportOut[4]) +{ + const float lineThicknessPixels = convertPercentageOfViewportToOpenGLLineWidth(lineThicknessPercentage, + vpHeight); + /* + * Region INSIDE the grid's box so that any data plots + * are WITHIN the box + */ + float graphicsLeft = vpX + leftAxisWidth + lineThicknessPixels; + float graphicsRight = vpX + vpWidth - rightAxisWidth - lineThicknessPixels; + float graphicsBottom = vpY + bottomAxisHeight + lineThicknessPixels; + float graphicsTop = vpY + vpHeight - topAxisHeight - titleHeight - lineThicknessPixels; + + int32_t graphicsWidth = graphicsRight - graphicsLeft; + int32_t graphicsHeight = graphicsTop - graphicsBottom; + + bool drawAxesFlag = true; + + const int32_t minViewportSize = 20; + if ((graphicsWidth < minViewportSize) + || (graphicsHeight < minViewportSize)) { + graphicsLeft = vpX; + graphicsRight = vpX + vpWidth; + graphicsBottom = vpY; + graphicsTop = vpY + vpHeight; + + graphicsWidth = graphicsRight - graphicsLeft; + graphicsHeight = graphicsTop - graphicsBottom; + drawAxesFlag = false; + } + + chartGraphicsDrawingViewportOut[0] = graphicsLeft; + chartGraphicsDrawingViewportOut[1] = graphicsBottom; + chartGraphicsDrawingViewportOut[2] = graphicsWidth; + chartGraphicsDrawingViewportOut[3] = graphicsHeight; + + CaretAssert(graphicsWidth > 0); + CaretAssert(graphicsHeight > 0); + + return drawAxesFlag; +} + +/** + * Update the viewport content with charting viewport and matrices. + * + * @param viewport + * The viewport. + */ +void +BrainOpenGLChartTwoDrawingFixedPipeline::updateViewportContentForCharting(const int32_t viewport[4]) +{ + CaretAssert(viewport[2] > 0); + CaretAssert(viewport[3] > 0); + + GLfloat modelviewArray[16]; + glGetFloatv(GL_MODELVIEW_MATRIX, modelviewArray); + GLfloat projectionArray[16]; + glGetFloatv(GL_PROJECTION_MATRIX, projectionArray); + Matrix4x4 modelviewMatrix; + modelviewMatrix.setMatrixFromOpenGL(modelviewArray); + Matrix4x4 projectionMatrix; + projectionMatrix.setMatrixFromOpenGL(projectionArray); + m_viewportContent->setChartDataMatricesAndViewport(projectionMatrix, + modelviewMatrix, + viewport); +} + +/** + * Draw the graphics primitive. + * + * @param primitive + * Primitive that will be drawn. + */ +void +BrainOpenGLChartTwoDrawingFixedPipeline::drawPrimitivePrivate(GraphicsPrimitive* primitive) +{ + if (primitive == NULL) { + return; + } + + GraphicsEngineDataOpenGL::draw(primitive); +} + +/** + * Constructor. + * + * @param histogramChart + * The file's histogram charting + * @param mapIndex + * Index of the map for which histogram is displayed. + * @param verticalAxisLocation + * Location of vertical axis for the histogram + * @param allMapsSelected + * True if ALL MAPS selected for histogram, else false. + */ +BrainOpenGLChartTwoDrawingFixedPipeline::HistogramChartDrawingInfo::HistogramChartDrawingInfo(ChartableTwoFileHistogramChart* histogramChart, + int32_t mapIndex, + ChartAxisLocationEnum::Enum verticalAxisLocation, + const bool allMapsSelected) +: +m_histogramChart(histogramChart), +m_mapIndex(mapIndex), +m_verticalAxisLocation(verticalAxisLocation), +m_allMapsSelected(allMapsSelected) +{ +} + +/** + * Destructor. + */ +BrainOpenGLChartTwoDrawingFixedPipeline::HistogramChartDrawingInfo::~HistogramChartDrawingInfo() +{ +} + +/** + * Constructor. + * + * @param textRenderer + * The text renderer. + * @param tabViewport + * Viewport of the tab containing the chart. + * @param dataMinX + * Minimum X-data value. + * @param dataMaxX + * Maximum X-data value. + * @param dataMinY + * Minimum Y-data value. + * @param dataMaxX + * Maximum Y-data value. + * @param axisLocation + * Location of axis as 'axis' may be NULL. + * @param axis + * Axis being setup. + * @param labelText + * Text for the label. + * @param lineWidthPercentage + * The line width percentage. + */ +BrainOpenGLChartTwoDrawingFixedPipeline::AxisDrawingInfo::AxisDrawingInfo(BrainOpenGLTextRenderInterface* textRenderer, + const int32_t tabViewport[4], + const float dataMinX, + const float dataMaxX, + const float dataMinY, + const float dataMaxY, + const ChartAxisLocationEnum::Enum axisLocation, + const ChartTwoCartesianAxis* axis, + const AString& labelText, + const float lineWidthPercentage) +: m_axisLocation(axisLocation), +m_axis(axis), +m_textRenderer(textRenderer), +m_tabViewportWidth(tabViewport[2]), +m_tabViewportHeight(tabViewport[3]) +{ + m_axisValid = false; + m_axisWidth = 0.0f; + m_axisHeight = 0.0f; + switch (axisLocation) { + case ChartAxisLocationEnum::CHART_AXIS_LOCATION_BOTTOM: + case ChartAxisLocationEnum::CHART_AXIS_LOCATION_TOP: + m_axisMinimumValue = dataMinX; + m_axisMaximumValue = dataMaxX; + break; + case ChartAxisLocationEnum::CHART_AXIS_LOCATION_LEFT: + case ChartAxisLocationEnum::CHART_AXIS_LOCATION_RIGHT: + m_axisMinimumValue = dataMinY; + m_axisMaximumValue = dataMaxY; + break; + } + float dataMinimumValue = m_axisMinimumValue; + float dataMaximumValue = m_axisMaximumValue; + + m_axisDisplayedFlag = false; + if (m_axis != NULL) { + CaretAssert(m_axis->getAxisLocation() == axisLocation); + m_axisDisplayedFlag = (axis->isEnabledByChart() + && axis->isDisplayedByUser()); + } + if ( ! m_axisDisplayedFlag) { + /* + * If axis is not displayed, use padding with a small percentage of space. + */ + const float emptyAxisPaddingPercentage = 1.0f; + switch (axisLocation) { + case ChartAxisLocationEnum::CHART_AXIS_LOCATION_BOTTOM: + case ChartAxisLocationEnum::CHART_AXIS_LOCATION_TOP: + m_axisHeight = convertPercentageOfViewportToPixels(emptyAxisPaddingPercentage, + m_tabViewportHeight); + m_labelPaddingSizePixels = m_axisHeight; + break; + case ChartAxisLocationEnum::CHART_AXIS_LOCATION_LEFT: + case ChartAxisLocationEnum::CHART_AXIS_LOCATION_RIGHT: + m_axisWidth = convertPercentageOfViewportToPixels(emptyAxisPaddingPercentage, + m_tabViewportWidth); + m_labelPaddingSizePixels = m_axisWidth; + break; + } + + /* + * This is necessary so that the correct axis minimum and + * maximum values (m_axisMinimumValue and m_axisMaximumValue). + */ + if (m_axis != NULL) { + std::vector scaleValuePositions; + std::vector scaleValuesText; + m_axis->getScaleValuesAndOffsets(dataMinimumValue, + dataMaximumValue, + 1.0, + m_axisMinimumValue, + m_axisMaximumValue, + scaleValuePositions, + scaleValuesText); + } + + return; + } + + if (m_axis == NULL) { + return; + } + m_axisValid = true; + + /* + * Padding values are percentages so convert to pixels. + */ + const float numericsTicksPaddingPercentage = 1.0f; + m_labelPaddingSizePixels = 0.0f; + m_numericsTicksPaddingSizePixels = 0.0f; + switch (axisLocation) { + case ChartAxisLocationEnum::CHART_AXIS_LOCATION_BOTTOM: + case ChartAxisLocationEnum::CHART_AXIS_LOCATION_TOP: + m_labelPaddingSizePixels = convertPercentageOfViewportToPixels(axis->getPaddingSize(), + m_tabViewportHeight); + m_numericsTicksPaddingSizePixels = convertPercentageOfViewportToPixels(numericsTicksPaddingPercentage, + m_tabViewportHeight); + break; + case ChartAxisLocationEnum::CHART_AXIS_LOCATION_LEFT: + case ChartAxisLocationEnum::CHART_AXIS_LOCATION_RIGHT: + m_labelPaddingSizePixels = convertPercentageOfViewportToPixels(axis->getPaddingSize(), + m_tabViewportHeight); + m_numericsTicksPaddingSizePixels = convertPercentageOfViewportToPixels(numericsTicksPaddingPercentage, + m_tabViewportHeight); + break; + } + + m_lineDrawingWidth = convertPercentageOfViewportToOpenGLLineWidth(lineWidthPercentage, + m_tabViewportHeight); + m_tickLength = 0.0f; + if (m_axis->isShowTickmarks()) { + const float tickLengthPercentage = 1.0f; + m_tickLength = convertPercentageOfViewportToOpenGLLineWidth(tickLengthPercentage, m_tabViewportHeight); + //m_tickLength = m_lineDrawingWidth; // * 2.0f; + } + + /* + * Width/height of label + */ + initializeLabel(labelText, m_labelWidth, m_labelHeight); + + /* + * Maximum width/height of numeric values + */ + float numericsWidth = 0.0f; + float numericsHeight = 0.0f; + initializeNumericText(dataMinimumValue, dataMaximumValue, + m_axis->isNumericsTextDisplayed(), + m_axis->isNumericsTextRotated(), + numericsWidth, numericsHeight); + + /* + * For the bottom and the top, we now have its height but we do not yet know its width + * and likewise for left and right. + */ + switch (axisLocation) { + case ChartAxisLocationEnum::CHART_AXIS_LOCATION_BOTTOM: + case ChartAxisLocationEnum::CHART_AXIS_LOCATION_TOP: + m_axisHeight = std::ceil(m_labelPaddingSizePixels + m_labelHeight + numericsHeight + m_numericsTicksPaddingSizePixels + m_tickLength); + CaretAssert(m_axisHeight >= 0.0f); + break; + case ChartAxisLocationEnum::CHART_AXIS_LOCATION_LEFT: + case ChartAxisLocationEnum::CHART_AXIS_LOCATION_RIGHT: + /* Note: label is rotated on left/right so use it's height */ + m_axisWidth = std::ceil(m_labelPaddingSizePixels + m_labelHeight + numericsWidth + m_numericsTicksPaddingSizePixels + m_tickLength); + CaretAssert(m_axisWidth >= 0.0f); + break; + } +} + +/** + * Initialize the numeric label by creating an annotation for the label + * and determining the width and height of the label. + * + * @param labelText + * Text of the label. + * @param widthOut + * Output with width of label. + * @param heightOut + * Output with hight of label. + */ +void +BrainOpenGLChartTwoDrawingFixedPipeline::AxisDrawingInfo::initializeLabel(const AString& labelText, + float& widthOut, + float& heightOut) +{ + widthOut = 0.0f; + heightOut = 0.0f; + + if (m_axis == NULL) { + return; + } + + AnnotationPercentSizeText* text = new AnnotationPercentSizeText(AnnotationAttributesDefaultTypeEnum::NORMAL, + AnnotationTextFontSizeTypeEnum::PERCENTAGE_OF_VIEWPORT_HEIGHT); + if (m_axis->isShowLabel()) { + float xyz[3] = { 0.0f, 0.0f, 0.0f }; + switch (m_axisLocation) { + case ChartAxisLocationEnum::CHART_AXIS_LOCATION_BOTTOM: + xyz[0] = 0.5f; + text->setVerticalAlignment(AnnotationTextAlignVerticalEnum::BOTTOM); + text->setHorizontalAlignment(AnnotationTextAlignHorizontalEnum::CENTER); + break; + case ChartAxisLocationEnum::CHART_AXIS_LOCATION_TOP: + xyz[0] = 0.5f; + text->setVerticalAlignment(AnnotationTextAlignVerticalEnum::TOP); + text->setHorizontalAlignment(AnnotationTextAlignHorizontalEnum::CENTER); + break; + case ChartAxisLocationEnum::CHART_AXIS_LOCATION_LEFT: + xyz[1] = 0.5f; + text->setHorizontalAlignment(AnnotationTextAlignHorizontalEnum::CENTER); + text->setVerticalAlignment(AnnotationTextAlignVerticalEnum::TOP); + text->setRotationAngle(-90.0f); + break; + case ChartAxisLocationEnum::CHART_AXIS_LOCATION_RIGHT: + xyz[1] = 0.5f; + text->setHorizontalAlignment(AnnotationTextAlignHorizontalEnum::CENTER); + text->setVerticalAlignment(AnnotationTextAlignVerticalEnum::BOTTOM); + text->setRotationAngle(-90.0f); + break; + } + text->setText(labelText); + text->setFontPercentViewportSize(m_axis->getLabelTextSize()); + text->getCoordinate()->setXYZ(xyz); + + double textWidth = 0.0; + double textHeight = 0.0; + m_textRenderer->getTextWidthHeightInPixels(*text, + m_tabViewportWidth, m_tabViewportHeight, + textWidth, textHeight); + widthOut = textWidth; + heightOut = textHeight; + } + + m_labelText.reset(text); +} + +/** + * Initialize the numeric text by creating an annotation for each numeric value + * and find the maximum width and height of the numeric values. + * + * @param dataMinimumDataValue + * Minimum data value for axis + * @param dataMaximumDataValue + * Maximum data value for axis + * @param showNumericFlag, + * Show numerics + * @param rotateNumericFlag + * Rotate numerics. + * @param maxWidthOut + * Output with maximum width of numeric text. + * @param maxHeightOut + * Output with maximum height of numeric text. + */ +void +BrainOpenGLChartTwoDrawingFixedPipeline::AxisDrawingInfo::initializeNumericText(const float dataMinimumDataValue, + const float dataMaximumDataValue, + const bool showNumericFlag, + const bool rotateNumericFlag, + float& maxWidthOut, + float& maxHeightOut) +{ + maxWidthOut = 0.0f; + maxHeightOut = 0.0f; + + if (m_axis == NULL) { + return; + } + + /* + * Using an axis length of 1.0 will result in the + * offsets ranging (0.0 to 1.0) + */ + const float axisLength = 1.0; + std::vector scaleValuePositions; + std::vector scaleValuesText; + m_axis->getScaleValuesAndOffsets(dataMinimumDataValue, + dataMaximumDataValue, + axisLength, + m_axisMinimumValue, + m_axisMaximumValue, + scaleValuePositions, + scaleValuesText); + + /* + * For each numeric value, create a text annotation and determine the + * width and height of the text. Also, set either the X or Y-coordinate + * with the normalized coordinate (zero to one) that will be used + * later to position the numeric value. + */ + CaretAssert(scaleValuePositions.size() == scaleValuesText.size()); + const int32_t numValues = static_cast(scaleValuesText.size()); + const int32_t firstIndex = 0; + const int32_t lastIndex = (numValues - 1); + for (int32_t i = 0; i < numValues; i++) { + AnnotationPercentSizeText* text = new AnnotationPercentSizeText(AnnotationAttributesDefaultTypeEnum::NORMAL, + AnnotationTextFontSizeTypeEnum::PERCENTAGE_OF_VIEWPORT_HEIGHT); + text->setFontPercentViewportSize(m_axis->getNumericsTextSize()); + if (rotateNumericFlag) { + text->setRotationAngle(-90.0f); + } + + float xyz[3] = { 0.0f, 0.0f, 0.0f }; + + AnnotationTextAlignVerticalEnum::Enum verticalAlignment = AnnotationTextAlignVerticalEnum::MIDDLE; + AnnotationTextAlignHorizontalEnum::Enum horizontalAlignment = AnnotationTextAlignHorizontalEnum::CENTER; + switch (m_axisLocation) { + case ChartAxisLocationEnum::CHART_AXIS_LOCATION_BOTTOM: + xyz[0] = scaleValuePositions[i]; + if (rotateNumericFlag) { + horizontalAlignment = AnnotationTextAlignHorizontalEnum::RIGHT; + if (i == firstIndex) { + verticalAlignment = AnnotationTextAlignVerticalEnum::TOP; + } + else if (i == lastIndex) { + verticalAlignment = AnnotationTextAlignVerticalEnum::BOTTOM; + } + else { + verticalAlignment = AnnotationTextAlignVerticalEnum::MIDDLE; + } + } + else { + verticalAlignment = AnnotationTextAlignVerticalEnum::TOP; + if (i == firstIndex) { + horizontalAlignment = AnnotationTextAlignHorizontalEnum::LEFT; + } + else if (i == lastIndex) { + horizontalAlignment = AnnotationTextAlignHorizontalEnum::RIGHT; + } + else { + horizontalAlignment = AnnotationTextAlignHorizontalEnum::CENTER; + } + } + break; + case ChartAxisLocationEnum::CHART_AXIS_LOCATION_TOP: + xyz[0] = scaleValuePositions[i]; + if (rotateNumericFlag) { + horizontalAlignment = AnnotationTextAlignHorizontalEnum::LEFT; + if (i == firstIndex) { + verticalAlignment = AnnotationTextAlignVerticalEnum::TOP; + } + else if (i == lastIndex) { + verticalAlignment = AnnotationTextAlignVerticalEnum::BOTTOM; + } + else { + verticalAlignment = AnnotationTextAlignVerticalEnum::MIDDLE; + } + } + else { + verticalAlignment = AnnotationTextAlignVerticalEnum::BOTTOM; + if (i == firstIndex) { + horizontalAlignment = AnnotationTextAlignHorizontalEnum::LEFT; + } + else if (i == lastIndex) { + horizontalAlignment = AnnotationTextAlignHorizontalEnum::RIGHT; + } + else { + horizontalAlignment = AnnotationTextAlignHorizontalEnum::CENTER; + } + } + break; + case ChartAxisLocationEnum::CHART_AXIS_LOCATION_LEFT: + xyz[1] = scaleValuePositions[i]; + if (rotateNumericFlag) { + verticalAlignment = AnnotationTextAlignVerticalEnum::BOTTOM; + if (i == firstIndex) { + horizontalAlignment = AnnotationTextAlignHorizontalEnum::LEFT; + } + else if (i == lastIndex) { + horizontalAlignment = AnnotationTextAlignHorizontalEnum::RIGHT; + } + else { + horizontalAlignment = AnnotationTextAlignHorizontalEnum::CENTER; + } + } + else { + horizontalAlignment = AnnotationTextAlignHorizontalEnum::RIGHT; + if (i == firstIndex) { + verticalAlignment = AnnotationTextAlignVerticalEnum::BOTTOM; + } + else if (i == lastIndex) { + verticalAlignment = AnnotationTextAlignVerticalEnum::TOP; + } + else { + verticalAlignment = AnnotationTextAlignVerticalEnum::MIDDLE; + } + } + break; + case ChartAxisLocationEnum::CHART_AXIS_LOCATION_RIGHT: + xyz[1] = scaleValuePositions[i]; + if (rotateNumericFlag) { + verticalAlignment = AnnotationTextAlignVerticalEnum::TOP; + if (i == firstIndex) { + horizontalAlignment = AnnotationTextAlignHorizontalEnum::LEFT; + } + else if (i == lastIndex) { + horizontalAlignment = AnnotationTextAlignHorizontalEnum::RIGHT; + } + else { + horizontalAlignment = AnnotationTextAlignHorizontalEnum::CENTER; + } + } + else { + horizontalAlignment = AnnotationTextAlignHorizontalEnum::LEFT; + if (i == firstIndex) { + verticalAlignment = AnnotationTextAlignVerticalEnum::BOTTOM; + } + else if (i == lastIndex) { + verticalAlignment = AnnotationTextAlignVerticalEnum::TOP; + } + else { + verticalAlignment = AnnotationTextAlignVerticalEnum::MIDDLE; + } + } + break; + } + text->setHorizontalAlignment(horizontalAlignment); + text->setVerticalAlignment(verticalAlignment); + + + if (showNumericFlag) { + text->setText(scaleValuesText[i]); + } + + text->getCoordinate()->setXYZ(xyz); + + double textWidth = 0.0; + double textHeight = 0.0; + m_textRenderer->getTextWidthHeightInPixels(*text, + m_tabViewportWidth, m_tabViewportHeight, + textWidth, textHeight); + if (rotateNumericFlag) { + std::swap(textWidth, + textHeight); + } + + maxWidthOut = std::max(maxWidthOut, static_cast(textWidth)); + maxHeightOut = std::max(maxHeightOut, static_cast(textHeight)); + + //text->setText(scaleValuesText[i]); + m_numericsText.push_back(std::unique_ptr(text)); + } +} + +/** + * Set the axis viewport. + * + * In the constructor, we were able to determine the width of the left and right + * axes and the height of the bottom and top axes. Now we can determine + * the size of the other dimension and computer a viewport for the axis. + * + * @param titleHeight + * Height of the title. + * @param bottomAxisHeight + * Height of the bottom axis. + * @param topAxisHeight + * Height of the top axis. + * @param leftAxisWidth + * Width of the left axis. + * @param rightAxisWidth + * Width of the right axis. + */ +void +BrainOpenGLChartTwoDrawingFixedPipeline::AxisDrawingInfo::setAxisViewport(const float titleHeight, + const float bottomAxisHeight, + const float topAxisHeight, + const float leftAxisWidth, + const float rightAxisWidth) +{ + switch (m_axisLocation) { + case ChartAxisLocationEnum::CHART_AXIS_LOCATION_BOTTOM: + case ChartAxisLocationEnum::CHART_AXIS_LOCATION_TOP: + m_axisWidth = m_tabViewportWidth - (leftAxisWidth + rightAxisWidth); + break; + case ChartAxisLocationEnum::CHART_AXIS_LOCATION_LEFT: + case ChartAxisLocationEnum::CHART_AXIS_LOCATION_RIGHT: + m_axisHeight = m_tabViewportHeight - (bottomAxisHeight + topAxisHeight + titleHeight); + break; + } + if (m_axisWidth < 1.0f) { + m_axisWidth = 1.0f; + } + if (m_axisHeight < 1.0f) { + m_axisHeight = 1.0f; + } + + switch (m_axisLocation) { + case ChartAxisLocationEnum::CHART_AXIS_LOCATION_BOTTOM: + m_axisViewport[0] = leftAxisWidth; + m_axisViewport[1] = 0.0; + break; + case ChartAxisLocationEnum::CHART_AXIS_LOCATION_TOP: + m_axisViewport[0] = leftAxisWidth; + m_axisViewport[1] = m_tabViewportHeight - topAxisHeight - titleHeight; + break; + case ChartAxisLocationEnum::CHART_AXIS_LOCATION_LEFT: + m_axisViewport[0] = 0.0f; + m_axisViewport[1] = bottomAxisHeight; + break; + case ChartAxisLocationEnum::CHART_AXIS_LOCATION_RIGHT: + m_axisViewport[0] = m_tabViewportWidth - rightAxisWidth; + m_axisViewport[1] = bottomAxisHeight; + break; + } + + m_axisViewport[2] = m_axisWidth; + m_axisViewport[3] = m_axisHeight; + + CaretAssert(m_axisViewport[0] >= 0.0f); + CaretAssert(m_axisViewport[1] >= 0.0f); + CaretAssert(m_axisViewport[2] >= 0.0f); + CaretAssert(m_axisViewport[3] >= 0.0f); +} + +/** + * When this method is called, we have the viewport for the axis and we can now use the + * viewport to position the numerical value and the label. + * + * @param foregroundFloatRGBA + * Color of the foreground. + * @param graphicsBoxLineThickness + * Line thickness of the graphics box. + */ +void +BrainOpenGLChartTwoDrawingFixedPipeline::AxisDrawingInfo::setLabelAndNumericsCoordinates(const float foregroundFloatRGBA[4], + const float graphicsBoxLineThickness) +{ + if (m_axis == NULL) { + return; + } + + if ( ! m_axisValid) { + return; + } + + if ( ! m_axisDisplayedFlag) { + return; + } + + /* + * Adjust the 'viewport' to account for the thickness of the lines + * for the box that contains the plot of data + */ + float vpX = m_axisViewport[0]; + float vpY = m_axisViewport[1]; + float vpWidth = m_axisViewport[2]; + float vpHeight = m_axisViewport[3]; + switch (m_axisLocation) { + case ChartAxisLocationEnum::CHART_AXIS_LOCATION_BOTTOM: + case ChartAxisLocationEnum::CHART_AXIS_LOCATION_TOP: + vpX += graphicsBoxLineThickness; + vpWidth -= (graphicsBoxLineThickness * 2.0); + break; + case ChartAxisLocationEnum::CHART_AXIS_LOCATION_LEFT: + case ChartAxisLocationEnum::CHART_AXIS_LOCATION_RIGHT: + vpY += graphicsBoxLineThickness; + vpHeight -= (graphicsBoxLineThickness * 2.0); + break; + } + + if (debugFlag) { + glColor3f(1.0, 0.0, 0.0); + const float v1[3] = { vpX, vpY, 0.0f }; + const float v2[3] = { vpX + vpWidth, vpY, 0.0f }; + const float v3[3] = { vpX + vpWidth, vpY + vpHeight, 0.0f }; + const float v4[3] = { vpX, vpY + vpHeight, 0.0f }; + const uint8_t rgba[4] { 255, 0, 0, 255}; + GraphicsShape::drawBoxOutlineByteColor(v1, v2, v3, v4, + rgba, + GraphicsPrimitive::LineWidthType::PIXELS, 1.0f); + } + + /* + * 'Center' the label in the viewport + */ + if (m_axis->isShowLabel()) { + float xyz[3] = { 0.0, 0.0, 0.0 }; + switch (m_axisLocation) { + case ChartAxisLocationEnum::CHART_AXIS_LOCATION_BOTTOM: + xyz[0] = vpX + (vpWidth / 2.0f); + xyz[1] = vpY + m_labelPaddingSizePixels; + break; + case ChartAxisLocationEnum::CHART_AXIS_LOCATION_LEFT: + xyz[0] = vpX + m_labelPaddingSizePixels; + xyz[1] = vpY + (vpHeight / 2.0f); + break; + case ChartAxisLocationEnum::CHART_AXIS_LOCATION_RIGHT: + xyz[0] = vpX + vpWidth - m_labelPaddingSizePixels; + xyz[1] = vpY + (vpHeight / 2.0f); + break; + case ChartAxisLocationEnum::CHART_AXIS_LOCATION_TOP: + xyz[0] = vpX + (vpWidth / 2.0f); + xyz[1] = vpY + vpHeight - m_labelPaddingSizePixels; + break; + } + m_labelText->setTextColor(CaretColorEnum::CUSTOM); + m_labelText->setCustomTextColor(foregroundFloatRGBA); + m_labelText->getCoordinate()->setXYZ(xyz); + } + + /* + * Now convert the 'normalized' (zero to one) numeric value coordinate + * to real coordinate. + */ + for (auto& text : m_numericsText) { + float xyz[3] = { 0.0, 0.0, 0.0 }; + text->getCoordinate()->getXYZ(xyz); + + switch (m_axisLocation) { + case ChartAxisLocationEnum::CHART_AXIS_LOCATION_BOTTOM: + xyz[0] = vpX + (vpWidth * xyz[0]); + xyz[1] = vpY + vpHeight - m_tickLength - m_numericsTicksPaddingSizePixels; + break; + case ChartAxisLocationEnum::CHART_AXIS_LOCATION_LEFT: + xyz[0] = vpX + vpWidth - m_tickLength - m_numericsTicksPaddingSizePixels; + xyz[1] = vpY + (vpHeight * xyz[1]); + break; + case ChartAxisLocationEnum::CHART_AXIS_LOCATION_RIGHT: + xyz[0] = vpX + m_tickLength + m_numericsTicksPaddingSizePixels; + xyz[1] = vpY + (vpHeight * xyz[1]); + break; + case ChartAxisLocationEnum::CHART_AXIS_LOCATION_TOP: + xyz[0] = vpX + (vpWidth * xyz[0]); + xyz[1] = vpY + m_tickLength + m_numericsTicksPaddingSizePixels; + break; + } + + text->setTextColor(CaretColorEnum::CUSTOM); + text->setCustomTextColor(foregroundFloatRGBA); + text->getCoordinate()->setXYZ(xyz); + } +} + +/** + * Draw the axis. + * + * @param chartDrawing + * The chart drawing parent. + * @param chartTwoOverlaySet + * Chart overlay set containing the axis. + * @param mouseX + * X-coordinate of the mouse. + * @param mouseY + * Y-coordinate of the mouse. + * @param foregroundFloatRGBA + * Color of the foreground. + * @param axisMinimumValueOut + * Output containing the minimum value for the axis + * @param axisMaximumValueOut + * Output containing the maximum value for the axis + */ +void +BrainOpenGLChartTwoDrawingFixedPipeline::AxisDrawingInfo::drawAxis(BrainOpenGLChartTwoDrawingFixedPipeline* chartDrawing, + ChartTwoOverlaySet* chartTwoOverlaySet, + const int32_t mouseX, + const int32_t mouseY, + const float foregroundFloatRGBA[4], + float& axisMinimumValueOut, + float& axisMaximumValueOut) +{ + axisMinimumValueOut = m_axisMinimumValue; + axisMaximumValueOut = m_axisMaximumValue; + + if (m_axis == NULL) { + return; + } + if ( ! m_axisValid) { + return; + } + + if ( ! m_axisDisplayedFlag) { + return; + } + + const int32_t numScaleValuesToDraw = static_cast(m_numericsText.size()); + + if (numScaleValuesToDraw > 0) { + const bool showTicksEnabledFlag = m_axis->isShowTickmarks(); + + GraphicsPrimitive::PrimitiveType primitiveType = GraphicsPrimitive::PrimitiveType::POLYGONAL_LINES; + std::unique_ptr ticksData + = std::unique_ptr(GraphicsPrimitive::newPrimitiveV3f(primitiveType, + foregroundFloatRGBA)); + ticksData->reserveForNumberOfVertices(numScaleValuesToDraw * 2); + + const int32_t firstTickIndex = 0; + const int32_t lastTickIndex = numScaleValuesToDraw - 1; + + for (int32_t i = 0; i < numScaleValuesToDraw; i++) { + /* + * Coordinate of numeric value text + */ + CaretAssertVectorIndex(m_numericsText, i); + AnnotationPercentSizeText* text = m_numericsText[i].get(); + float xyz[3]; + text->getCoordinate()->getXYZ(xyz); + const float textX = xyz[0]; + const float textY = xyz[1]; + + /* + * Tick starts at an offset from its corresponding numerical value. + * The length of the tick is increased by half the line width so + * that the tick connects to the box around the data plot without + * a gap. + */ + float tickStartX = textX; + float tickStartY = textY; + float tickEndX = 0.0; + float tickEndY = 0.0; + const float tickLength = m_tickLength + (m_lineDrawingWidth / 2.0f); + switch (m_axisLocation) { + case ChartAxisLocationEnum::CHART_AXIS_LOCATION_BOTTOM: + tickStartY += m_numericsTicksPaddingSizePixels; + tickEndX = tickStartX; + tickEndY = tickStartY + tickLength; + break; + case ChartAxisLocationEnum::CHART_AXIS_LOCATION_TOP: + tickStartY -= m_numericsTicksPaddingSizePixels; + tickEndX = tickStartX; + tickEndY = tickStartY - tickLength; + break; + case ChartAxisLocationEnum::CHART_AXIS_LOCATION_LEFT: + tickStartX += m_numericsTicksPaddingSizePixels; + tickEndX = tickStartX + tickLength; + tickEndY = tickStartY; + break; + case ChartAxisLocationEnum::CHART_AXIS_LOCATION_RIGHT: + tickStartX -= m_numericsTicksPaddingSizePixels; + tickEndX = tickStartX - tickLength; + tickEndY = tickStartY; + break; + } + + if (showTicksEnabledFlag) { + if ((i != firstTickIndex) + && (i != lastTickIndex)) { + ticksData->addVertex(tickStartX, tickStartY); + ticksData->addVertex(tickEndX, tickEndY); + } + } + + /* + * Draw the numeric value. + */ + m_textRenderer->drawTextAtViewportCoords(textX, + textY, + 0.0, + *text); + } + + /* + * Draw the ticks. + * Note Line width is set in pixel and was converted from PERCENTAGE OF VIEWPORT HEIGHT + */ + if (ticksData->getNumberOfVertices() > 1) { + ticksData->setLineWidth(GraphicsPrimitive::LineWidthType::PIXELS, m_lineDrawingWidth); + chartDrawing->drawPrimitivePrivate(ticksData.get()); + } + } + + /* + * Draw the label + */ + if (m_axis->isShowLabel()) { + float xyz[3]; + m_labelText->getCoordinate()->getXYZ(xyz); + if (chartDrawing->m_identificationModeFlag) { + /* + * For identification simply draw a box using a triangle strip + */ + int32_t primitiveIndex = -1; + float primitiveDepth = 0.0; + + float bottomLeft[3], bottomRight[3], topLeft[3], topRight[3]; + m_textRenderer->getBoundsForTextAtViewportCoords(*(m_labelText.get()), xyz[0], xyz[1], xyz[2], + m_tabViewportWidth, m_tabViewportHeight, + bottomLeft, bottomRight, topRight, topLeft); + + std::unique_ptr boxPrimitive = std::unique_ptr(GraphicsPrimitive::newPrimitiveV3f(GraphicsPrimitive::PrimitiveType::OPENGL_TRIANGLE_STRIP, + foregroundFloatRGBA)); + boxPrimitive->reserveForNumberOfVertices(4); + boxPrimitive->addVertex(topLeft); + boxPrimitive->addVertex(bottomLeft); + boxPrimitive->addVertex(topRight); + boxPrimitive->addVertex(bottomRight); + + GraphicsEngineDataOpenGL::drawWithSelection(boxPrimitive.get(), + mouseX, + mouseY, + primitiveIndex, + primitiveDepth); + + if (primitiveIndex >= 0) { + if (chartDrawing->m_selectionItemChartLabel->isOtherScreenDepthCloserToViewer(primitiveDepth)) { + chartDrawing->m_selectionItemChartLabel->setChartTwoCartesianAxis(const_cast(m_axis), + chartTwoOverlaySet); + } + } + } + else { + m_textRenderer->drawTextAtViewportCoords(xyz[0], + xyz[1], + 0.0, + *(m_labelText.get())); + } + } +} + +/** + * Constructor. + * + * @param textRenderer + * The text renderer. + * @param tabViewport + * Viewport of the tab containing the chart. + * @param title + * The chart title. + */ +BrainOpenGLChartTwoDrawingFixedPipeline::TitleDrawingInfo::TitleDrawingInfo(BrainOpenGLTextRenderInterface* textRenderer, + const int32_t tabViewport[4], + const ChartTwoTitle* title) +: m_textRenderer(textRenderer), +m_tabViewportWidth(tabViewport[2]), +m_tabViewportHeight(tabViewport[3]), +m_title(title) +{ + const AString titleText = m_title->getText().trimmed(); + m_titleDisplayedFlag = (m_title->isDisplayed() + && ( ! titleText.isEmpty())); + + if ( ! m_titleDisplayedFlag) { + /* + * If axis is not displayed, use padding with a small percentage of space. + */ + const float emptyAxisPaddingPercentage = 1.0f; + m_paddingSizePixels = convertPercentageOfViewportToPixels(emptyAxisPaddingPercentage, + m_tabViewportHeight); + m_titleHeight = m_paddingSizePixels; + return; + } + + m_paddingSizePixels = convertPercentageOfViewportToPixels(m_title->getPaddingSize(), + m_tabViewportHeight); + + /* + * Create a text annotation for drawing the title + */ + AnnotationPercentSizeText* text = new AnnotationPercentSizeText(AnnotationAttributesDefaultTypeEnum::NORMAL, + AnnotationTextFontSizeTypeEnum::PERCENTAGE_OF_VIEWPORT_HEIGHT); + float xyz[3] = { 0.0f, 0.0f, 0.0f }; + xyz[0] = 0.5f; + text->setVerticalAlignment(AnnotationTextAlignVerticalEnum::TOP); + text->setHorizontalAlignment(AnnotationTextAlignHorizontalEnum::CENTER); + text->setText(titleText); + text->setFontPercentViewportSize(m_title->getTextSize()); + text->getCoordinate()->setXYZ(xyz); + + double m_textWidth = 0.0; + double m_textHeight = 0.0; + m_textRenderer->getTextWidthHeightInPixels(*text, + m_tabViewportWidth, m_tabViewportHeight, + m_textWidth, m_textHeight); + m_text.reset(text); + + m_titleHeight = m_paddingSizePixels + m_textHeight; + CaretAssert(m_titleHeight >= 0.0f); +} + +/** + * Set the title viewport. Once the width of the left and right and the height of the + * bottom and top axes are known, we can determine the sizes for the title. + * + * @param leftAxisWidth + * Width of the left axis. + * @param rightAxisWidth + * Width of the right axis. + */ +void +BrainOpenGLChartTwoDrawingFixedPipeline::TitleDrawingInfo::setTitleViewport(const float leftAxisWidth, + const float rightAxisWidth) +{ + if ( ! m_titleDisplayedFlag) { + return; + } + + m_titleWidth = m_tabViewportWidth - (leftAxisWidth + rightAxisWidth); + if (m_titleWidth < 1.0f) { + m_titleWidth = 1.0f; + } + if (m_titleHeight < 1.0f) { + m_titleHeight = 1.0f; + } + + m_titleWidth = m_tabViewportWidth - (leftAxisWidth + rightAxisWidth); + m_titleViewport[0] = leftAxisWidth; + m_titleViewport[1] = m_tabViewportHeight - m_titleHeight; + + m_titleViewport[2] = m_titleWidth; + m_titleViewport[3] = m_titleHeight; + + CaretAssert(m_titleViewport[0] >= 0.0f); + CaretAssert(m_titleViewport[1] >= 0.0f); + CaretAssert(m_titleViewport[2] >= 0.0f); + CaretAssert(m_titleViewport[3] >= 0.0f); + + float xyz[3]; + m_text->getCoordinate()->getXYZ(xyz); + xyz[0] = m_titleViewport[0] + (m_titleWidth / 2.0f); + xyz[1] = m_titleViewport[1] + m_titleHeight - m_paddingSizePixels; + m_text->getCoordinate()->setXYZ(xyz); +} + +/** + * Draw the title. + * + * @param foregroundFloatRGBA + * Color of the foreground. + */ +void +BrainOpenGLChartTwoDrawingFixedPipeline::TitleDrawingInfo::drawTitle(const float foregroundFloatRGBA[4]) +{ + if (m_title == NULL) { + return; + } + if ( ! m_titleDisplayedFlag) { + return; + } + + if (debugFlag) { + const float v1[3] = { m_titleViewport[0], m_titleViewport[1], 0.0f }; + const float v2[3] = { m_titleViewport[0] + m_titleViewport[2], m_titleViewport[1], 0.0f }; + const float v3[3] = { m_titleViewport[0] + m_titleViewport[2], m_titleViewport[1] + m_titleViewport[3], 0.0f }; + const float v4[3] = { m_titleViewport[0], m_titleViewport[1] + m_titleViewport[3], 0.0f }; + const uint8_t rgba[4] { 255, 0, 0, 255}; + GraphicsShape::drawBoxOutlineByteColor(v1, v2, v3, v4, + rgba, + GraphicsPrimitive::LineWidthType::PIXELS, 2.0f); + } + + m_text->setTextColor(CaretColorEnum::CUSTOM); + m_text->setCustomTextColor(foregroundFloatRGBA); + + float xyz[3]; + m_text->getCoordinate()->getXYZ(xyz); + m_textRenderer->drawTextAtViewportCoords(xyz[0], + xyz[1], + 0.0, + *(m_text.get())); +} diff --git a/src/Brain/BrainOpenGLChartTwoDrawingFixedPipeline.h b/src/Brain/BrainOpenGLChartTwoDrawingFixedPipeline.h new file mode 100644 index 0000000000000000000000000000000000000000..807efd64bb34ad07a2d4a0633ca9b86977f0f117 --- /dev/null +++ b/src/Brain/BrainOpenGLChartTwoDrawingFixedPipeline.h @@ -0,0 +1,334 @@ +#ifndef __BRAIN_OPEN_G_L_CHART_TWO_DRAWING_FIXED_PIPELINE_H__ +#define __BRAIN_OPEN_G_L_CHART_TWO_DRAWING_FIXED_PIPELINE_H__ + +/*LICENSE_START*/ +/* + * Copyright (C) 2017 Washington University School of Medicine + * + * 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 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ +/*LICENSE_END*/ + +#include +#include +#include "BrainOpenGLChartTwoDrawingInterface.h" +#include "CaretColorEnum.h" +#include "ChartAxisLocationEnum.h" +#include "ChartTwoDataTypeEnum.h" +#include "ChartTwoMatrixTriangularViewingModeEnum.h" +#include "GraphicsPrimitive.h" + +namespace caret { + + class Annotation; + class AnnotationPercentSizeText; + class BrainOpenGLViewportContent; + class BrowserTabContent; + class CaretPreferences; + class ChartTwoCartesianAxis; + class ChartTwoDataCartesian; + class ChartTwoOverlaySet; + class ChartTwoTitle; + class ChartableTwoFileHistogramChart; + class ChartableTwoFileLineSeriesChart; + class ChartableTwoFileMatrixChart; + class SelectionItemAnnotation; + class SelectionItemChartTwoHistogram; + class SelectionItemChartTwoLabel; + class SelectionItemChartTwoLineSeries; + class SelectionItemChartTwoMatrix; + + class BrainOpenGLChartTwoDrawingFixedPipeline : public BrainOpenGLChartTwoDrawingInterface { + + public: + BrainOpenGLChartTwoDrawingFixedPipeline(const BrainOpenGLViewportContent* viewportContent); + + virtual ~BrainOpenGLChartTwoDrawingFixedPipeline(); + + virtual void drawChartOverlaySet(Brain* brain, + ModelChartTwo* chartTwoModel, + BrainOpenGLFixedPipeline* fixedPipelineDrawing, + const SelectionItemDataTypeEnum::Enum selectionItemDataType, + const int32_t viewport[4], + std::vector& annotationsOut) override; + + // ADD_NEW_METHODS_HERE + + private: + /** + * Contains histogram for drawing + */ + class HistogramChartDrawingInfo { + public: + HistogramChartDrawingInfo(ChartableTwoFileHistogramChart* histogramChart, + int32_t mapIndex, + ChartAxisLocationEnum::Enum verticalAxisLocation, + const bool allMapsSelected); + + ChartableTwoFileHistogramChart* m_histogramChart; + int32_t m_mapIndex; + ChartAxisLocationEnum::Enum m_verticalAxisLocation; + const bool m_allMapsSelected; + + ~HistogramChartDrawingInfo(); + }; + + /** + * Contains line chart for drawing + */ + class LineSeriesChartDrawingInfo { + public: + LineSeriesChartDrawingInfo(const ChartableTwoFileLineSeriesChart* lineSeriesChart, + const ChartTwoDataCartesian* chartTwoCartesianData, + const ChartAxisLocationEnum::Enum verticalAxisLocation) + : m_lineSeriesChart(lineSeriesChart), + m_chartTwoCartesianData(chartTwoCartesianData), + m_verticalAxisLocation(verticalAxisLocation) { } + + const ChartableTwoFileLineSeriesChart* m_lineSeriesChart; + const ChartTwoDataCartesian* m_chartTwoCartesianData; + const ChartAxisLocationEnum::Enum m_verticalAxisLocation; + }; + + /** + * Determines size of title and draws the title + */ + class TitleDrawingInfo { + public: + TitleDrawingInfo(BrainOpenGLTextRenderInterface* textRenderer, + const int32_t tabViewport[4], + const ChartTwoTitle* title); + + void setTitleViewport(const float leftAxisWidth, + const float rightAxisWidth); + + void drawTitle(const float foregroundFloatRGBA[4]); + + float m_titleHeight = 0.0f; + float m_titleWidth = 0.0f; + bool m_titleDisplayedFlag; + + private: + BrainOpenGLTextRenderInterface* m_textRenderer; + const float m_tabViewportWidth = 0.0f; + const float m_tabViewportHeight = 0.0f; + const ChartTwoTitle* m_title; + + std::unique_ptr m_text; + float m_paddingSizePixels = 0.0f; + float m_titleViewport[4]; + }; + + /** + * Determines size of an axis and draws the axis + */ + class AxisDrawingInfo { + public: + AxisDrawingInfo(BrainOpenGLTextRenderInterface* textRenderer, + const int32_t tabViewport[4], + const float dataMinX, + const float dataMaxX, + const float dataMinY, + const float dataMaxY, + const ChartAxisLocationEnum::Enum axisLocation, + const ChartTwoCartesianAxis* axis, + const AString& labelText, + const float lineWidthPercentage); + + void setAxisViewport(const float titleHeight, + const float bottomAxisHeight, + const float topAxisHeight, + const float leftAxisWidth, + const float rightAxisWidth); + + void setLabelAndNumericsCoordinates(const float foregroundFloatRGBA[4], + const float graphicsBoxLineThickness); + + void drawAxis(BrainOpenGLChartTwoDrawingFixedPipeline* chartDrawing, + ChartTwoOverlaySet* chartTwoOverlaySet, + const int32_t mouseX, + const int32_t mouseY, + const float foregroundFloatRGBA[4], + float& axisMinimumValueOut, + float& axisMaximumValueOut); + + std::unique_ptr m_labelText; + + std::vector> m_numericsText; + + float m_axisWidth = 0.0f; + float m_axisHeight = 0.0f; + + /** Axis minimum value is produced by the axis scaling user/auto */ + float m_axisMinimumValue = 0.0f; + float m_axisMaximumValue = 0.0f; + + float m_labelPaddingSizePixels = 0.0f; + float m_numericsTicksPaddingSizePixels = 1.0f; + bool m_axisValid = false; + bool m_axisDisplayedFlag = false; + + int32_t m_axisViewport[4]; + + private: + const ChartAxisLocationEnum::Enum m_axisLocation; + const ChartTwoCartesianAxis* m_axis; + BrainOpenGLTextRenderInterface* m_textRenderer; + const float m_tabViewportWidth = 0.0f; + const float m_tabViewportHeight = 0.0f; + float m_lineDrawingWidth = 1.0f; + float m_tickLength = 0.0f; + float m_labelWidth = 0.0f; + float m_labelHeight = 0.0f; + + void initializeNumericText(const float dataMinimumDataValue, + const float dataMaximumDataValue, + const bool showNumericFlag, + const bool rotateNumericFlag, + float& maxWidthOut, + float& maxHeightOut); + + void initializeLabel(const AString& labelText, + float& widthOut, + float& heightOut); + + }; + + class MatrixRowColumnHighight { + public: + MatrixRowColumnHighight(GraphicsPrimitive* graphicsPrimitive, + const float modelViewMatrix[16]) + : m_graphicsPrimitive(std::unique_ptr(graphicsPrimitive)) { + for (int32_t i = 0; i < 16; i++) m_modelViewMatrix[i] = modelViewMatrix[i]; + } + + std::unique_ptr m_graphicsPrimitive; + float m_modelViewMatrix[16]; + }; + + BrainOpenGLChartTwoDrawingFixedPipeline(const BrainOpenGLChartTwoDrawingFixedPipeline&); + + BrainOpenGLChartTwoDrawingFixedPipeline& operator=(const BrainOpenGLChartTwoDrawingFixedPipeline&); + + void restoreStateOfOpenGL(); + + void saveStateOfOpenGL(); + + void drawMatrixChart(); + + void drawMatrixChartContent(const ChartableTwoFileMatrixChart* matrixChart, + const ChartTwoMatrixTriangularViewingModeEnum::Enum chartViewingType, + const float cellWidth, + const float cellHeight, + const float zooming, + std::vector& rowColumnHighlightingOut); + + void drawHistogramOrLineSeriesChart(const ChartTwoDataTypeEnum::Enum chartDataType); + + void drawChartGraphicsBoxAndSetViewport(const float vpX, + const float vpY, + const float vpWidth, + const float vpHeight, + const float lineThicknessPercentage, + const float titleHeight, + const float bottomAxisHeight, + const float topAxisHeight, + const float leftAxisWidth, + const float rightAxisWidth, + const bool drawBoxFlag, + int32_t chartGraphicsDrawingViewportOut[4]); + + bool createChartDrawingViewport(const float vpX, + const float vpY, + const float vpWidth, + const float vpHeight, + const float lineThicknessPercentage, + const float titleHeight, + const float bottomAxisHeight, + const float topAxisHeight, + const float leftAxisWidth, + const float rightAxisWidth, + int32_t chartGraphicsDrawingViewportOut[4]); + + void drawPrimitivePrivate(GraphicsPrimitive* primitive); + + void updateViewportContentForCharting(const int32_t viewport[4]); + + static float convertPercentageOfViewportToPixels(const float percentageWidthOrHeight, + const float viewportWidthOrHeight); + + static float convertPercentageOfViewportToOpenGLLineWidth(const float percentageWidthOrHeight, + const float viewportWidthOrHeight); + + Brain* m_brain; + + const BrainOpenGLViewportContent* m_viewportContent; + + BrowserTabContent* m_browserTabContent; + + ModelChartTwo* m_chartTwoModel; + + BrainOpenGLFixedPipeline* m_fixedPipelineDrawing; + + BrainOpenGLTextRenderInterface* m_textRenderer; + + ChartTwoOverlaySet* m_chartOverlaySet; + + SelectionItemDataTypeEnum::Enum m_selectionItemDataType; + + int32_t m_viewport[4]; + + int32_t m_tabIndex; + + float m_translation[3]; + + float m_zooming; + + CaretPreferences* m_preferences; + + SelectionItemAnnotation* m_selectionItemAnnotation; + + SelectionItemChartTwoHistogram* m_selectionItemHistogram; + + SelectionItemChartTwoLineSeries* m_selectionItemLineSeries; + + SelectionItemChartTwoMatrix* m_selectionItemMatrix; + + SelectionItemChartTwoLabel* m_selectionItemChartLabel; + + bool m_identificationModeFlag; + + /** These annotations will be drawn by the annotation drawing code */ + std::vector m_annotationsForDrawingOutput; + + static float s_minimumLineWidthOpenGL; + static float s_maximumLineWidthOpenGL; + + static const int32_t IDENTIFICATION_INDICES_PER_HISTOGRAM = 2; + static const int32_t IDENTIFICATION_INDICES_PER_CHART_LINE = 2; + static const int32_t IDENTIFICATION_INDICES_PER_MATRIX_ELEMENT = 2; + + // ADD_NEW_MEMBERS_HERE + + friend class AxisDrawingInfo; + }; + +#ifdef __BRAIN_OPEN_G_L_CHART_TWO_DRAWING_FIXED_PIPELINE_DECLARE__ + float BrainOpenGLChartTwoDrawingFixedPipeline::s_minimumLineWidthOpenGL = 1.0f; + float BrainOpenGLChartTwoDrawingFixedPipeline::s_maximumLineWidthOpenGL = 1.0f; +#endif // __BRAIN_OPEN_G_L_CHART_TWO_DRAWING_FIXED_PIPELINE_DECLARE__ + +} // namespace +#endif //__BRAIN_OPEN_G_L_CHART_TWO_DRAWING_FIXED_PIPELINE_H__ diff --git a/src/Brain/BrainOpenGLChartTwoDrawingInterface.h b/src/Brain/BrainOpenGLChartTwoDrawingInterface.h new file mode 100644 index 0000000000000000000000000000000000000000..5ae58d32961951e83bd8bfb8acef3190b32f0861 --- /dev/null +++ b/src/Brain/BrainOpenGLChartTwoDrawingInterface.h @@ -0,0 +1,82 @@ +#ifndef __BRAIN_OPEN_G_L_CHART_TWO_DRAWING_INTERFACE_H__ +#define __BRAIN_OPEN_G_L_CHART_TWO_DRAWING_INTERFACE_H__ + +/*LICENSE_START*/ +/* + * Copyright (C) 2017 Washington University School of Medicine + * + * 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 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ +/*LICENSE_END*/ + + + + +#include "SelectionItemDataTypeEnum.h" + +namespace caret { + + class Annotation; + class Brain; + class BrainOpenGLFixedPipeline; + class BrainOpenGLTextRenderInterface; + class ModelChartTwo; + + class BrainOpenGLChartTwoDrawingInterface { + + public: + BrainOpenGLChartTwoDrawingInterface() { } + + virtual ~BrainOpenGLChartTwoDrawingInterface() { } + + /** + * Draw charts from a chart overlay. + * + * @param brain + * Brain. + * @param chartTwoModel + * The chart two model. + * @param fixedPipelineDrawing + * The fixed pipeline OpenGL drawing. + * @param selectionItemDataType + * Selected data type. + * @param viewport + * Viewport for the chart. + * @param annotationChartGraphicsLabelsOut + * Output containing annotation chart axis labels that will be drawn + * by the Annotation OpenGL Drawing. + */ + virtual void drawChartOverlaySet(Brain* brain, + ModelChartTwo* chartTwoModel, + BrainOpenGLFixedPipeline* fixedPipelineDrawing, + const SelectionItemDataTypeEnum::Enum selectionItemDataType, + const int32_t viewport[4], + std::vector& annotationChartGraphicsLabelsOut) = 0; + + + // ADD_NEW_METHODS_HERE + + private: + BrainOpenGLChartTwoDrawingInterface(const BrainOpenGLChartTwoDrawingInterface&); + + BrainOpenGLChartTwoDrawingInterface& operator=(const BrainOpenGLChartTwoDrawingInterface&); + + // ADD_NEW_MEMBERS_HERE + + }; + + +} // namespace +#endif //__BRAIN_OPEN_G_L_CHART_TWO_DRAWING_INTERFACE_H__ diff --git a/src/Brain/BrainOpenGLFixedPipeline.cxx b/src/Brain/BrainOpenGLFixedPipeline.cxx index 0c6c988336f6c1e8eda3714d1e2acd9ebbfff1a3..24a865e05c7ad4381dcaebf04048db4cd375bb87 100644 --- a/src/Brain/BrainOpenGLFixedPipeline.cxx +++ b/src/Brain/BrainOpenGLFixedPipeline.cxx @@ -43,15 +43,14 @@ #include "Brain.h" #include "BrainOpenGLAnnotationDrawingFixedPipeline.h" #include "BrainOpenGLChartDrawingFixedPipeline.h" +#include "BrainOpenGLChartTwoDrawingFixedPipeline.h" #include "BrainOpenGLPrimitiveDrawing.h" -#include "BrainOpenGLTextureManager.h" #include "BrainOpenGLVolumeObliqueSliceDrawing.h" #include "BrainOpenGLVolumeSliceDrawing.h" #include "BrainOpenGLShapeCone.h" #include "BrainOpenGLShapeCube.h" #include "BrainOpenGLShapeCylinder.h" #include "BrainOpenGLShapeRing.h" -#include "BrainOpenGLShapeRingOutline.h" #include "BrainOpenGLShapeSphere.h" #include "BrainOpenGLViewportContent.h" #include "BrainStructure.h" @@ -61,13 +60,13 @@ #include "CaretDataFileSelectionModel.h" #include "CaretLogger.h" #include "CaretMappableDataFile.h" -#include "CaretMappableDataFileAndMapSelectionModel.h" #include "CaretPreferences.h" #include "ChartableMatrixInterface.h" #include "ChartableMatrixSeriesInterface.h" #include "ChartModelDataSeries.h" #include "ChartModelFrequencySeries.h" #include "ChartModelTimeSeries.h" +#include "ChartTwoOverlaySet.h" #include "CiftiBrainordinateLabelFile.h" #include "CiftiFiberOrientationFile.h" #include "CiftiFiberTrajectoryFile.h" @@ -89,7 +88,7 @@ #include "EventManager.h" #include "EventModelSurfaceGet.h" #include "EventNodeIdentificationColorsGetFromCharts.h" -#include "EventPaletteGetByName.h" +#include "EventOpenGLObjectToWindowTransform.h" #include "FastStatistics.h" #include "Fiber.h" #include "FiberOrientation.h" @@ -100,6 +99,10 @@ #include "GapsAndMargins.h" #include "GiftiLabel.h" #include "GiftiLabelTable.h" +#include "GraphicsEngineDataOpenGL.h" +#include "GraphicsPrimitiveV3fC4ub.h" +#include "GraphicsPrimitiveV3f.h" +#include "GraphicsShape.h" #include "GroupAndNameHierarchyModel.h" #include "IdentifiedItemNode.h" #include "IdentificationManager.h" @@ -121,6 +124,7 @@ #include "SelectionManager.h" #include "MathFunctions.h" #include "ModelChart.h" +#include "ModelChartTwo.h" #include "ModelSurface.h" #include "ModelSurfaceMontage.h" #include "ModelVolume.h" @@ -130,7 +134,6 @@ #include "OverlaySet.h" #include "Palette.h" #include "PaletteColorMapping.h" -#include "PaletteFile.h" #include "PaletteScalarAndColor.h" #include "Plane.h" #include "SessionManager.h" @@ -150,39 +153,26 @@ using namespace caret; -/* - * When true, - */ -static bool drawTabAnnotationsAfterTabContentFlag = true; - /** * Constructor. * - * @param parentGLWidget + * @param textRenderer * The optional text renderer is used for text rendering. * This parameter may be NULL in which case no text * rendering is performed. */ -BrainOpenGLFixedPipeline::BrainOpenGLFixedPipeline(const int32_t windowIndex, - BrainOpenGLTextRenderInterface* textRenderer) -: BrainOpenGL(textRenderer), -m_windowIndex(windowIndex) +BrainOpenGLFixedPipeline::BrainOpenGLFixedPipeline(BrainOpenGLTextRenderInterface* textRenderer) +: BrainOpenGL(textRenderer) { - CaretAssert((m_windowIndex >= 0) - && (m_windowIndex < BrainConstants::MAXIMUM_NUMBER_OF_BROWSER_WINDOWS)); - this->initializeMembersBrainOpenGL(); this->colorIdentification = new IdentificationWithColor(); m_annotationDrawing.grabNew(new BrainOpenGLAnnotationDrawingFixedPipeline(this)); - m_textureManager.grabNew(new BrainOpenGLTextureManager(m_windowIndex)); - + m_shapeSphere = NULL; m_shapeCone = NULL; m_shapeCylinder = NULL; m_shapeCube = NULL; m_shapeCubeRounded = NULL; - m_shapeCircleOutline = NULL; - m_shapeCircleFilled = NULL; this->surfaceNodeColoring = new SurfaceNodeColoring(); m_brain = NULL; m_clippingPlaneGroup = NULL; @@ -217,37 +207,25 @@ BrainOpenGLFixedPipeline::~BrainOpenGLFixedPipeline() delete m_shapeCubeRounded; m_shapeCubeRounded = NULL; } - if (m_shapeCircleFilled != NULL) { - delete m_shapeCircleFilled; - m_shapeCircleFilled = NULL; - } - if (m_shapeCircleOutline != NULL) { - delete m_shapeCircleOutline; - m_shapeCircleOutline = NULL; - } if (this->surfaceNodeColoring != NULL) { delete this->surfaceNodeColoring; this->surfaceNodeColoring = NULL; } - for (std::map::iterator iter = m_shapeEllipseOutlines.begin(); - iter != m_shapeEllipseOutlines.end(); - iter++) { - BrainOpenGLShapeRingOutline* shape = iter->second; - delete shape; - } - m_shapeEllipseOutlines.clear(); - delete this->colorIdentification; this->colorIdentification = NULL; + + GraphicsShape::deleteAllPrimitives(); } /** * Selection on a model. * + * @param windowIndex + * Index of window for selection * @param brain * The brain (must be valid!) - * @param viewportConent + * @param viewportContent * Viewport content in which mouse was clicked * @param mouseX * X position of mouse click @@ -262,18 +240,24 @@ BrainOpenGLFixedPipeline::~BrainOpenGLFixedPipeline() * selected. */ void -BrainOpenGLFixedPipeline::selectModel(Brain* brain, - BrainOpenGLViewportContent* viewportContent, +BrainOpenGLFixedPipeline::selectModelImplementation(const int32_t windowIndex, + Brain* brain, + const BrainOpenGLViewportContent* viewportContent, const int32_t mouseX, const int32_t mouseY, const bool applySelectionBackgroundFiltering) { m_brain = brain; + m_windowIndex = windowIndex; CaretAssert(m_brain); + CaretAssert((m_windowIndex >= 0) + && (m_windowIndex < BrainConstants::MAXIMUM_NUMBER_OF_BROWSER_WINDOWS)); setTabViewport(viewportContent); - std::vector viewportContentsVector; + m_specialCaseGraphicsAnnotations.clear(); + + std::vector viewportContentsVector; viewportContentsVector.push_back(viewportContent); setAnnotationColorBarsForDrawing(viewportContentsVector); @@ -296,16 +280,13 @@ BrainOpenGLFixedPipeline::selectModel(Brain* brain, this->drawModelInternal(MODE_IDENTIFICATION, viewportContent); - if (drawTabAnnotationsAfterTabContentFlag) { - /* - * Clear depth buffer since tab and window - * annotations ALWAYS are on top of - * everything else. - */ - glClear(GL_DEPTH_BUFFER_BIT); - drawTabAnnotations(viewportContent); //, -// m_tabViewport); - } + /* + * Clear depth buffer since tab and window + * annotations ALWAYS are on top of + * everything else. + */ + glClear(GL_DEPTH_BUFFER_BIT); + drawTabAnnotations(viewportContent); int windowViewport[4]; viewportContent->getWindowViewport(windowViewport); @@ -314,6 +295,7 @@ BrainOpenGLFixedPipeline::selectModel(Brain* brain, m_brain->getSelectionManager()->filterSelections(applySelectionBackgroundFiltering); m_brain = NULL; + m_windowIndex = -1; } /** @@ -322,6 +304,8 @@ BrainOpenGLFixedPipeline::selectModel(Brain* brain, * coordinate in 'projectionOut' will be valid. In addition, * the barycentric coordinate may also be valid in 'projectionOut'. * + * @param windowIndex + * Index of window for projection * @param brain * The brain (must be valid!) * @param viewportContent @@ -334,19 +318,24 @@ BrainOpenGLFixedPipeline::selectModel(Brain* brain, * Contains projection result upon exit. */ void -BrainOpenGLFixedPipeline::projectToModel(Brain* brain, - BrainOpenGLViewportContent* viewportContent, +BrainOpenGLFixedPipeline::projectToModelImplementation(const int32_t windowIndex, + Brain* brain, + const BrainOpenGLViewportContent* viewportContent, const int32_t mouseX, const int32_t mouseY, SurfaceProjectedItem& projectionOut) { m_brain = brain; + m_windowIndex = windowIndex; CaretAssert(m_brain); + CaretAssert((m_windowIndex >= 0) + && (m_windowIndex < BrainConstants::MAXIMUM_NUMBER_OF_BROWSER_WINDOWS)); setTabViewport(viewportContent); m_clippingPlaneGroup = NULL; + m_specialCaseGraphicsAnnotations.clear(); m_brain->getSelectionManager()->reset(); this->modeProjectionData = &projectionOut; @@ -370,14 +359,49 @@ BrainOpenGLFixedPipeline::projectToModel(Brain* brain, this->modeProjectionData = NULL; m_brain = NULL; + m_windowIndex = -1; +} + +/** + * Setup the content of the transform event with current transformation data. + * + * @param transformEvent + * The transform event. + */ +void +BrainOpenGLFixedPipeline::loadObjectToWindowTransform(EventOpenGLObjectToWindowTransform* transformEvent) +{ + + if (getContextSharingGroupPointer() != NULL) { + std::array modelviewArray; + std::array projectionArray; + std::array depthRange; + std::array viewport; + + glGetDoublev(GL_MODELVIEW_MATRIX, modelviewArray.data()); + glGetDoublev(GL_PROJECTION_MATRIX, projectionArray.data()); + glGetDoublev(GL_DEPTH_RANGE, depthRange.data()); + glGetIntegerv(GL_VIEWPORT, viewport.data()); + + transformEvent->setup(modelviewArray, + projectionArray, + viewport, + depthRange, + s_gluLookAtCenterFromEyeOffsetDistance); + transformEvent->setEventProcessed(); + } + else { + CaretAssertMessage(0, "Received EventOpenGLObjectToWindowTransform but current context is invalid."); + } + } /** - * Update the foreground and background colors using the model in + * Update the foreground and background colors using the model in * the given viewport content. */ void -BrainOpenGLFixedPipeline::updateForegroundAndBackgroundColors(BrainOpenGLViewportContent* vpContent) +BrainOpenGLFixedPipeline::updateForegroundAndBackgroundColors(const BrainOpenGLViewportContent* vpContent) { /* * Default to colors for surface @@ -398,6 +422,10 @@ BrainOpenGLFixedPipeline::updateForegroundAndBackgroundColors(BrainOpenGLViewpor prefs->getBackgroundAndForegroundColors()->getColorForegroundChartView(m_foregroundColorByte); prefs->getBackgroundAndForegroundColors()->getColorBackgroundChartView(m_backgroundColorByte); break; + case ModelTypeEnum::MODEL_TYPE_CHART_TWO: + prefs->getBackgroundAndForegroundColors()->getColorForegroundChartView(m_foregroundColorByte); + prefs->getBackgroundAndForegroundColors()->getColorBackgroundChartView(m_backgroundColorByte); + break; case ModelTypeEnum::MODEL_TYPE_INVALID: break; case ModelTypeEnum::MODEL_TYPE_SURFACE: @@ -459,30 +487,17 @@ BrainOpenGLFixedPipeline::setTabViewport(const BrainOpenGLViewportContent* vpCon * Output with window color bars in the viewports. */ void -BrainOpenGLFixedPipeline::setAnnotationColorBarsForDrawing(std::vector& /*viewportContents*/) +BrainOpenGLFixedPipeline::setAnnotationColorBarsForDrawing(const std::vector& /*viewportContents*/) { m_annotationColorBarsForDrawing.clear(); -// std::vector tabIndices; -// for (int32_t i = 0; i < static_cast(viewportContents.size()); i++) { -// /* -// * Viewport of window. -// */ -// BrainOpenGLViewportContent* vpContent = viewportContents[i]; -// if (vpContent != NULL) { -// BrowserTabContent* tabContent = vpContent->getBrowserTabContent(); -// if (tabContent != NULL) { -// tabIndices.push_back(tabContent->getTabNumber()); -// } -// } -// } /* * When in tile tabs, a tab may have a color bar in window * space that appears in a different tab. So, we need to * get all color bars. Otherwise, the user will not be able * to select a color when it is displayed in a different tab. */ - EventAnnotationColorBarGet colorBarEvent; //tabIndices); + EventAnnotationColorBarGet colorBarEvent; EventManager::get()->sendEvent(colorBarEvent.getPointer()); m_annotationColorBarsForDrawing = colorBarEvent.getAnnotationColorBars(); } @@ -490,20 +505,27 @@ BrainOpenGLFixedPipeline::setAnnotationColorBarsForDrawing(std::vector& viewportContents) +BrainOpenGLFixedPipeline::drawModelsImplementation(const int32_t windowIndex, + Brain* brain, + const std::vector& viewportContents) { m_brain = brain; + m_windowIndex = windowIndex; CaretAssert(m_brain); + CaretAssert((m_windowIndex >= 0) + && (m_windowIndex < BrainConstants::MAXIMUM_NUMBER_OF_BROWSER_WINDOWS)); setTabViewport(NULL); + m_specialCaseGraphicsAnnotations.clear(); setAnnotationColorBarsForDrawing(viewportContents); m_tileTabsActiveFlag = (viewportContents.size() > 1); @@ -550,7 +572,7 @@ BrainOpenGLFixedPipeline::drawModels(Brain* brain, /* * Viewport of window. */ - BrainOpenGLViewportContent* vpContent = viewportContents[i]; + const BrainOpenGLViewportContent* vpContent = viewportContents[i]; setTabViewport(vpContent); glViewport(m_tabViewport[0], m_tabViewport[1], m_tabViewport[2], m_tabViewport[3]); @@ -601,98 +623,35 @@ BrainOpenGLFixedPipeline::drawModels(Brain* brain, * in Tile Tabs when user selects a tab. */ if (vpContent->isTabHighlighted()) { - glMatrixMode(GL_PROJECTION); - glPushMatrix(); - glLoadIdentity(); -// const float width = windowViewport[2]; -// const float height = windowViewport[3]; - const float width = m_tabViewport[2]; - const float height = m_tabViewport[3]; - glOrtho(0.0, width, 0.0, height, -100.0, 100.0); - - glMatrixMode(GL_MODELVIEW); - glPushMatrix(); - glLoadIdentity(); - - glColor3fv(m_foregroundColorFloat); - - const float thickness = 10; - - /* - * Left Side - */ - glBegin(GL_QUADS); - glVertex3f(0.0, 0.0, 0.0); - glVertex3f(thickness, 0.0, 0.0); - glVertex3f(thickness, height, 0.0); - glVertex3f(0.0, height, 0.0); - glEnd(); - - /* - * Right Side - */ - glBegin(GL_QUADS); - glVertex3f(width - thickness, 0.0, 0.0); - glVertex3f(width, 0.0, 0.0); - glVertex3f(width, height, 0.0); - glVertex3f(width - thickness, height, 0.0); - glEnd(); - - /* - * Bottom Side - */ - glBegin(GL_QUADS); - glVertex3f(0.0, 0.0, 0.0); - glVertex3f(width, 0.0, 0.0); - glVertex3f(width, thickness, 0.0); - glVertex3f(0.0, thickness, 0.0); - glEnd(); - - /* - * Top Side - */ - glBegin(GL_QUADS); - glVertex3f(0.0, height - thickness, 0.0); - glVertex3f(width, height - thickness, 0.0); - glVertex3f(width, height, 0.0); - glVertex3f(0.0, height, 0.0); - glEnd(); - - glPopMatrix(); - glMatrixMode(GL_PROJECTION); - glPopMatrix(); - - glMatrixMode(GL_MODELVIEW); + drawTabHighlighting(m_tabViewport[2], + m_tabViewport[3], + m_foregroundColorFloat); } } if ( ! viewportContents.empty()) { - if (drawTabAnnotationsAfterTabContentFlag) { + /* + * Clear depth buffer since tab and window + * annotations ALWAYS are on top of + * everything else. + */ + glClear(GL_DEPTH_BUFFER_BIT); + + for (int32_t i = 0; i < static_cast(viewportContents.size()); i++) { /* - * Clear depth buffer since tab and window - * annotations ALWAYS are on top of - * everything else. + * Viewport of window. */ - glClear(GL_DEPTH_BUFFER_BIT); + const BrainOpenGLViewportContent* vpContent = viewportContents[i]; + setTabViewport(vpContent); - for (int32_t i = 0; i < static_cast(viewportContents.size()); i++) { - /* - * Viewport of window. - */ - BrainOpenGLViewportContent* vpContent = viewportContents[i]; - setTabViewport(vpContent); - - /* - * Update foreground and background colors for model - */ - updateForegroundAndBackgroundColors(vpContent); - - drawTabAnnotations(vpContent); - //m_tabViewport); - } + /* + * Update foreground and background colors for model + */ + updateForegroundAndBackgroundColors(vpContent); + + drawTabAnnotations(vpContent); } - /* * Draw window viewport annotations */ @@ -702,20 +661,169 @@ BrainOpenGLFixedPipeline::drawModels(Brain* brain, drawWindowAnnotations(windowViewport); } + m_specialCaseGraphicsAnnotations.clear(); + this->checkForOpenGLError(NULL, "At end of drawModels()"); m_brain = NULL; + m_windowIndex = -1; +} + +/** + * Draw a box to highlight a selected tab. + * + * @param width + * Width of tab. + * @param height + * Height of tab. + * @param rgb + * RGB color components for the 'frame' drawn around the tab. + */ +void +BrainOpenGLFixedPipeline::drawTabHighlighting(const float width, + const float height, + const float rgb[3]) +{ + const bool depthFlag = glIsEnabled(GL_DEPTH_TEST); + glDisable(GL_DEPTH_TEST); + + glMatrixMode(GL_PROJECTION); + glPushMatrix(); + glLoadIdentity(); + glOrtho(0.0, width, 0.0, height, -100.0, 100.0); + + glMatrixMode(GL_MODELVIEW); + glPushMatrix(); + glLoadIdentity(); + + glColor3fv(rgb); + + const float thickness = 10; + + /* + * Left Side + */ + glBegin(GL_QUADS); + glVertex3f(0.0, 0.0, 0.0); + glVertex3f(thickness, 0.0, 0.0); + glVertex3f(thickness, height, 0.0); + glVertex3f(0.0, height, 0.0); + glEnd(); + + /* + * Right Side + */ + glBegin(GL_QUADS); + glVertex3f(width - thickness, 0.0, 0.0); + glVertex3f(width, 0.0, 0.0); + glVertex3f(width, height, 0.0); + glVertex3f(width - thickness, height, 0.0); + glEnd(); + + /* + * Bottom Side + */ + glBegin(GL_QUADS); + glVertex3f(0.0, 0.0, 0.0); + glVertex3f(width, 0.0, 0.0); + glVertex3f(width, thickness, 0.0); + glVertex3f(0.0, thickness, 0.0); + glEnd(); + + /* + * Top Side + */ + glBegin(GL_QUADS); + glVertex3f(0.0, height - thickness, 0.0); + glVertex3f(width, height - thickness, 0.0); + glVertex3f(width, height, 0.0); + glVertex3f(0.0, height, 0.0); + glEnd(); + + glPopMatrix(); + glMatrixMode(GL_PROJECTION); + glPopMatrix(); + + glMatrixMode(GL_MODELVIEW); + + if (depthFlag) { + glEnable(GL_DEPTH_TEST); + } +} + +/** + * Draw the chart coordinate space annotations. + * + * @param tabContent + * Viewport content + */ +void +BrainOpenGLFixedPipeline::drawChartCoordinateSpaceAnnotations(const BrainOpenGLViewportContent* viewportContent) +{ + glPushAttrib(GL_VIEWPORT_BIT); + + Matrix4x4 projectionMatrix; + Matrix4x4 modelviewMatrix; + int viewport[4]; + if (viewportContent->getChartDataMatricesAndViewport(projectionMatrix, + modelviewMatrix, + viewport)) { + + glViewport(viewport[0], + viewport[1], + viewport[2], + viewport[3]); + + glMatrixMode(GL_PROJECTION); + glPushMatrix(); + float projectionArray[16]; + projectionMatrix.getMatrixForOpenGL(projectionArray); + glLoadMatrixf(projectionArray); + + glMatrixMode(GL_MODELVIEW); + glPushMatrix(); + float modelviewArray[16]; + modelviewMatrix.getMatrixForOpenGL(modelviewArray); + glLoadMatrixf(modelviewArray); + + /* + * Draw annotations for this surface and maybe draw + * the model annotations. + */ + BrainOpenGLAnnotationDrawingFixedPipeline::Inputs inputs(this->m_brain, + this->mode, + BrainOpenGLFixedPipeline::s_gluLookAtCenterFromEyeOffsetDistance, + m_windowIndex, + this->windowTabIndex, + BrainOpenGLAnnotationDrawingFixedPipeline::Inputs::WINDOW_DRAWING_NO); + std::vector emptyColorBars; + std::vector emptyViewportAnnotations; + m_annotationDrawing->drawAnnotations(&inputs, + AnnotationCoordinateSpaceEnum::CHART, + emptyColorBars, + emptyViewportAnnotations, + NULL); + + + + glPopMatrix(); + glMatrixMode(GL_PROJECTION); + glPopMatrix(); + glMatrixMode(GL_MODELVIEW); + } + + glPopAttrib(); } + /** * Draw the tab annotations. * - * @param windowViewport - * Viewport (x, y, w, h). + * @param tabContent + * Viewport content */ void -BrainOpenGLFixedPipeline::drawTabAnnotations(BrainOpenGLViewportContent* tabContent) -// const int tabViewport[4]) +BrainOpenGLFixedPipeline::drawTabAnnotations(const BrainOpenGLViewportContent* tabContent) { if (tabContent->getBrowserTabContent() == NULL) { return; @@ -745,14 +853,13 @@ BrainOpenGLFixedPipeline::drawTabAnnotations(BrainOpenGLViewportContent* tabCont BrainOpenGLAnnotationDrawingFixedPipeline::Inputs inputs(this->m_brain, this->mode, BrainOpenGLFixedPipeline::s_gluLookAtCenterFromEyeOffsetDistance, - tabViewport, m_windowIndex, this->windowTabIndex, - BrainOpenGLAnnotationDrawingFixedPipeline::Inputs::TEXT_HEIGHT_USE_OPENGL_VIEWPORT_HEIGHT, BrainOpenGLAnnotationDrawingFixedPipeline::Inputs::WINDOW_DRAWING_NO); m_annotationDrawing->drawAnnotations(&inputs, AnnotationCoordinateSpaceEnum::TAB, m_annotationColorBarsForDrawing, + m_specialCaseGraphicsAnnotations, NULL); glPopMatrix(); @@ -776,16 +883,13 @@ BrainOpenGLFixedPipeline::drawWindowAnnotations(const int windowViewport[4]) * User may want window annotations only when in tile tabs view. */ BrainOpenGLAnnotationDrawingFixedPipeline::Inputs::WindowDrawingMode windowDrawingMode = BrainOpenGLAnnotationDrawingFixedPipeline::Inputs::WINDOW_DRAWING_NO; -// bool drawWindowAnnotationsFlag = false; if (m_tileTabsActiveFlag) { -// drawWindowAnnotationsFlag = true; windowDrawingMode = BrainOpenGLAnnotationDrawingFixedPipeline::Inputs::WINDOW_DRAWING_YES; } else { const DisplayPropertiesAnnotation* dpa = m_brain->getDisplayPropertiesAnnotation(); if (dpa->isDisplayWindowAnnotationsInSingleTabViews(m_windowIndex)) { windowDrawingMode = BrainOpenGLAnnotationDrawingFixedPipeline::Inputs::WINDOW_DRAWING_YES; -// drawWindowAnnotationsFlag = true; } } @@ -816,17 +920,16 @@ BrainOpenGLFixedPipeline::drawWindowAnnotations(const int windowViewport[4]) BrainOpenGLAnnotationDrawingFixedPipeline::Inputs inputs(this->m_brain, this->mode, BrainOpenGLFixedPipeline::s_gluLookAtCenterFromEyeOffsetDistance, - windowViewport, m_windowIndex, this->windowTabIndex, - BrainOpenGLAnnotationDrawingFixedPipeline::Inputs::TEXT_HEIGHT_USE_OPENGL_VIEWPORT_HEIGHT, windowDrawingMode); m_annotationDrawing->drawAnnotations(&inputs, AnnotationCoordinateSpaceEnum::WINDOW, m_annotationColorBarsForDrawing, + m_specialCaseGraphicsAnnotations, NULL); - + glPopMatrix(); glMatrixMode(GL_PROJECTION); glPopMatrix(); @@ -843,7 +946,7 @@ BrainOpenGLFixedPipeline::drawWindowAnnotations(const int windowViewport[4]) */ void BrainOpenGLFixedPipeline::drawModelInternal(Mode mode, - BrainOpenGLViewportContent* viewportContent) + const BrainOpenGLViewportContent* viewportContent) { ElapsedTimer et; et.start(); @@ -859,7 +962,17 @@ BrainOpenGLFixedPipeline::drawModelInternal(Mode mode, this->mode = mode; -// drawBackgroundImage(viewportContent); + glPushAttrib(GL_MULTISAMPLE_BIT); + switch (this->mode) { + case MODE_DRAWING: + break; + case MODE_IDENTIFICATION: + glDisable(GL_MULTISAMPLE); + break; + case MODE_PROJECTION: + glDisable(GL_MULTISAMPLE); + break; + } if (this->browserTabContent != NULL) { m_clippingPlaneGroup = const_cast(this->browserTabContent->getClippingPlaneGroup()); @@ -878,16 +991,17 @@ BrainOpenGLFixedPipeline::drawModelInternal(Mode mode, if(model != NULL) { CaretAssert((this->windowTabIndex >= 0) && (this->windowTabIndex < BrainConstants::MAXIMUM_NUMBER_OF_BROWSER_TABS)); - bool modelAllowsPalettes = true; - ModelChart* modelChart = dynamic_cast(model); + ModelChartTwo* modelTwoChart = dynamic_cast(model); ModelSurface* surfaceModel = dynamic_cast(model); ModelSurfaceMontage* surfaceMontageModel = dynamic_cast(model); ModelVolume* volumeModel = dynamic_cast(model); ModelWholeBrain* wholeBrainModel = dynamic_cast(model); if (modelChart != NULL) { - drawChartData(browserTabContent, modelChart, viewport); - modelAllowsPalettes = true; + drawChartOneData(browserTabContent, modelChart, viewport); + } + else if (modelTwoChart != NULL) { + drawChartTwoData(viewportContent, modelTwoChart, viewport); } else if (surfaceModel != NULL) { m_mirroredClippingEnabled = true; @@ -908,37 +1022,15 @@ BrainOpenGLFixedPipeline::drawModelInternal(Mode mode, wholeBrainModel, viewport); } else { - modelAllowsPalettes = false; CaretAssertMessage(0, "Unknown type of model for drawing"); } - - - int viewport[4]; - viewportContent->getModelViewport(viewport); - glViewport(viewport[0], viewport[1], viewport[2], viewport[3]); - if (modelAllowsPalettes) { - this->drawAllPalettes(model->getBrain()); - } - - BrainOpenGLAnnotationDrawingFixedPipeline::Inputs inputs(this->m_brain, - this->mode, - BrainOpenGLFixedPipeline::s_gluLookAtCenterFromEyeOffsetDistance, - m_tabViewport, - m_windowIndex, - this->windowTabIndex, - BrainOpenGLAnnotationDrawingFixedPipeline::Inputs::TEXT_HEIGHT_USE_OPENGL_VIEWPORT_HEIGHT, - BrainOpenGLAnnotationDrawingFixedPipeline::Inputs::WINDOW_DRAWING_NO); - if ( ! drawTabAnnotationsAfterTabContentFlag) { - m_annotationDrawing->drawAnnotations(&inputs, - AnnotationCoordinateSpaceEnum::TAB, - m_annotationColorBarsForDrawing, - NULL); - } } } drawBackgroundImage(viewportContent); + glPopAttrib(); + glFlush(); this->checkForOpenGLError(model, "At end of drawModelInternal()"); @@ -952,17 +1044,6 @@ BrainOpenGLFixedPipeline::drawModelInternal(Mode mode, } } -/** - * @return Get the texture manager. - */ -BrainOpenGLTextureManager* -BrainOpenGLFixedPipeline::getTextureManager() -{ - BrainOpenGLTextureManager* tm = m_textureManager.getPointer(); - CaretAssert(tm); - return tm; -} - /** * Set the viewport. * @@ -1111,7 +1192,7 @@ BrainOpenGLFixedPipeline::applyClippingPlanes(const ClippingDataType clippingDat if (m_clippingPlaneGroup->isDisplayClippingBoxSelected()) { glColor3f(1.0, 0.0, 0.0); - glLineWidth(2.0); + setLineWidth(2.0); glPushMatrix(); glTranslatef(panning[0], panning[1], panning[2]); @@ -1253,12 +1334,12 @@ BrainOpenGLFixedPipeline::applyViewingTransformations(const Model* model, case ProjectionViewTypeEnum::PROJECTION_VIEW_CEREBELLUM_DORSAL: case ProjectionViewTypeEnum::PROJECTION_VIEW_CEREBELLUM_POSTERIOR: case ProjectionViewTypeEnum::PROJECTION_VIEW_CEREBELLUM_VENTRAL: - eyeZ = BrainOpenGLFixedPipeline::s_gluLookAtCenterFromEyeOffsetDistance; //5.0; + eyeZ = BrainOpenGLFixedPipeline::s_gluLookAtCenterFromEyeOffsetDistance; upY = 1.0; useGluLookAt = true; break; case ProjectionViewTypeEnum::PROJECTION_VIEW_CEREBELLUM_FLAT_SURFACE: - eyeZ = BrainOpenGLFixedPipeline::s_gluLookAtCenterFromEyeOffsetDistance; //5.0; + eyeZ = BrainOpenGLFixedPipeline::s_gluLookAtCenterFromEyeOffsetDistance; upY = 1.0; useGluLookAt = true; break; @@ -1304,16 +1385,6 @@ BrainOpenGLFixedPipeline::applyViewingTransformations(const Model* model, translation[1], translation[2]); -// if (rightCortexFlatFlag) { -// /* -// * When drawing right flat, the translation is "left translation" -// * so need to flip sign of X-offset. -// */ -// float rightFlatOffsetX, rightFlatOffsetY; -// browserTabContent->getRightCortexFlatMapOffset(rightFlatOffsetX, rightFlatOffsetY); -// glTranslatef(-rightFlatOffsetX, rightFlatOffsetY, 0.0); -// } - glMultMatrixd(rotationMatrixElements); /* @@ -1434,10 +1505,10 @@ BrainOpenGLFixedPipeline::getVolumeFitToWindowScalingAndTranslation(const Volume float temp; float scaleWindowX = (orthoExtentX / xExtent); - temp = (orthoExtentX / yExtent);//parasaggital y is screen x + temp = (orthoExtentX / yExtent);/* parasaggital y is screen x */ if (temp < scaleWindowX) scaleWindowX = temp; float scaleWindowY = (orthoExtentY / zExtent); - temp = (orthoExtentY / yExtent);//axial y is screen y + temp = (orthoExtentY / yExtent);/* axial y is screen y */ if (temp < scaleWindowY) scaleWindowY = temp; scalingOut = std::min(scaleWindowX, scaleWindowY); @@ -1480,6 +1551,25 @@ BrainOpenGLFixedPipeline::initializeOpenGL() */ glEnable(GL_NORMALIZE); + + /* + * OpenGL RedBook status that multisampling is available if + * GL_SAMPLE_BUFFES is 1 and GL_SAMPLES is greater than one. + */ + GLint sampleBuffersCount = 0; + glGetIntegerv(GL_SAMPLE_BUFFERS, &sampleBuffersCount); + GLint sampleCount = 0; + glGetIntegerv(GL_SAMPLES, &sampleCount); + const bool enableMultiSampleFlag = ((sampleBuffersCount >= 1) + && (sampleCount > 1)); + + if (enableMultiSampleFlag) { + glEnable(GL_MULTISAMPLE); + } + else { + glDisable(GL_MULTISAMPLE); + } + /* * Avoid drawing backfacing polygons */ @@ -1523,16 +1613,6 @@ BrainOpenGLFixedPipeline::initializeOpenGL() m_shapeCubeRounded = new BrainOpenGLShapeCube(1.0, BrainOpenGLShapeCube::ROUNDED); } - if (m_shapeCircleOutline == NULL) { - m_shapeCircleOutline = new BrainOpenGLShapeRing(20, - 0.9, - 1.0); - } - if (m_shapeCircleFilled == NULL) { - m_shapeCircleFilled = new BrainOpenGLShapeRing(20, - 0.0, - 1.0); - } if (this->initializedOpenGLFlag) { return; @@ -1570,6 +1650,12 @@ BrainOpenGLFixedPipeline::enableLighting() glEnable(GL_LIGHTING); glEnable(GL_COLOR_MATERIAL); + + if (browserTabContent != NULL) { + if ( ! browserTabContent->isLightingEnabled()) { + this->disableLighting(); + } + } break; case MODE_IDENTIFICATION: case MODE_PROJECTION: @@ -1594,6 +1680,15 @@ BrainOpenGLFixedPipeline::disableLighting() void BrainOpenGLFixedPipeline::enableLineAntiAliasing() { + /* + * If multi-sampling is enabled, it handle anti-aliasing + */ + if (glIsEnabled(GL_MULTISAMPLE)) { + return; + } + + return ; + glEnable(GL_LINE_SMOOTH); glEnable(GL_BLEND); glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); @@ -1606,6 +1701,13 @@ BrainOpenGLFixedPipeline::enableLineAntiAliasing() void BrainOpenGLFixedPipeline::disableLineAntiAliasing() { + /* + * If multi-sampling is enabled, it handle anti-aliasing + */ + if (glIsEnabled(GL_MULTISAMPLE)) { + return; + } + glDisable(GL_LINE_SMOOTH); glDisable(GL_BLEND); } @@ -1736,10 +1838,8 @@ BrainOpenGLFixedPipeline::drawSurface(Surface* surface, const DisplayPropertiesBorders* dpb = m_brain->getDisplayPropertiesBorders(); const float borderAboveSurfaceOffset = dpb->getAboveSurfaceOffset(); if (borderAboveSurfaceOffset != 0.0) { -// const float factor = borderAboveSurfaceOffset * 1.0 + 1.0; -// const float units = borderAboveSurfaceOffset * 1.0 + 1.0; - const float factor = borderAboveSurfaceOffset;// + 0.5; - const float units = 1.0;// + 0.5; + const float factor = borderAboveSurfaceOffset; + const float units = 1.0; glEnable(GL_POLYGON_OFFSET_FILL); glPolygonOffset(factor, units); } @@ -1774,20 +1874,21 @@ BrainOpenGLFixedPipeline::drawSurface(Surface* surface, BrainOpenGLAnnotationDrawingFixedPipeline::Inputs inputs(this->m_brain, this->mode, BrainOpenGLFixedPipeline::s_gluLookAtCenterFromEyeOffsetDistance, - m_tabViewport, m_windowIndex, this->windowTabIndex, - BrainOpenGLAnnotationDrawingFixedPipeline::Inputs::TEXT_HEIGHT_USE_TAB_VIEWPORT_HEIGHT, BrainOpenGLAnnotationDrawingFixedPipeline::Inputs::WINDOW_DRAWING_NO); std::vector emptyColorBars; + std::vector emptyViewportAnnotations; m_annotationDrawing->drawAnnotations(&inputs, AnnotationCoordinateSpaceEnum::SURFACE, emptyColorBars, + emptyViewportAnnotations, surface); if (drawAnnotationsInModelSpaceFlag) { m_annotationDrawing->drawAnnotations(&inputs, AnnotationCoordinateSpaceEnum::STEREOTAXIC, emptyColorBars, + emptyViewportAnnotations, NULL); } } @@ -1821,20 +1922,21 @@ BrainOpenGLFixedPipeline::drawSurface(Surface* surface, BrainOpenGLAnnotationDrawingFixedPipeline::Inputs inputs(this->m_brain, this->mode, BrainOpenGLFixedPipeline::s_gluLookAtCenterFromEyeOffsetDistance, - m_tabViewport, m_windowIndex, this->windowTabIndex, - BrainOpenGLAnnotationDrawingFixedPipeline::Inputs::TEXT_HEIGHT_USE_TAB_VIEWPORT_HEIGHT, BrainOpenGLAnnotationDrawingFixedPipeline::Inputs::WINDOW_DRAWING_NO); std::vector emptyColorBars; + std::vector emptyViewportAnnotations; m_annotationDrawing->drawAnnotations(&inputs, AnnotationCoordinateSpaceEnum::SURFACE, emptyColorBars, + emptyViewportAnnotations, surface); if (drawAnnotationsInModelSpaceFlag) { m_annotationDrawing->drawAnnotations(&inputs, AnnotationCoordinateSpaceEnum::STEREOTAXIC, emptyColorBars, + emptyViewportAnnotations, NULL); } @@ -2107,8 +2209,8 @@ BrainOpenGLFixedPipeline::drawSurfaceTriangles(Surface* surface, * Determine position in triangle using barycentric coordinates */ double displayXYZ[3] = { - this->mouseX, - this->mouseY, + (double)this->mouseX, + (double)this->mouseY, 0.0 }; @@ -2130,15 +2232,15 @@ BrainOpenGLFixedPipeline::drawSurfaceTriangles(Surface* surface, * Convert to surface coordinates */ const float projectedXYZ[3] = { - (dc1[0]*areaU + dc2[0]*areaV + dc3[0]*areaW) / totalArea, - (dc1[1]*areaU + dc2[1]*areaV + dc3[1]*areaW) / totalArea, - (dc1[2]*areaU + dc2[2]*areaV + dc3[2]*areaW) / totalArea + (float)((dc1[0]*areaU + dc2[0]*areaV + dc3[0]*areaW) / totalArea), + (float)((dc1[1]*areaU + dc2[1]*areaV + dc3[1]*areaW) / totalArea), + (float)((dc1[2]*areaU + dc2[2]*areaV + dc3[2]*areaW) / totalArea) }; const float barycentricAreas[3] = { - areaU, - areaV, - areaW + (float)areaU, + (float)areaV, + (float)areaW }; const int32_t barycentricNodes[3] = { @@ -2447,57 +2549,53 @@ BrainOpenGLFixedPipeline::drawSurfaceNodeAttributes(Surface* surface) return; break; } - - uint8_t idRGBA[4]; - - for (std::vector::const_iterator iter = identifiedNodes.begin(); - iter != identifiedNodes.end(); - iter++) { - const IdentifiedItemNode& nodeID = *iter; - - /* - * Show symbol for node ID? - */ - if ( ! nodeID.isShowIdentificationSymbol()) { - continue; - } - - const int32_t nodeIndex = nodeID.getNodeIndex(); - const int32_t i3 = nodeIndex * 3; - const float* xyz = &coordinates[i3]; - - if (m_clippingPlaneGroup->isSurfaceSelected()) { - if ( ! isCoordinateInsideClippingPlanesForStructure(structure, xyz)) { - continue; + if (idManager->isShowSurfaceIdentificationSymbols()) { + for (std::vector::const_iterator iter = identifiedNodes.begin(); + iter != identifiedNodes.end(); + iter++) { + const IdentifiedItemNode& nodeID = *iter; + const int32_t nodeIndex = nodeID.getNodeIndex(); + const int32_t i3 = nodeIndex * 3; + const float* xyz = &coordinates[i3]; + + if (m_clippingPlaneGroup->isSurfaceSelected()) { + if ( ! isCoordinateInsideClippingPlanesForStructure(structure, xyz)) { + continue; + } } - } - - const float symbolDiameter = nodeID.getSymbolSize(); - - if (isSelect) { - this->colorIdentification->addItem(idRGBA, - SelectionItemDataTypeEnum::SURFACE_NODE_IDENTIFICATION_SYMBOL, - nodeIndex); - } - else { - if (structure == nodeID.getStructure()) { - nodeID.getSymbolRGBA(idRGBA); - - colorsFromChartsEvent.applyChartColorToNode(nodeIndex, - idRGBA); + + const float symbolDiameter = nodeID.getSymbolSize(); + + uint8_t symbolRGBA[4]; + + if (isSelect) { + this->colorIdentification->addItem(symbolRGBA, + SelectionItemDataTypeEnum::SURFACE_NODE_IDENTIFICATION_SYMBOL, + nodeIndex); } else { - nodeID.getContralateralSymbolRGB(idRGBA); + if (structure == nodeID.getStructure()) { + nodeID.getSymbolRGBA(symbolRGBA); + + colorsFromChartsEvent.applyChartColorToNode(nodeIndex, + symbolRGBA); + } + else { + nodeID.getContralateralSymbolRGB(symbolRGBA); + } } + symbolRGBA[3] = 255; + + /* + * Need to draw each symbol independently since each symbol + * contains a unique size (diameter) + */ + std::unique_ptr idPrimitive(GraphicsPrimitive::newPrimitiveV3fC4ub(GraphicsPrimitive::PrimitiveType::SPHERES)); + idPrimitive->setSphereDiameter(GraphicsPrimitive::SphereSizeType::MILLIMETERS, symbolDiameter); + idPrimitive->addVertex(xyz, + symbolRGBA); + GraphicsEngineDataOpenGL::draw(idPrimitive.get()); } - idRGBA[3] = 255; - - - glPushMatrix(); - glTranslatef(xyz[0], xyz[1], xyz[2]); - this->drawSphereWithDiameter(idRGBA, - symbolDiameter); - glPopMatrix(); } if (isSelect) { @@ -2527,21 +2625,14 @@ BrainOpenGLFixedPipeline::drawSurfaceNodeAttributes(Surface* surface) * * @param borderDrawInfo * Info about border being drawn. - * @param border - * Border that is drawn on the surface. - * @param borderFileIndex - * Index of border file. - * @param borderIndex - * Index of border. - * @param isSelect - * Selection mode is active. */ -void +void BrainOpenGLFixedPipeline::drawBorder(const BorderDrawInfo& borderDrawInfo) { CaretAssert(borderDrawInfo.surface); + CaretAssert(borderDrawInfo.topologyHelper); CaretAssert(borderDrawInfo.border); - + const StructureEnum::Enum surfaceStructure = borderDrawInfo.surface->getStructure(); const StructureEnum::Enum contralateralSurfaceStructure = StructureEnum::getContralateralStructure(surfaceStructure); const int32_t numBorderPoints = borderDrawInfo.border->getNumberOfPoints(); @@ -2556,19 +2647,35 @@ BrainOpenGLFixedPipeline::drawBorder(const BorderDrawInfo& borderDrawInfo) const DisplayPropertiesBorders* dpb = brain->getDisplayPropertiesBorders(); const DisplayGroupEnum::Enum displayGroup = dpb->getDisplayGroupForTab(this->windowTabIndex); pointDiameter = dpb->getPointSize(displayGroup, - this->windowTabIndex); + this->windowTabIndex); lineWidth = dpb->getLineWidth(displayGroup, this->windowTabIndex); drawType = dpb->getDrawingType(displayGroup, this->windowTabIndex); } + if (borderDrawInfo.border == this->borderBeingDrawn) { + if (borderDrawInfo.surface != NULL) { + const BoundingBox* bb = borderDrawInfo.surface->getBoundingBox(); + const float maxSize = std::max(bb->getDifferenceX(), + std::max(bb->getDifferenceY(), bb->getDifferenceZ())); + if (maxSize > 0.0f) { + const float percentSize = 0.03f; + pointDiameter = maxSize * percentSize; + } + } + } + bool drawSphericalPoints = false; bool drawSquarePoints = false; - bool drawLines = false; + bool drawLineSegments = false; + bool drawPolylines = false; switch (drawType) { case BorderDrawingTypeEnum::DRAW_AS_LINES: - drawLines = true; + drawLineSegments = true; + break; + case BorderDrawingTypeEnum::DRAW_AS_POLYLINES: + drawPolylines = true; break; case BorderDrawingTypeEnum::DRAW_AS_POINTS_SPHERES: drawSphericalPoints = true; @@ -2577,11 +2684,11 @@ BrainOpenGLFixedPipeline::drawBorder(const BorderDrawInfo& borderDrawInfo) drawSquarePoints = true; break; case BorderDrawingTypeEnum::DRAW_AS_POINTS_AND_LINES: - drawLines = true; + drawLineSegments = true; drawSphericalPoints = true; break; } - + const bool flatSurfaceFlag = (borderDrawInfo.surface->getSurfaceType() == SurfaceTypeEnum::FLAT); bool flatSurfaceDrawUnstretchedLinesFlag = false; float unstretchedLinesLength = -1.0; @@ -2594,25 +2701,103 @@ BrainOpenGLFixedPipeline::drawBorder(const BorderDrawInfo& borderDrawInfo) } const float drawAtDistanceAboveSurface = 0.0; - - std::vector pointXYZ; - std::vector pointAnatomicalXYZ; - std::vector pointIndex; - const CaretPointer th = borderDrawInfo.surface->getTopologyHelper(); - const std::vector& nodesBoundaryEdgeCount = th->getNumberOfBoundaryEdgesForAllNodes(); + const std::vector& nodesBoundaryEdgeCount = borderDrawInfo.topologyHelper->getNumberOfBoundaryEdgesForAllNodes(); CaretAssert(static_cast(nodesBoundaryEdgeCount.size()) == borderDrawInfo.surface->getNumberOfNodes()); + const uint8_t highlightFirstPointRGBA[4] = { 0, 255, 0, 1 }; + const uint8_t highlightLastPointRGBA[4] = { 0, 192, 0, 1 }; + + const uint8_t solidColorRGBA[4] = { + static_cast(borderDrawInfo.rgba[0] * 255.0f), + static_cast(borderDrawInfo.rgba[1] * 255.0f), + static_cast(borderDrawInfo.rgba[2] * 255.0f), + static_cast(borderDrawInfo.rgba[3] * 255.0f) + }; + + std::unique_ptr pointsPrimitive; + std::unique_ptr pointsIdentificationPrimitive; + std::unique_ptr firstPointPrimitive; + std::unique_ptr lastPointPrimitive; + if (drawSquarePoints) { + if (borderDrawInfo.isSelect) { + pointsIdentificationPrimitive.reset(GraphicsPrimitive::newPrimitiveV3fC4ub(GraphicsPrimitive::PrimitiveType::OPENGL_POINTS)); + } + else { + pointsPrimitive.reset(GraphicsPrimitive::newPrimitiveV3f(GraphicsPrimitive::PrimitiveType::OPENGL_POINTS, + solidColorRGBA)); + firstPointPrimitive.reset(GraphicsPrimitive::newPrimitiveV3f(GraphicsPrimitive::PrimitiveType::OPENGL_POINTS, + highlightFirstPointRGBA)); + lastPointPrimitive.reset(GraphicsPrimitive::newPrimitiveV3f(GraphicsPrimitive::PrimitiveType::OPENGL_POINTS, + highlightLastPointRGBA)); + } + } + if (drawSphericalPoints) { + if (borderDrawInfo.isSelect) { + pointsIdentificationPrimitive.reset(GraphicsPrimitive::newPrimitiveV3fC4ub(GraphicsPrimitive::PrimitiveType::SPHERES)); + } + else { + pointsPrimitive.reset(GraphicsPrimitive::newPrimitiveV3f(GraphicsPrimitive::PrimitiveType::SPHERES, + solidColorRGBA)); + firstPointPrimitive.reset(GraphicsPrimitive::newPrimitiveV3f(GraphicsPrimitive::PrimitiveType::SPHERES, + highlightFirstPointRGBA)); + lastPointPrimitive.reset(GraphicsPrimitive::newPrimitiveV3f(GraphicsPrimitive::PrimitiveType::SPHERES, + highlightLastPointRGBA)); + } + } + + /* + * Due to the possibility of 'unstretched lines' not being drawn + * must OPENGL_LINES must be used since there is no way to skip + * a line segment in a line strip + */ + GraphicsPrimitive::PrimitiveType lineType = GraphicsPrimitive::PrimitiveType::OPENGL_LINES; + bool allowPrimitiveRestartFlag = false; + if (drawPolylines) { + lineType = GraphicsPrimitive::PrimitiveType::POLYGONAL_LINE_STRIP_BEVEL_JOIN; + allowPrimitiveRestartFlag = true; + } + std::unique_ptr linesPrimitive; + std::unique_ptr linesIdentificationPrimitive; + if (drawLineSegments + || drawPolylines) { + if (borderDrawInfo.isSelect) { + linesIdentificationPrimitive.reset(GraphicsPrimitive::newPrimitiveV3fC4ub(lineType)); + } + else { + linesPrimitive.reset(GraphicsPrimitive::newPrimitiveV3f(lineType, + solidColorRGBA)); + } + } + + const bool featureClippingFlag = isFeatureClippingEnabled(); + + bool lastPointForLineValidFlag = false; + float lastPointForLineXYZ[3] = { 0.0f, 0.0f, 0.0f }; /* * Find points valid for this surface */ for (int32_t i = 0; i < numBorderPoints; i++) { + /* + * If previous point was not valid, need to "restart" the border lines + */ + if (i > 0) { + if ( ! lastPointForLineValidFlag) { + if (allowPrimitiveRestartFlag) { + if (linesPrimitive) { + linesPrimitive->addPrimitiveRestart(); + } + if (linesIdentificationPrimitive) { + linesIdentificationPrimitive->addPrimitiveRestart(); + } + } + } + } + const SurfaceProjectedItem* p = borderDrawInfo.border->getPoint(i); /* - * If surface structure does not match the point's structure, - * check to see if contralateral display is enabled and - * compare contralateral surface structure to point's structure. + * Test to match structure */ const StructureEnum::Enum pointStructure = p->getStructure(); bool structureMatches = true; @@ -2624,281 +2809,199 @@ BrainOpenGLFixedPipeline::drawBorder(const BorderDrawInfo& borderDrawInfo) } } } - if (structureMatches == false) { + if ( ! structureMatches) { + lastPointForLineValidFlag = false; continue; } + /* + * Test if point projects to surface successfully + */ float xyz[3]; - bool isXyzValid = p->getProjectedPositionAboveSurface(*borderDrawInfo.surface, - xyz, - drawAtDistanceAboveSurface); + bool projectionValidFlag = p->getProjectedPositionAboveSurface(*borderDrawInfo.surface, + borderDrawInfo.topologyHelper, + xyz, + drawAtDistanceAboveSurface); + if ( ! projectionValidFlag) { + lastPointForLineValidFlag = false; + continue; + } - if (isXyzValid) { - /* - * On a flat surface, do not draw border points that are attached to all edge nodes - * as they will likely result in points outside of the flat surface - * (near cuts and medial wall) - */ - if (flatSurfaceDrawUnstretchedLinesFlag){ - if (p->getBarycentricProjection()->isValid()) { - const int32_t* baryNodes = p->getBarycentricProjection()->getTriangleNodes(); - if (baryNodes != NULL) { - int32_t edgeNodeCount = 0; - if (nodesBoundaryEdgeCount[baryNodes[0]] > 0) edgeNodeCount++; - if (nodesBoundaryEdgeCount[baryNodes[1]] > 0) edgeNodeCount++; - if (nodesBoundaryEdgeCount[baryNodes[2]] > 0) edgeNodeCount++; - if (edgeNodeCount >= 3) { - isXyzValid = false; - } + /* + * Test if point is inside clipping planes + */ + if (featureClippingFlag) { + if ( ! isCoordinateInsideClippingPlanesForStructure(surfaceStructure, + xyz)) { + lastPointForLineValidFlag = false; + continue; + } + } + + /* + * If this is a flat surface and unstretched lines is enabled + * Do not use point if it was projected to all nodes that are on edges + * Typically these edges are where cuts were made for flattening + */ + if (flatSurfaceDrawUnstretchedLinesFlag){ + if (p->getBarycentricProjection()->isValid()) { + const int32_t* baryNodes = p->getBarycentricProjection()->getTriangleNodes(); + if (baryNodes != NULL) { + int32_t edgeNodeCount = 0; + if (nodesBoundaryEdgeCount[baryNodes[0]] > 0) edgeNodeCount++; + if (nodesBoundaryEdgeCount[baryNodes[1]] > 0) edgeNodeCount++; + if (nodesBoundaryEdgeCount[baryNodes[2]] > 0) edgeNodeCount++; + if (edgeNodeCount >= 3) { + lastPointForLineValidFlag = false; + continue; } } } } - if (isXyzValid) { - if (flatSurfaceDrawUnstretchedLinesFlag) { - float anatXYZ[3]; - const bool isAnatXyzValid = p->getProjectedPositionAboveSurface(*borderDrawInfo.anatomicalSurface, - anatXYZ, - drawAtDistanceAboveSurface); - if (isAnatXyzValid) { - pointXYZ.push_back(xyz[0]); - pointXYZ.push_back(xyz[1]); - pointXYZ.push_back(xyz[2]); - pointAnatomicalXYZ.push_back(anatXYZ[0]); - pointAnatomicalXYZ.push_back(anatXYZ[1]); - pointAnatomicalXYZ.push_back(anatXYZ[2]); - pointIndex.push_back(i); - + /* + * For Lines ONLY: + * If flat surface and unstretched lines is enabled + * + */ + if (flatSurfaceDrawUnstretchedLinesFlag) { + if (i > 0) { + if (lastPointForLineValidFlag) { + float anatXYZ[3]; + const bool anatXyzValid = p->getProjectedPositionAboveSurface(*borderDrawInfo.anatomicalSurface, + borderDrawInfo.topologyHelper, + anatXYZ, + drawAtDistanceAboveSurface); + const SurfaceProjectedItem* lastP = borderDrawInfo.border->getPoint(i - 1); + float lastAnatXYZ[3]; + const bool lastAnatXyzValid = lastP->getProjectedPositionAboveSurface(*borderDrawInfo.anatomicalSurface, + borderDrawInfo.topologyHelper, + lastAnatXYZ, + drawAtDistanceAboveSurface); + if (anatXyzValid && lastAnatXyzValid) { + if (unstretchedBorderLineTest(xyz, + lastPointForLineXYZ, + anatXYZ, + lastAnatXYZ, + unstretchedLinesLength)) { + if (allowPrimitiveRestartFlag) { + if (linesPrimitive) { + linesPrimitive->addPrimitiveRestart(); + } + if (linesIdentificationPrimitive) { + linesIdentificationPrimitive->addPrimitiveRestart(); + } + } + } + } } } - else { - pointXYZ.push_back(xyz[0]); - pointXYZ.push_back(xyz[1]); - pointXYZ.push_back(xyz[2]); - pointIndex.push_back(i); - } } - } - - const bool doClipping = isFeatureClippingEnabled(); - - const int32_t numPointsToDraw = static_cast(pointXYZ.size() / 3); - if (flatSurfaceDrawUnstretchedLinesFlag) { - CaretAssert(pointXYZ.size() == pointAnatomicalXYZ.size()); - } - - /* - * Draw points - */ - if (drawSphericalPoints - || drawSquarePoints) { - for (int32_t i = 0; i < numPointsToDraw; i++) { - const int32_t i3 = i * 3; - - const float* xyz = &pointXYZ[i3]; - - if (doClipping) { - if ( ! isCoordinateInsideClippingPlanesForStructure(surfaceStructure, - xyz)) { - continue; - } - } - - glPushMatrix(); - glTranslatef(xyz[0], xyz[1], xyz[2]); + + if (borderDrawInfo.isSelect) { + uint8_t idRGBA[4]; + this->colorIdentification->addItem(idRGBA, + SelectionItemDataTypeEnum::BORDER_SURFACE, + borderDrawInfo.borderFileIndex, + borderDrawInfo.borderIndex, + i); - if (borderDrawInfo.isSelect) { - uint8_t idRGBA[4]; - this->colorIdentification->addItem(idRGBA, - SelectionItemDataTypeEnum::BORDER_SURFACE, - borderDrawInfo.borderFileIndex, - borderDrawInfo.borderIndex, - pointIndex[i]); - idRGBA[3] = 255; - if (drawSphericalPoints) { - this->drawSphereWithDiameter(idRGBA, - pointDiameter); - } - else { - this->drawSquare(idRGBA, - pointDiameter); - } + if (pointsIdentificationPrimitive) { + pointsIdentificationPrimitive->addVertex(xyz, idRGBA); } - else { - float rgba[4] = { - borderDrawInfo.rgba[0], - borderDrawInfo.rgba[1], - borderDrawInfo.rgba[2], - borderDrawInfo.rgba[3], - }; - if (isHighlightEndPoints) { - if (i == 0) { - rgba[0] = 0.0; - rgba[1] = 1.0; - rgba[2] = 0.0; - rgba[3] = 1.0; - } - else if (i == (numPointsToDraw - 1)) { - rgba[0] = 0.0; - rgba[1] = 0.75; - rgba[2] = 0.0; - rgba[3] = 1.0; + if (linesIdentificationPrimitive) { + if (drawLineSegments) { + if (lastPointForLineValidFlag) { + linesIdentificationPrimitive->addVertex(lastPointForLineXYZ, idRGBA); + linesIdentificationPrimitive->addVertex(xyz, idRGBA); } } - if (drawSphericalPoints) { - this->drawSphereWithDiameter(rgba, - pointDiameter); - } else { - this->drawSquare(rgba, - pointDiameter); + linesIdentificationPrimitive->addVertex(xyz, idRGBA); } } - - glPopMatrix(); } - } - - /* - * Draw lines - */ - if (drawLines - && (numPointsToDraw > 1)) { - this->setLineWidth(lineWidth); - - this->disableLighting(); - - if (borderDrawInfo.isSelect) { - /* - * Start at one, since need two points for each line - */ - for (int32_t i = 1; i < numPointsToDraw; i++) { - /* - * On a flat surface, do not draw a line segment if it is - * from non-consecutive border points. This occurs when - * a border point does not project to the flat surface - * due to a cut or removal of the medial wall. If helps - * prevent long border lines stretching from one edge of the - * surface to a far away edge. - */ - if (flatSurfaceDrawUnstretchedLinesFlag) { - if (pointIndex[i] != (pointIndex[i-1] + 1)) { - continue; + else { + if (pointsPrimitive) { + if (isHighlightEndPoints) { + if (i == 0) { + firstPointPrimitive->addVertex(xyz); } - } - - const int32_t i3 = i * 3; - uint8_t idRGBA[4]; - this->colorIdentification->addItem(idRGBA, - SelectionItemDataTypeEnum::BORDER_SURFACE, - borderDrawInfo.borderFileIndex, - borderDrawInfo.borderIndex, - pointIndex[i]); - glColor3ubv(idRGBA); - - CaretAssertVectorIndex(pointXYZ, i3 + 2); - const float* xyz1 = &pointXYZ[i3 - 3]; - const float* xyz2 = &pointXYZ[i3]; - - bool drawIt = true; - if (doClipping) { - if (isCoordinateInsideClippingPlanesForStructure(surfaceStructure, - xyz1) - && (isCoordinateInsideClippingPlanesForStructure(surfaceStructure, - xyz2)) ) { - /* nothing */ + else if (i == (numBorderPoints - 1)) { + lastPointPrimitive->addVertex(xyz); } else { - drawIt = false; - } - } - - if (drawIt) { - if (flatSurfaceDrawUnstretchedLinesFlag) { - CaretAssertVectorIndex(pointAnatomicalXYZ, i3 + 2); - if (unstretchedBorderLineTest(xyz1, - xyz2, - &pointAnatomicalXYZ[i3], - &pointAnatomicalXYZ[i3-3], - unstretchedLinesLength)) { - drawIt = false; - } + pointsPrimitive->addVertex(xyz); } } - if (drawIt) { - glBegin(GL_LINES); - glVertex3fv(xyz1); - glVertex3fv(xyz2); - glEnd(); + else { + pointsPrimitive->addVertex(xyz); } } - } - else { - glColor3fv(borderDrawInfo.rgba); - - /* - * Start at one, since need two points for each line - */ - glBegin(GL_LINES); - for (int32_t i = 1; i < numPointsToDraw; i++) { - /* - * On a flat surface, do not draw a line segment if it is - * from non-consecutive border points. This occurs when - * a border point does not project to the flat surface - * due to a cut or removal of the medial wall. If helps - * prevent long border lines stretching from one edge of the - * surface to a far away edge. - */ - if (flatSurfaceDrawUnstretchedLinesFlag) { - if (pointIndex[i] != (pointIndex[i-1] + 1)) { - continue; - } - } - - const int32_t i3 = i * 3; - CaretAssertVectorIndex(pointXYZ, i3 + 2); - const float* xyz1 = &pointXYZ[i3 - 3]; - const float* xyz2 = &pointXYZ[i3]; - - bool drawIt = true; - if (doClipping) { - if (isCoordinateInsideClippingPlanesForStructure(surfaceStructure, - xyz1) - && (isCoordinateInsideClippingPlanesForStructure(surfaceStructure, - xyz2)) ) { - /* nothing */ - } - else { - drawIt = false; - } - } - - - if (drawIt) { - if (flatSurfaceDrawUnstretchedLinesFlag) { - CaretAssertVectorIndex(pointAnatomicalXYZ, i3 + 2); - if (unstretchedBorderLineTest(xyz1, - xyz2, - &pointAnatomicalXYZ[i3], - &pointAnatomicalXYZ[i3-3], - unstretchedLinesLength)) { - drawIt = false; - } + if (linesPrimitive) { + if (drawLineSegments) { + if (lastPointForLineValidFlag) { + linesPrimitive->addVertex(lastPointForLineXYZ); + linesPrimitive->addVertex(xyz); } } - - if (drawIt) { - glVertex3fv(xyz1); - glVertex3fv(xyz2); + else { + linesPrimitive->addVertex(xyz); } } - glEnd(); } - this->enableLighting(); + /* + * May need point for next time + */ + lastPointForLineXYZ[0] = xyz[0]; + lastPointForLineXYZ[1] = xyz[1]; + lastPointForLineXYZ[2] = xyz[2]; + lastPointForLineValidFlag = true; } -} + + glPushAttrib(GL_ENABLE_BIT); + glDisable(GL_LIGHTING); + + if (pointsIdentificationPrimitive) { + pointsIdentificationPrimitive->setPointDiameter(GraphicsPrimitive::PointSizeType::MILLIMETERS, + pointDiameter); + pointsIdentificationPrimitive->setSphereDiameter(GraphicsPrimitive::SphereSizeType::MILLIMETERS, + pointDiameter); + GraphicsEngineDataOpenGL::draw(pointsIdentificationPrimitive.get()); + } + else if (pointsPrimitive) { + pointsPrimitive->setPointDiameter(GraphicsPrimitive::PointSizeType::MILLIMETERS, + pointDiameter); + pointsPrimitive->setSphereDiameter(GraphicsPrimitive::SphereSizeType::MILLIMETERS, + pointDiameter); + if (pointsPrimitive->getPrimitiveType() == GraphicsPrimitive::PrimitiveType::SPHERES) { + glEnable(GL_LIGHTING); + } + GraphicsEngineDataOpenGL::draw(pointsPrimitive.get()); + if (firstPointPrimitive->isValid()) { + GraphicsEngineDataOpenGL::draw(firstPointPrimitive.get()); + } + if (lastPointPrimitive->isValid()) { + GraphicsEngineDataOpenGL::draw(lastPointPrimitive.get()); + } + } + if (linesIdentificationPrimitive) { + glDisable(GL_LIGHTING); + linesIdentificationPrimitive->setLineWidth(GraphicsPrimitive::LineWidthType::PIXELS, + lineWidth); + GraphicsEngineDataOpenGL::draw(linesIdentificationPrimitive.get()); + } + else if (linesPrimitive) { + glDisable(GL_LIGHTING); + linesPrimitive->setLineWidth(GraphicsPrimitive::LineWidthType::PIXELS, + lineWidth); + GraphicsEngineDataOpenGL::draw(linesPrimitive.get()); + } + + glPopAttrib(); +}//p-> + /** * Determine if the ratio if border length over anatomical border length @@ -2938,46 +3041,6 @@ BrainOpenGLFixedPipeline::unstretchedBorderLineTest(const float p1[3], return false; } - -/** - * Set the OpenGL line width. Value is clamped - * to minimum and maximum values to prevent - * OpenGL error caused by invalid line width. - */ -void -BrainOpenGLFixedPipeline::setLineWidth(const float lineWidth) -{ - if (lineWidth > s_maxLineWidth) { - glLineWidth(s_maxLineWidth); - } - else if (lineWidth < s_minLineWidth) { - glLineWidth(s_minLineWidth); - } - else { - glLineWidth(lineWidth); - } -} - -/** - * Set the OpenGL point size. Value is clamped - * to minimum and maximum values to prevent - * OpenGL error caused by invalid point size. - */ -void -BrainOpenGLFixedPipeline::setPointSize(const float pointSize) -{ - if (pointSize > s_maxPointSize) { - glPointSize(s_maxPointSize); - } - else if (pointSize < s_minPointSize) { - glPointSize(s_minPointSize); - } - else { - glPointSize(pointSize); - } -} - - /** * Draw foci on a surface. * @param surface @@ -3027,20 +3090,29 @@ BrainOpenGLFixedPipeline::drawSurfaceFoci(Surface* surface) const bool doClipping = isFeatureClippingEnabled(); - bool drawAsSpheres = false; + bool lightingOnFlag = false; + std::unique_ptr fociPrimitive; switch (fociDisplayProperties->getDrawingType(displayGroup, this->windowTabIndex)) { case FociDrawingTypeEnum::DRAW_AS_SPHERES: - drawAsSpheres = true; + fociPrimitive.reset(GraphicsPrimitive::newPrimitiveV3fC4ub(GraphicsPrimitive::PrimitiveType::SPHERES)); + fociPrimitive->setSphereDiameter(GraphicsPrimitive::SphereSizeType::MILLIMETERS, focusDiameter); + lightingOnFlag = true; break; case FociDrawingTypeEnum::DRAW_AS_SQUARES: + fociPrimitive.reset(GraphicsPrimitive::newPrimitiveV3fC4ub(GraphicsPrimitive::PrimitiveType::OPENGL_POINTS)); + fociPrimitive->setPointDiameter(GraphicsPrimitive::PointSizeType::MILLIMETERS, focusDiameter); + lightingOnFlag = false; break; } - + if (isSelect) { + lightingOnFlag = false; + } + const CaretColorEnum::Enum caretColor = fociDisplayProperties->getStandardColorType(displayGroup, this->windowTabIndex); float caretColorRGBA[4]; - CaretColorEnum::toRGBFloat(caretColor, caretColorRGBA); + CaretColorEnum::toRGBAFloat(caretColor, caretColorRGBA); const bool isPasteOntoSurface = fociDisplayProperties->isPasteOntoSurface(displayGroup, this->windowTabIndex); @@ -3064,7 +3136,12 @@ BrainOpenGLFixedPipeline::drawSurfaceFoci(Surface* surface) for (int32_t j = 0; j < numFoci; j++) { Focus* focus = fociFile->getFocus(j); - float rgba[4] = { 0.0, 0.0, 0.0, 1.0 }; + float rgbaFloat[4] = { + caretColorRGBA[0], + caretColorRGBA[1], + caretColorRGBA[2], + caretColorRGBA[3] + }; const GroupAndNameHierarchyItem* nameItem = focus->getGroupNameSelectionItem(); if (nameItem != NULL) { @@ -3076,41 +3153,43 @@ BrainOpenGLFixedPipeline::drawSurfaceFoci(Surface* surface) switch (fociColoringType) { case FeatureColoringTypeEnum::FEATURE_COLORING_TYPE_CLASS: - if (focus->isClassRgbaValid() == false) { + if ( ! focus->isClassRgbaValid()) { const GiftiLabel* colorLabel = classColorTable->getLabelBestMatching(focus->getClassName()); if (colorLabel != NULL) { - colorLabel->getColor(rgba); - focus->setClassRgba(rgba); - } - else { - focus->setClassRgba(rgba); + colorLabel->getColor(rgbaFloat); + focus->setClassRgba(rgbaFloat); } +// else { +// focus->setClassRgba(rgbaFloat); +// } } - focus->getClassRgba(rgba); + focus->getClassRgba(rgbaFloat); break; case FeatureColoringTypeEnum::FEATURE_COLORING_TYPE_STANDARD_COLOR: - rgba[0] = caretColorRGBA[0]; - rgba[1] = caretColorRGBA[1]; - rgba[2] = caretColorRGBA[2]; - rgba[3] = caretColorRGBA[3]; + rgbaFloat[0] = caretColorRGBA[0]; + rgbaFloat[1] = caretColorRGBA[1]; + rgbaFloat[2] = caretColorRGBA[2]; + rgbaFloat[3] = caretColorRGBA[3]; break; case FeatureColoringTypeEnum::FEATURE_COLORING_TYPE_NAME: if (focus->isNameRgbaValid() == false) { const GiftiLabel* colorLabel = nameColorTable->getLabelBestMatching(focus->getName()); if (colorLabel != NULL) { - colorLabel->getColor(rgba); - focus->setNameRgba(rgba); - } - else { - focus->setNameRgba(rgba); + colorLabel->getColor(rgbaFloat); + focus->setNameRgba(rgbaFloat); } +// else { +// focus->setNameRgba(rgbaFloat); +// } } - focus->getNameRgba(rgba); + focus->getNameRgba(rgbaFloat); break; } - glColor3fv(rgba); - + /* + * Always have valid color for RGBA + */ + rgbaFloat[3] = 1.0f; const int32_t numProjections = focus->getNumberOfProjections(); for (int32_t k = 0; k < numProjections; k++) { @@ -3141,44 +3220,41 @@ BrainOpenGLFixedPipeline::drawSurfaceFoci(Surface* surface) } if (drawIt) { - glPushMatrix(); - glTranslatef(xyz[0], xyz[1], xyz[2]); - if (isSelect) { uint8_t idRGBA[4]; this->colorIdentification->addItem(idRGBA, SelectionItemDataTypeEnum::FOCUS_SURFACE, - i, // file index - j, // focus index - k);// projection index + i, /* file index */ + j, /* focus index */ + k);/* projection index */ idRGBA[3] = 255; - if (drawAsSpheres) { - this->drawSphereWithDiameter(idRGBA, - focusDiameter); - } - else { - this->drawSquare(idRGBA, - focusDiameter); - } + fociPrimitive->addVertex(xyz, idRGBA); } else { - if (drawAsSpheres) { - this->drawSphereWithDiameter(rgba, - focusDiameter); - } - else { - this->drawSquare(rgba, - focusDiameter); - } + const uint8_t rgbaByte[4] = { + static_cast(rgbaFloat[0] * 255), + static_cast(rgbaFloat[1] * 255), + static_cast(rgbaFloat[2] * 255), + static_cast(rgbaFloat[3] * 255) + }; + fociPrimitive->addVertex(xyz, rgbaByte); } - - glPopMatrix(); } } } } } + glPushAttrib(GL_ENABLE_BIT); + if (lightingOnFlag) { + enableLighting(); + } + else { + disableLighting(); + } + + GraphicsEngineDataOpenGL::draw(fociPrimitive.get()); + if (isSelect) { int32_t fociFileIndex = -1; int32_t focusIndex = -1; @@ -3211,6 +3287,8 @@ BrainOpenGLFixedPipeline::drawSurfaceFoci(Surface* surface) } } } + + glPopAttrib(); } @@ -3222,6 +3300,8 @@ BrainOpenGLFixedPipeline::drawSurfaceFoci(Surface* surface) void BrainOpenGLFixedPipeline::drawSurfaceBorders(Surface* surface) { + CaretAssert(surface); + SelectionItemBorderSurface* idBorder = m_brain->getSelectionManager()->getSurfaceBorderIdentification(); /* @@ -3266,9 +3346,13 @@ BrainOpenGLFixedPipeline::drawSurfaceBorders(Surface* surface) const CaretColorEnum::Enum caretColor = borderDisplayProperties->getStandardColorType(displayGroup, this->windowTabIndex); float caretColorRGBA[4]; - CaretColorEnum::toRGBFloat(caretColor, caretColorRGBA); + CaretColorEnum::toRGBAFloat(caretColor, caretColorRGBA); const bool isContralateralEnabled = borderDisplayProperties->isContralateralDisplayed(displayGroup, this->windowTabIndex); + BorderDrawInfo borderDrawInfo; + borderDrawInfo.surface = surface; + borderDrawInfo.topologyHelper = surface->getTopologyHelper().getPointer(); + const int32_t numBorderFiles = brain->getNumberOfBorderFiles(); for (int32_t i = 0; i < numBorderFiles; i++) { BorderFile* borderFile = brain->getBorderFile(i); @@ -3328,8 +3412,6 @@ BrainOpenGLFixedPipeline::drawSurfaceBorders(Surface* surface) } glColor3fv(rgba); - BorderDrawInfo borderDrawInfo; - borderDrawInfo.surface = surface; borderDrawInfo.border = border; borderDrawInfo.rgba[0] = rgba[0]; borderDrawInfo.rgba[1] = rgba[1]; @@ -3400,6 +3482,7 @@ BrainOpenGLFixedPipeline::drawSurfaceBorderBeingDrawn(const Surface* surface) if (this->borderBeingDrawn != NULL) { BorderDrawInfo borderDrawInfo; borderDrawInfo.surface = const_cast(surface); + borderDrawInfo.topologyHelper = surface->getTopologyHelper().getPointer(); borderDrawInfo.border = this->borderBeingDrawn; borderDrawInfo.rgba[0] = 1.0; borderDrawInfo.rgba[1] = 0.0; @@ -3435,7 +3518,6 @@ BrainOpenGLFixedPipeline::setupVolumeDrawInfo(BrowserTabContent* browserTabConte { volumeDrawInfoOut.clear(); - PaletteFile* paletteFile = brain->getPaletteFile(); OverlaySet* overlaySet = browserTabContent->getOverlaySet(); const int32_t numberOfOverlays = overlaySet->getNumberOfDisplayedOverlays(); for (int32_t iOver = (numberOfOverlays - 1); iOver >= 0; iOver--) { @@ -3466,10 +3548,9 @@ BrainOpenGLFixedPipeline::setupVolumeDrawInfo(BrowserTabContent* browserTabConte statistics = const_cast(mapFile->getMapFastStatistics(mapIndex)); break; } - //CaretAssert(statistics); PaletteColorMapping* paletteColorMapping = mapFile->getMapPaletteColorMapping(mapIndex); - Palette* palette = paletteFile->getPaletteByName(paletteColorMapping->getSelectedPaletteName()); + const Palette* palette = paletteColorMapping->getPalette(); if (palette != NULL) { /* * Statistics may be NULL for a dense connectome file @@ -3555,7 +3636,7 @@ BrainOpenGLFixedPipeline::drawVolumeModel(BrowserTabContent* browserTabContent, VolumeSliceDrawingTypeEnum::Enum sliceDrawingType = browserTabContent->getSliceDrawingType(); VolumeSliceProjectionTypeEnum::Enum sliceProjectionType = browserTabContent->getSliceProjectionType(); - + VolumeSliceInterpolationEdgeEffectsMaskingEnum::Enum obliqueMaskType = browserTabContent->getVolumeSliceInterpolationEdgeEffectsMaskingType(); /* * There is/was a flaw in volume drawing in that it does not "center" @@ -3585,11 +3666,12 @@ BrainOpenGLFixedPipeline::drawVolumeModel(BrowserTabContent* browserTabContent, else { BrainOpenGLVolumeObliqueSliceDrawing obliqueVolumeSliceDrawing; obliqueVolumeSliceDrawing.draw(this, - browserTabContent, - volumeDrawInfo, - sliceDrawingType, - sliceProjectionType, - viewport); + browserTabContent, + volumeDrawInfo, + sliceDrawingType, + sliceProjectionType, + obliqueMaskType, + viewport); } } @@ -3689,8 +3771,6 @@ BrainOpenGLFixedPipeline::drawVolumeVoxelsAsCubesWholeBrain(std::vectorgetPaletteFile(); - for (int32_t iVol = 0; iVol < numberOfVolumesToDraw; iVol++) { VolumeDrawInfo& volInfo = volumeDrawInfo[iVol]; if (volInfo.opacity < 1.0) { @@ -3742,8 +3822,7 @@ BrainOpenGLFixedPipeline::drawVolumeVoxelsAsCubesWholeBrain(std::vectorgetVoxelColorInMapForLabelData(paletteFile, - labelMapData, + ciftiLabelFile->getVoxelColorInMapForLabelData(labelMapData, iVoxel, jVoxel, kVoxel, @@ -3753,8 +3832,7 @@ BrainOpenGLFixedPipeline::drawVolumeVoxelsAsCubesWholeBrain(std::vectorgetVoxelColorInMap(paletteFile, - iVoxel, + volumeFile->getVoxelColorInMap(iVoxel, jVoxel, kVoxel, volInfo.mapIndex, @@ -4150,9 +4228,9 @@ BrainOpenGLFixedPipeline::drawAllFiberOrientations(const FiberOrientationDisplay }; const float halfMagnitudeVector[3] = { - magnitudeVector[0] * 0.5, - magnitudeVector[1] * 0.5, - magnitudeVector[2] * 0.5, + magnitudeVector[0] * 0.5f, + magnitudeVector[1] * 0.5f, + magnitudeVector[2] * 0.5f, }; /* @@ -4217,7 +4295,7 @@ BrainOpenGLFixedPipeline::drawAllFiberOrientations(const FiberOrientationDisplay { const int32_t indx = j % 3; switch (indx) { - case 0: // use RED + case 0: /* use RED */ glColor4f(BrainOpenGLFixedPipeline::COLOR_RED[0], BrainOpenGLFixedPipeline::COLOR_RED[1], BrainOpenGLFixedPipeline::COLOR_RED[2], @@ -4227,7 +4305,7 @@ BrainOpenGLFixedPipeline::drawAllFiberOrientations(const FiberOrientationDisplay fiberRGBA[2] = BrainOpenGLFixedPipeline::COLOR_RED[2]; fiberRGBA[3] = alpha; break; - case 1: // use BLUE + case 1: /* use BLUE */ glColor4f(BrainOpenGLFixedPipeline::COLOR_BLUE[0], BrainOpenGLFixedPipeline::COLOR_BLUE[1], BrainOpenGLFixedPipeline::COLOR_BLUE[2], @@ -4237,7 +4315,7 @@ BrainOpenGLFixedPipeline::drawAllFiberOrientations(const FiberOrientationDisplay fiberRGBA[2] = BrainOpenGLFixedPipeline::COLOR_BLUE[2]; fiberRGBA[3] = alpha; break; - case 2: // use GREEN + case 2: /* use GREEN */ glColor4f(BrainOpenGLFixedPipeline::COLOR_GREEN[0], BrainOpenGLFixedPipeline::COLOR_GREEN[1], BrainOpenGLFixedPipeline::COLOR_GREEN[2], @@ -4654,11 +4732,11 @@ BrainOpenGLFixedPipeline::drawEllipticalCone(const float rgba[4], double zero = 1.0e-3; if (std::abs(vz) < zero) { - ax = 57.2957795*std::acos( vx/z ); // rotation angle in x-y plane + ax = 57.2957795*std::acos( vx/z ); /* rotation angle in x-y plane */ if ( vx <= 0.0f ) ax = -ax; } else { - ax = 57.2957795*std::acos( vz/z ); // rotation angle + ax = 57.2957795*std::acos( vz/z ); /* rotation angle */ if ( vz <= 0.0f ) ax = -ax; } @@ -4675,11 +4753,11 @@ BrainOpenGLFixedPipeline::drawEllipticalCone(const float rgba[4], } if (std::abs(vz) < zero) { - glRotated(90.0, 0.0, 1.0, 0.0); // Rotate & align with x axis - glRotated(ax, -1.0, 0.0, 0.0); // Rotate to point 2 in x-y plane + glRotated(90.0, 0.0, 1.0, 0.0); /* Rotate & align with x axis */ + glRotated(ax, -1.0, 0.0, 0.0); /* Rotate to point 2 in x-y plane */ } else { - glRotated(ax, rx, ry, 0.0); // Rotate about rotation vector + glRotated(ax, rx, ry, 0.0); /* Rotate about rotation vector */ } glPushMatrix(); @@ -4739,11 +4817,11 @@ BrainOpenGLFixedPipeline::drawCylinder(const float rgba[4], double zero = 1.0e-3; if (std::abs(vz) < zero) { - ax = 57.2957795*std::acos( vx/z ); // rotation angle in x-y plane + ax = 57.2957795*std::acos( vx/z ); /* rotation angle in x-y plane */ if ( vx <= 0.0f ) ax = -ax; } else { - ax = 57.2957795*std::acos( vz/z ); // rotation angle + ax = 57.2957795*std::acos( vz/z ); /* rotation angle */ if ( vz <= 0.0f ) ax = -ax; } @@ -4760,11 +4838,11 @@ BrainOpenGLFixedPipeline::drawCylinder(const float rgba[4], } if (std::abs(vz) < zero) { - glRotated(90.0, 0.0, 1.0, 0.0); // Rotate & align with x axis - glRotated(ax, -1.0, 0.0, 0.0); // Rotate to point 2 in x-y plane + glRotated(90.0, 0.0, 1.0, 0.0); /* Rotate & align with x axis */ + glRotated(ax, -1.0, 0.0, 0.0); /* Rotate to point 2 in x-y plane */ } else { - glRotated(ax, rx, ry, 0.0); // Rotate about rotation vector + glRotated(ax, rx, ry, 0.0); /* Rotate about rotation vector */ } glPushMatrix(); @@ -5093,13 +5171,32 @@ BrainOpenGLFixedPipeline::drawWholeBrainModel(BrowserTabContent* browserTabConte VolumeSliceDrawingTypeEnum::Enum sliceDrawingType = browserTabContent->getSliceDrawingType(); VolumeSliceProjectionTypeEnum::Enum sliceProjectionType = browserTabContent->getSliceProjectionType(); - BrainOpenGLVolumeSliceDrawing volumeSliceDrawing; - volumeSliceDrawing.draw(this, - browserTabContent, - twoDimSliceDrawVolumeDrawInfo, //volumeDrawInfo, - sliceDrawingType, - sliceProjectionType, - viewport); + switch (sliceProjectionType) { + case VolumeSliceProjectionTypeEnum::VOLUME_SLICE_PROJECTION_OBLIQUE: + { + VolumeSliceInterpolationEdgeEffectsMaskingEnum::Enum obliqueMaskType = browserTabContent->getVolumeSliceInterpolationEdgeEffectsMaskingType(); + BrainOpenGLVolumeObliqueSliceDrawing volumeSliceDrawing; + volumeSliceDrawing.draw(this, + browserTabContent, + twoDimSliceDrawVolumeDrawInfo, + sliceDrawingType, + sliceProjectionType, + obliqueMaskType, + viewport); + } + break; + case VolumeSliceProjectionTypeEnum::VOLUME_SLICE_PROJECTION_ORTHOGONAL: + { + BrainOpenGLVolumeSliceDrawing volumeSliceDrawing; + volumeSliceDrawing.draw(this, + browserTabContent, + twoDimSliceDrawVolumeDrawInfo, + sliceDrawingType, + sliceProjectionType, + viewport); + } + break; + } } } } @@ -5190,60 +5287,6 @@ BrainOpenGLFixedPipeline::drawWholeBrainModel(BrowserTabContent* browserTabConte glPopMatrix(); } } -// /* -// * Draw surfaces last so that opacity works. -// */ -// const int32_t numberOfBrainStructures = brain->getNumberOfBrainStructures(); -// for (int32_t i = 0; i < numberOfBrainStructures; i++) { -// BrainStructure* brainStructure = brain->getBrainStructure(i); -// const StructureEnum::Enum structure = brainStructure->getStructure(); -// Surface* surface = wholeBrainModel->getSelectedSurface(structure, -// tabNumberIndex); -// if (surface != NULL) { -// float dx = 0.0; -// float dy = 0.0; -// float dz = 0.0; -// -// bool drawIt = false; -// switch (structure) { -// case StructureEnum::CORTEX_LEFT: -// drawIt = browserTabContent->isWholeBrainLeftEnabled(); -// dx = -browserTabContent->getWholeBrainLeftRightSeparation(); -// if ((surfaceType != SurfaceTypeEnum::ANATOMICAL) -// && (surfaceType != SurfaceTypeEnum::RECONSTRUCTION)) { -// dx -= surface->getBoundingBox()->getMaxX(); -// } -// break; -// case StructureEnum::CORTEX_RIGHT: -// drawIt = browserTabContent->isWholeBrainRightEnabled(); -// dx = browserTabContent->getWholeBrainLeftRightSeparation(); -// if ((surfaceType != SurfaceTypeEnum::ANATOMICAL) -// && (surfaceType != SurfaceTypeEnum::RECONSTRUCTION)) { -// dx -= surface->getBoundingBox()->getMinX(); -// } -// break; -// case StructureEnum::CEREBELLUM: -// drawIt = browserTabContent->isWholeBrainCerebellumEnabled(); -// dz = browserTabContent->getWholeBrainCerebellumSeparation(); -// break; -// default: -// CaretLogWarning("programmer-issure: Surface type not left/right/cerebellum"); -// break; -// } -// -// const float* nodeColoringRGBA = this->surfaceNodeColoring->colorSurfaceNodes(wholeBrainModel, -// surface, -// this->windowTabIndex); -// -// if (drawIt) { -// glPushMatrix(); -// glTranslatef(dx, dy, dz); -// this->drawSurface(surface, -// nodeColoringRGBA); -// glPopMatrix(); -// } -// } -// } } /** @@ -5257,44 +5300,46 @@ BrainOpenGLFixedPipeline::drawWholeBrainModel(BrowserTabContent* browserTabConte * The viewport (x, y, width, height) */ void -BrainOpenGLFixedPipeline::drawChartData(BrowserTabContent* browserTabContent, +BrainOpenGLFixedPipeline::drawChartOneData(BrowserTabContent* browserTabContent, ModelChart* chartModel, const int32_t viewport[4]) { + CaretAssert(browserTabContent); CaretAssert(chartModel); const int32_t tabIndex = browserTabContent->getTabNumber(); + ChartModelCartesian* cartesianChart = NULL; ChartableMatrixInterface* matrixChartFile = NULL; - const ChartDataTypeEnum::Enum chartDataType = chartModel->getSelectedChartDataType(tabIndex); + const ChartOneDataTypeEnum::Enum chartDataType = chartModel->getSelectedChartOneDataType(tabIndex); SelectionItemDataTypeEnum::Enum selectionItemDataType = SelectionItemDataTypeEnum::INVALID; int32_t scalarDataSeriesMapIndex = -1; switch (chartDataType) { - case ChartDataTypeEnum::CHART_DATA_TYPE_INVALID: + case ChartOneDataTypeEnum::CHART_DATA_TYPE_INVALID: break; - case ChartDataTypeEnum::CHART_DATA_TYPE_LINE_DATA_SERIES: + case ChartOneDataTypeEnum::CHART_DATA_TYPE_LINE_DATA_SERIES: cartesianChart = chartModel->getSelectedDataSeriesChartModel(tabIndex); selectionItemDataType = SelectionItemDataTypeEnum::CHART_DATA_SERIES; break; - case ChartDataTypeEnum::CHART_DATA_TYPE_LINE_FREQUENCY_SERIES: + case ChartOneDataTypeEnum::CHART_DATA_TYPE_LINE_FREQUENCY_SERIES: cartesianChart = chartModel->getSelectedFrequencySeriesChartModel(tabIndex); selectionItemDataType = SelectionItemDataTypeEnum::CHART_FREQUENCY_SERIES; break; - case ChartDataTypeEnum::CHART_DATA_TYPE_LINE_TIME_SERIES: + case ChartOneDataTypeEnum::CHART_DATA_TYPE_LINE_TIME_SERIES: cartesianChart = chartModel->getSelectedTimeSeriesChartModel(tabIndex); selectionItemDataType = SelectionItemDataTypeEnum::CHART_TIME_SERIES; break; - case ChartDataTypeEnum::CHART_DATA_TYPE_MATRIX_LAYER: + case ChartOneDataTypeEnum::CHART_DATA_TYPE_MATRIX_LAYER: { CaretDataFileSelectionModel* matrixFileSelector = chartModel->getChartableMatrixParcelFileSelectionModel(tabIndex); matrixChartFile = matrixFileSelector->getSelectedFileOfType(); selectionItemDataType = SelectionItemDataTypeEnum::CHART_MATRIX; } break; - case ChartDataTypeEnum::CHART_DATA_TYPE_MATRIX_SERIES: + case ChartOneDataTypeEnum::CHART_DATA_TYPE_MATRIX_SERIES: { CaretDataFileSelectionModel* fileModel = chartModel->getChartableMatrixSeriesFileSelectionModel(tabIndex); CaretDataFile* caretFile = fileModel->getSelectedFile(); @@ -5335,23 +5380,58 @@ BrainOpenGLFixedPipeline::drawChartData(BrowserTabContent* browserTabContent, } /** - * Setup the orthographic projection. + * Draw a chart two model. + * + * @param viewportContent + * Content of the viewport + * @param chartModel + * The chart model. * @param viewport * The viewport (x, y, width, height) - * @param projectionType - * Type of view projection. */ void -BrainOpenGLFixedPipeline::setOrthographicProjection(const int32_t viewport[4], - const ProjectionViewTypeEnum::Enum projectionType) +BrainOpenGLFixedPipeline::drawChartTwoData(const BrainOpenGLViewportContent* viewportContent, + ModelChartTwo* chartModel, + const int32_t viewport[4]) { - setOrthographicProjectionWithHeight(viewport, - projectionType, - getModelViewingHalfWindowHeight()); -} + + CaretAssert(browserTabContent); + CaretAssert(chartModel); -/** - * Setup the orthographic projection for the given surface file. + std::vector annotationFromChartDrawing; + BrainOpenGLChartTwoDrawingFixedPipeline chartDrawing(viewportContent); + chartDrawing.drawChartOverlaySet(m_brain, + chartModel, + this, + SelectionItemDataTypeEnum::CHART_DATA_SERIES, + viewport, + annotationFromChartDrawing); + + m_specialCaseGraphicsAnnotations.insert(m_specialCaseGraphicsAnnotations.end(), + annotationFromChartDrawing.begin(), + annotationFromChartDrawing.end()); + + drawChartCoordinateSpaceAnnotations(viewportContent); +} + +/** + * Setup the orthographic projection. + * @param viewport + * The viewport (x, y, width, height) + * @param projectionType + * Type of view projection. + */ +void +BrainOpenGLFixedPipeline::setOrthographicProjection(const int32_t viewport[4], + const ProjectionViewTypeEnum::Enum projectionType) +{ + setOrthographicProjectionWithHeight(viewport, + projectionType, + getModelViewingHalfWindowHeight()); +} + +/** + * Setup the orthographic projection for the given surface file. * * @param viewport * The viewport (x, y, width, height) @@ -5414,11 +5494,9 @@ BrainOpenGLFixedPipeline::setOrthographicProjectionForWithBoundingBox(const int3 * See also BrowserTabContent::restoreFromScene() that tries to make * old scenes compatible with this new scaling. */ - //const float zDiff = boundingBox->getDifferenceZ(); if (windowVerticalSize != 0.0) { modelHalfHeight = windowVerticalSize / 2.0; -// const float yDiff = boundingBox->getDifferenceY(); if ((windowHorizontalSize > 0.0) && (viewport[2] > 0.0)) { /* @@ -5473,8 +5551,8 @@ BrainOpenGLFixedPipeline::setOrthographicProjectionWithHeight(const int32_t view this->orthographicLeft = -halfWindowHeight * aspectRatio; this->orthographicTop = halfWindowHeight; this->orthographicBottom = -halfWindowHeight; - this->orthographicNear = -1000.0; //-500.0; //-10000.0; - this->orthographicFar = 1000.0; //500.0; // 10000.0; + this->orthographicNear = -1000.0; + this->orthographicFar = 1000.0; switch (projectionType) { case ProjectionViewTypeEnum::PROJECTION_VIEW_CEREBELLUM_ANTERIOR: @@ -5505,6 +5583,10 @@ BrainOpenGLFixedPipeline::setOrthographicProjectionWithHeight(const int32_t view this->orthographicFar, this->orthographicNear); break; } + +// std::cout << "Viewport: " << AString::fromNumbers(viewport, 4, ",") << std::endl; +// std::cout << " Ortho Left/Bottom: " << this->orthographicLeft << ", " << this->orthographicBottom << std::endl; +// std::cout << " Ortho Right/Top: " << this->orthographicRight << ", " << this->orthographicTop << std::endl; } /** @@ -5525,12 +5607,12 @@ BrainOpenGLFixedPipeline::setOrthographicProjectionWithWidth(const int32_t viewp double width = viewport[2]; double height = viewport[3]; double aspectRatio = (width / height); - this->orthographicRight = halfWindowWidth; // halfWindowHeight * aspectRatio; - this->orthographicLeft = -halfWindowWidth; // -halfWindowHeight * aspectRatio; - this->orthographicTop = halfWindowWidth / aspectRatio; // halfWindowHeight; - this->orthographicBottom = -halfWindowWidth / aspectRatio; //-halfWindowHeight; - this->orthographicNear = -1000.0; //-500.0; //-10000.0; - this->orthographicFar = 1000.0; //500.0; // 10000.0; + this->orthographicRight = halfWindowWidth; + this->orthographicLeft = -halfWindowWidth; + this->orthographicTop = halfWindowWidth / aspectRatio; + this->orthographicBottom = -halfWindowWidth / aspectRatio; + this->orthographicNear = -1000.0; + this->orthographicFar = 1000.0; switch (projectionType) { case ProjectionViewTypeEnum::PROJECTION_VIEW_CEREBELLUM_ANTERIOR: @@ -5573,62 +5655,97 @@ BrainOpenGLFixedPipeline::checkForOpenGLError(const Model* model, BrainOpenGL::testForOpenGLError(msgIn, model, this->m_windowIndex, - this->windowTabIndex); - -// GLenum errorCode = glGetError(); -// if (errorCode != GL_NO_ERROR) { -// AString msg; -// if (msgIn.isEmpty() == false) { -// msg += (msgIn + "\n"); -// } -// msg += ("OpenGL Error: " + AString((char*)gluErrorString(errorCode)) + "\n"); -// msg += ("OpenGL Version: " + AString((char*)glGetString(GL_VERSION)) + "\n"); -// msg += ("OpenGL Vendor: " + AString((char*)glGetString(GL_VENDOR)) + "\n"); -// if (model != NULL) { -// msg += ("While drawing brain model " + model->getNameForGUI(true) + "\n"); -// } -// msg += ("In tab number " + AString::number(this->windowTabIndex) + "\n"); -// -// GLint maxNameStackDepth, maxModelStackDepth, maxProjStackDepth; -// glGetIntegerv(GL_MAX_PROJECTION_STACK_DEPTH, -// &maxProjStackDepth); -// glGetIntegerv(GL_MAX_MODELVIEW_STACK_DEPTH, -// &maxModelStackDepth); -// glGetIntegerv(GL_MAX_NAME_STACK_DEPTH, -// &maxNameStackDepth); -// -// GLint nameStackDepth, modelStackDepth, projStackDepth; -// glGetIntegerv(GL_PROJECTION_STACK_DEPTH, -// &projStackDepth); -// glGetIntegerv(GL_MODELVIEW_STACK_DEPTH, -// &modelStackDepth); -// glGetIntegerv(GL_NAME_STACK_DEPTH, -// &nameStackDepth); -// -// msg += ("Projection Matrix Stack Depth " -// + AString::number(projStackDepth) -// + " Max Depth " -// + AString::number(maxProjStackDepth) -// + "\n"); -// msg += ("Model Matrix Stack Depth " -// + AString::number(modelStackDepth) -// + " Max Depth " -// + AString::number(maxModelStackDepth) -// + "\n"); -// msg += ("Name Matrix Stack Depth " -// + AString::number(nameStackDepth) -// + " Max Depth " -// + AString::number(maxNameStackDepth) -// + "\n"); -// SystemBacktrace myBacktrace; -// SystemUtilities::getBackTrace(myBacktrace); -// msg += ("Backtrace:\n" -// + myBacktrace.toSymbolString() -// + "\n"); -// CaretLogSevere(msg); -// } + this->windowTabIndex); +} + +/** + * Get the depth and RGBA value at the given pixel position. + * + * @param pixelX + * The pixel X-coordinate + * @param pixelY + * The pixel Y-coordinate + * @param depthOut + * Output containing depth at pixel. + * @param rgbaOut + * Output containing RGBA components at pixel. + * @return + * True if output is valid, else false. + * Invalid could be due to an invalid pixel XY. + * + */ +bool +BrainOpenGLFixedPipeline::getPixelDepthAndRGBA(const int32_t pixelX, + const int32_t pixelY, + float& depthOut, + float rgbaOut[4]) +{ + depthOut = -1.0; + rgbaOut[0] = 0.0; + rgbaOut[1] = 0.0; + rgbaOut[2] = 0.0; + rgbaOut[3] = 0.0; + + GLint viewport[4]; + glGetIntegerv(GL_VIEWPORT, + viewport); + if ((pixelX >= viewport[0]) + && (pixelX < viewport[2]) + && (pixelY >= viewport[1]) + && (pixelY < viewport[3])) { + /* OK */ + } + else { + /* + * Invalid pixel XY + */ + return false; + } + + /* + * Saves glPixelStore parameters + */ + glPushClientAttrib(GL_CLIENT_PIXEL_STORE_BIT); + + /* + * QOpenGLWidget Note: The QOpenGLWidget always renders in a + * frame buffer object (see its documentation). This is + * probably why calls to glReadBuffer() always cause an + * OpenGL error. + */ +#ifdef WORKBENCH_USE_QT5_QOPENGL_WIDGET + /* do not call glReadBuffer() */ +#else + glReadBuffer(GL_BACK); +#endif + glPixelStorei(GL_PACK_SKIP_ROWS, 0); + glPixelStorei(GL_PACK_SKIP_PIXELS, 0); + glPixelStorei(GL_PACK_ALIGNMENT, 4); /* float is 4 bytes */ + glReadPixels(pixelX, + pixelY, + 1, + 1, + GL_RGBA, + GL_FLOAT, + rgbaOut); + + /* + * Get depth from depth buffer + */ + glReadPixels(pixelX, + pixelY, + 1, + 1, + GL_DEPTH_COMPONENT, + GL_FLOAT, + &depthOut); + + glPopClientAttrib(); + + return true; } + /** * Analyze color information to extract identification data. * @param dataType @@ -5654,9 +5771,19 @@ BrainOpenGLFixedPipeline::getIndexFromColorSelection(SelectionItemDataTypeEnum:: */ glPushClientAttrib(GL_CLIENT_PIXEL_STORE_BIT); - // Figure out item was picked using color in color buffer - // + /* + * Determine item picked by examination of color in back buffer + * + * QOpenGLWidget Note: The QOpenGLWidget always renders in a + * frame buffer object (see its documentation). This is + * probably why calls to glReadBuffer() always cause an + * OpenGL error. + */ +#ifdef WORKBENCH_USE_QT5_QOPENGL_WIDGET + /* do not call glReadBuffer() */ +#else glReadBuffer(GL_BACK); +#endif glPixelStorei(GL_PACK_SKIP_ROWS, 0); glPixelStorei(GL_PACK_SKIP_PIXELS, 0); glPixelStorei(GL_PACK_ALIGNMENT, 1); @@ -5725,9 +5852,19 @@ BrainOpenGLFixedPipeline::getIndexFromColorSelection(SelectionItemDataTypeEnum:: */ glPushClientAttrib(GL_CLIENT_PIXEL_STORE_BIT); - // Figure out item was picked using color in color buffer - // + /* + * Determine item picked by examination of color in back buffer + * + * QOpenGLWidget Note: The QOpenGLWidget always renders in a + * frame buffer object (see its documentation). This is + * probably why calls to glReadBuffer() always cause an + * OpenGL error. + */ +#ifdef WORKBENCH_USE_QT5_QOPENGL_WIDGET + /* do not call glReadBuffer() */ +#else glReadBuffer(GL_BACK); +#endif glPixelStorei(GL_PACK_SKIP_ROWS, 0); glPixelStorei(GL_PACK_SKIP_PIXELS, 0); glPixelStorei(GL_PACK_ALIGNMENT, 1); @@ -5800,9 +5937,19 @@ BrainOpenGLFixedPipeline::getIndexFromColorSelection(SelectionItemDataTypeEnum:: */ glPushClientAttrib(GL_CLIENT_PIXEL_STORE_BIT); - // Figure out item was picked using color in color buffer - // + /* + * Determine item picked by examination of color in back buffer + * + * QOpenGLWidget Note: The QOpenGLWidget always renders in a + * frame buffer object (see its documentation). This is + * probably why calls to glReadBuffer() always cause an + * OpenGL error. + */ +#ifdef WORKBENCH_USE_QT5_QOPENGL_WIDGET + /* do not call glReadBuffer() */ +#else glReadBuffer(GL_BACK); +#endif glPixelStorei(GL_PACK_SKIP_ROWS, 0); glPixelStorei(GL_PACK_SKIP_PIXELS, 0); glPixelStorei(GL_PACK_ALIGNMENT, 1); @@ -5982,106 +6129,6 @@ BrainOpenGLFixedPipeline::drawRoundedCube(const float rgba[4], glPopMatrix(); } -/** - * Draw an outline circle. - * - * @param rgba - * Color for drawing. - * @param diameter - * Diameter of the circle. - */ -void -BrainOpenGLFixedPipeline::drawCircleOutline(const uint8_t rgba[4], - const double diameter) -{ - glPushMatrix(); - glScaled(diameter, diameter, 1.0); - m_shapeCircleOutline->draw(rgba); - glPopMatrix(); -} - -/** - * Draw a filled circle. - * - * @param rgba - * Color for drawing. - * @param diameter - * Diameter of the circle. - */ -void -BrainOpenGLFixedPipeline::drawCircleFilled(const uint8_t rgba[4], - const double diameter) -{ - glPushMatrix(); - glScaled(diameter, diameter, 1.0); - m_shapeCircleFilled->draw(rgba); - glPopMatrix(); -} - -/** - * Draw an outline ellipse. - * - * @param rgba - * Color for drawing. - * @param majorAxis - * Diameter of the major axis. - * @param minorAxis - * Diameter of the minor axis. - * @param lineThickness - * Thickness of the line. - */ -void -BrainOpenGLFixedPipeline::drawEllipseOutline(const uint8_t rgba[4], - const double majorAxis, - const double minorAxis, - const double lineThickness) -{ - glPushMatrix(); - glScaled(majorAxis, minorAxis, 1.0); - - BrainOpenGLShapeRingOutline* ringOutline = NULL; - - std::map::iterator iter = m_shapeEllipseOutlines.find(lineThickness); - if (iter != m_shapeEllipseOutlines.end()) { - ringOutline = iter->second; - } - else { - ringOutline = new BrainOpenGLShapeRingOutline(20, - 1.0, - lineThickness); - m_shapeEllipseOutlines.insert(std::make_pair(lineThickness, - ringOutline)); - } - - CaretAssert(ringOutline); - - ringOutline->draw(rgba); - - glPopMatrix(); -} - -/** - * Draw an outline ellipse. - * - * @param rgba - * Color for drawing. - * @param majorAxis - * Diameter of the major axis. - * @param minorAxis - * Diameter of the minor axis. - */ -void -BrainOpenGLFixedPipeline::drawEllipseFilled(const uint8_t rgba[4], - const double majorAxis, - const double minorAxis) -{ - glPushMatrix(); - glScaled(majorAxis, minorAxis, 1.0); - m_shapeCircleFilled->draw(rgba); - glPopMatrix(); -} - - /** * Draw a cuboid (3D Box) * @@ -6205,7 +6252,7 @@ BrainOpenGLFixedPipeline::drawSquare(const uint8_t rgba[4], * Viewport content which image is displayed. */ void -BrainOpenGLFixedPipeline::drawBackgroundImage(BrainOpenGLViewportContent* vpContent) +BrainOpenGLFixedPipeline::drawBackgroundImage(const BrainOpenGLViewportContent* vpContent) { BrowserTabContent* btc = vpContent->getBrowserTabContent(); if (btc == NULL) { @@ -6254,9 +6301,9 @@ BrainOpenGLFixedPipeline::drawBackgroundImage(BrainOpenGLViewportContent* vpCont /** * Draw the given image in the given viewport. * - * @param viewport - * The viewport dimensions. - * @param image + * @param vpContent + * The viewport content. + * @param imageFile * The QImage that is drawn. * @param windowZ * Z-position for image. @@ -6270,7 +6317,7 @@ BrainOpenGLFixedPipeline::drawBackgroundImage(BrainOpenGLViewportContent* vpCont * Opacity. */ void -BrainOpenGLFixedPipeline::drawImage(BrainOpenGLViewportContent* vpContent, +BrainOpenGLFixedPipeline::drawImage(const BrainOpenGLViewportContent* vpContent, ImageFile* imageFile, const float windowZ, const float frontZ, @@ -6386,7 +6433,7 @@ BrainOpenGLFixedPipeline::drawImage(BrainOpenGLViewportContent* vpContent, for (int32_t i = 0; i < numberOfPixels; i++) { const int32_t i4 = i * 4; CaretAssertVectorIndex(imageBytesRGBA, i4 + 3); - uint8_t pixelAlpha = 255; //imageBytesRGBA[i4 + 3]; + uint8_t pixelAlpha = 255; if (testThresholdFlag) { if ((imageBytesRGBA[i4] < minimumThreshold) @@ -6495,8 +6542,8 @@ BrainOpenGLFixedPipeline::drawImage(BrainOpenGLViewportContent* vpContent, if (isSelectImageControlPoint) { this->colorIdentification->addItem(rgba, SelectionItemDataTypeEnum::IMAGE_CONTROL_POINT, - 0, // file index - icp); // index in file + 0, /* file index */ + icp); /* index in file */ rgba[3] = 255; } drawSphereWithDiameter(rgba, 10); @@ -6660,857 +6707,6 @@ BrainOpenGLFixedPipeline::drawTextAtModelCoords(const float modelXYZ[3], } } -/** - * Draw the palettes showing how scalars are mapped - * to colors. - * @param brain - * Brain containing model being drawn. - * @param viewport - * Viewport for the model. - */ -void -BrainOpenGLFixedPipeline::drawAllPalettes(Brain* brain) -{ - const bool useTheNewestPaletteDrawingFlag = true; - if (useTheNewestPaletteDrawingFlag) { - return; - } - - /* - * Turn off depth testing - */ - glDisable(GL_DEPTH_TEST); - - - /* - * Save the projection matrix, model matrix, and viewport. - */ - glMatrixMode(GL_PROJECTION); - GLfloat savedProjectionMatrix[16]; - glGetFloatv(GL_PROJECTION_MATRIX, - savedProjectionMatrix); - glMatrixMode(GL_MODELVIEW); - GLfloat savedModelviewMatrix[16]; - glGetFloatv(GL_MODELVIEW_MATRIX, - savedModelviewMatrix); - GLint savedViewport[4]; - glGetIntegerv(GL_VIEWPORT, - savedViewport); - - CaretAssert(brain); - - /* - * Check for a 'selection' type mode - */ - bool selectFlag = false; - switch (this->mode) { - case MODE_DRAWING: - break; - case MODE_IDENTIFICATION: - selectFlag = true; - break; - case MODE_PROJECTION: - return; - break; - } - if (selectFlag) { - return; - } - - this->disableLighting(); - - PaletteFile* paletteFile = brain->getPaletteFile(); - CaretAssert(paletteFile); - - std::vector mapFiles; - std::vector mapIndices; - this->browserTabContent->getDisplayedPaletteMapFiles(mapFiles, - mapIndices); - - /* - * Each map file has a palette drawn to represent the - * datas mapping to colors. - */ - const int32_t numMapFiles = static_cast(mapFiles.size()); - for (int32_t i = 0; i < numMapFiles; i++) { - const int mapIndex = mapIndices[i]; - const PaletteColorMapping* pcm = mapFiles[i]->getMapPaletteColorMapping(mapIndex); - if (pcm != NULL) { - const AString paletteName = pcm->getSelectedPaletteName(); - const Palette* palette = paletteFile->getPaletteByName(paletteName); - if (palette != NULL) { - FastStatistics* statistics = NULL; - switch (mapFiles[i]->getPaletteNormalizationMode()) { - case PaletteNormalizationModeEnum::NORMALIZATION_ALL_MAP_DATA: - statistics = const_cast(mapFiles[i]->getFileFastStatistics()); - break; - case PaletteNormalizationModeEnum::NORMALIZATION_SELECTED_MAP_DATA: - statistics = const_cast(mapFiles[i]->getMapFastStatistics(mapIndex)); - break; - } - if (statistics != NULL) { - this->drawPalette(palette, - pcm, - statistics, - i); - } - } - else { - CaretLogWarning("Palette named " - + paletteName - + " not found in palette file."); - } - } - } - - /* - * Restore the projection matrix, model matrix, and viewport. - */ - glMatrixMode(GL_PROJECTION); - glLoadMatrixf(savedProjectionMatrix); - glMatrixMode(GL_MODELVIEW); - glLoadMatrixf(savedModelviewMatrix); - glViewport(savedViewport[0], - savedViewport[1], - savedViewport[2], - savedViewport[3]); -} - -///** -// * Draw a palette. -// * @param palette -// * Palette that is drawn. -// * @param paletteColorMapping -// * Controls mapping of data to colors. -// * @param statistics -// * Statistics describing the data that is mapped to the palette. -// * @param paletteDrawingIndex -// * Counts number of palettes being drawn for the Y-position -// */ -//void -//BrainOpenGLFixedPipeline::drawPalette(const Palette* palette, -// const PaletteColorMapping* paletteColorMapping, -// const FastStatistics* statistics, -// const int paletteDrawingIndex) -//{ -// /* -// * Save viewport. -// */ -// GLint modelViewport[4]; -// glGetIntegerv(GL_VIEWPORT, modelViewport); -// -// /* -// * Create a viewport for drawing the palettes in the -// * lower left corner of the window. Try to use -// * 25% of the display's width. -// */ -// const GLint colorbarViewportWidth = std::max(static_cast(modelViewport[2] * 0.25), -// (GLint)120); -// const GLint colorbarViewportHeight = 35; -// const GLint colorbarViewportX = modelViewport[0] + 10; -// const GLint colorbarVerticalSpacing = 10; -// GLint colorbarViewportY = (modelViewport[1] -// + colorbarVerticalSpacing -// + (paletteDrawingIndex * colorbarViewportHeight)); -// -// glViewport(colorbarViewportX, -// colorbarViewportY, -// colorbarViewportWidth, -// colorbarViewportHeight); -// -// CaretLogFine("Palette " + palette->getName() + " Viewport: (" -// + AString::number(colorbarViewportX) + ", " -// + AString::number(colorbarViewportY) + ", " -// + AString::number(colorbarViewportWidth) + ", " -// + AString::number(colorbarViewportHeight) -// + ")\n Model Viewport: " -// + AString::fromNumbers(modelViewport, 4, ", ")); -// /* -// * Types of values for display -// */ -// const bool isPositiveDisplayed = paletteColorMapping->isDisplayPositiveDataFlag(); -// const bool isNegativeDisplayed = paletteColorMapping->isDisplayNegativeDataFlag(); -// const bool isZeroDisplayed = paletteColorMapping->isDisplayZeroDataFlag(); -// const bool isPositiveOnly = (isPositiveDisplayed && (isNegativeDisplayed == false)); -// const bool isNegativeOnly = ((isPositiveDisplayed == false) && isNegativeDisplayed); -// -// /* -// * Create an orthographic projection that ranges in X: -// * (-1, 1) If negative and positive displayed -// * (-1, 0) If positive is NOT displayed -// * (0, 1) If negative is NOT displayed -// */ -// const GLdouble halfHeight = static_cast(colorbarViewportHeight / 2); -// const GLdouble orthoHeight = halfHeight; -// GLdouble orthoLeft = -1.0; -// GLdouble orthoRight = 1.0; -// if (isPositiveOnly) { -// orthoLeft = 0.0; -// } -// else if (isNegativeOnly) { -// orthoRight = 0.0; -// } -// glMatrixMode(GL_PROJECTION); -// glLoadIdentity(); -// glOrtho(orthoLeft, orthoRight, -// -orthoHeight, orthoHeight, -// -1.0, 1.0); -// -// glMatrixMode (GL_MODELVIEW); -// glLoadIdentity(); -// -// /* -// * A little extra so viewport gets filled -// */ -// const GLdouble orthoLeftWithExtra = orthoLeft - 0.10; -// const GLdouble orthoRightWithExtra = orthoRight + 0.10; -// -// /* -// * Fill the palette viewport with the background color -// * Add a little to left and right so viewport is filled (excess will get clipped) -// */ -// glColor3fv(m_backgroundColorFloat); -// glRectf(orthoLeftWithExtra, -orthoHeight, orthoRightWithExtra, orthoHeight); -// -// /* -// * Always interpolate if the palette has only two colors -// */ -// bool interpolateColor = paletteColorMapping->isInterpolatePaletteFlag(); -// if (palette->getNumberOfScalarsAndColors() <= 2) { -// interpolateColor = true; -// } -// -// /* -// * Draw the colorbar starting with the color assigned -// * to the negative end of the palette. -// * Colorbar scalars range from -1 to 1. -// */ -// const int iStart = palette->getNumberOfScalarsAndColors() - 1; -// const int iEnd = 1; -// const int iStep = -1; -// for (int i = iStart; i >= iEnd; i += iStep) { -// /* -// * palette data for 'left' side of a color in the palette. -// */ -// const PaletteScalarAndColor* sc = palette->getScalarAndColor(i); -// float scalar = sc->getScalar(); -// float rgba[4]; -// sc->getColor(rgba); -// -// /* -// * palette data for 'right' side of a color in the palette. -// */ -// const PaletteScalarAndColor* nextSC = palette->getScalarAndColor(i - 1); -// float nextScalar = nextSC->getScalar(); -// float nextRGBA[4]; -// nextSC->getColor(nextRGBA); -// const bool isNoneColorFlag = nextSC->isNoneColor(); -// -// /* -// * Exclude negative regions if not displayed. -// */ -// if (isNegativeDisplayed == false) { -// if (nextScalar < 0.0) { -// continue; -// } -// else if (scalar < 0.0) { -// scalar = 0.0; -// } -// } -// -// /* -// * Exclude positive regions if not displayed. -// */ -// if (isPositiveDisplayed == false) { -// if (scalar > 0.0) { -// continue; -// } -// else if (nextScalar > 0.0) { -// nextScalar = 0.0; -// } -// } -// -// /* -// * Normally, the first entry has a scalar value of -1. -// * If it does not, use the first color draw from -// * -1 to the first scalar value. -// */ -// if (i == iStart) { -// if (sc->isNoneColor() == false) { -// if (scalar > -1.0) { -// const float xStart = -1.0; -// const float xEnd = scalar; -// glColor3fv(rgba); -// glBegin(GL_POLYGON); -// glVertex3f(xStart, 0.0, 0.0); -// glVertex3f(xStart, -halfHeight, 0.0); -// glVertex3f(xEnd, -halfHeight, 0.0); -// glVertex3f(xEnd, 0.0, 0.0); -// glEnd(); -// } -// } -// } -// -// /* -// * If the 'next' color is none, drawing -// * is skipped to let the background show -// * throw the 'none' region of the palette. -// */ -// if (isNoneColorFlag == false) { -// /* -// * left and right region of an entry in the palette -// */ -// const float xStart = scalar; -// const float xEnd = nextScalar; -// -// /* -// * Unless interpolating, use the 'next' color. -// */ -// float* startRGBA = nextRGBA; -// float* endRGBA = nextRGBA; -// if (interpolateColor) { -// startRGBA = rgba; -// } -// -// /* -// * Draw the region in the palette. -// */ -// glBegin(GL_POLYGON); -// glColor3fv(startRGBA); -// glVertex3f(xStart, 0.0, 0.0); -// glVertex3f(xStart, -halfHeight, 0.0); -// glColor3fv(endRGBA); -// glVertex3f(xEnd, -halfHeight, 0.0); -// glVertex3f(xEnd, 0.0, 0.0); -// glEnd(); -// -// /* -// * The last scalar value is normally 1.0. If the last -// * scalar is less than 1.0, then fill in the rest of -// * the palette from the last scalar to 1.0. -// */ -// if (i == iEnd) { -// if (nextScalar < 1.0) { -// const float xStart = nextScalar; -// const float xEnd = 1.0; -// glColor3fv(nextRGBA); -// glBegin(GL_POLYGON); -// glVertex3f(xStart, 0.0, 0.0); -// glVertex3f(xStart, -halfHeight, 0.0); -// glVertex3f(xEnd, -halfHeight, 0.0); -// glVertex3f(xEnd, 0.0, 0.0); -// glEnd(); -// } -// } -// } -// } -// -// /* -// * Draw over thresholded regions with background color -// */ -// const PaletteThresholdTypeEnum::Enum thresholdType = paletteColorMapping->getThresholdType(); -// if (thresholdType != PaletteThresholdTypeEnum::THRESHOLD_TYPE_OFF) { -// const float minMaxThresholds[2] = { -// paletteColorMapping->getThresholdMinimum(thresholdType), -// paletteColorMapping->getThresholdMaximum(thresholdType) -// }; -// float normalizedThresholds[2]; -// -// paletteColorMapping->mapDataToPaletteNormalizedValues(statistics, -// minMaxThresholds, -// normalizedThresholds, -// 2); -// -// switch (paletteColorMapping->getThresholdTest()) { -// case PaletteThresholdTestEnum::THRESHOLD_TEST_SHOW_INSIDE: -// glColor3fv(m_backgroundColorFloat); -// glRectf(orthoLeftWithExtra, -orthoHeight, normalizedThresholds[0], orthoHeight); -// glRectf(normalizedThresholds[1], -orthoHeight, orthoRightWithExtra, orthoHeight); -// break; -// case PaletteThresholdTestEnum::THRESHOLD_TEST_SHOW_OUTSIDE: -// glColor3fv(m_backgroundColorFloat); -// glRectf(normalizedThresholds[0], -orthoHeight, normalizedThresholds[1], orthoHeight); -// break; -// } -// } -// -// /* -// * If zeros are not displayed, draw a line in the -// * background color at zero in the palette. -// */ -// if (isZeroDisplayed == false) { -// this->setLineWidth(1.0); -// glColor3fv(m_backgroundColorFloat); -// glBegin(GL_LINES); -// glVertex2f(0.0, -halfHeight); -// glVertex2f(0.0, 0.0); -// glEnd(); -// } -// -// AString textLeft; -// AString textCenter; -// AString textRight; -// const bool useNewFormattingFlag = true; -// if (useNewFormattingFlag) { -// /* -// * NEW FORMATTING !!!!! -// */ -// paletteColorMapping->getPaletteColorBarScaleText(statistics, -// textLeft, -// textCenter, -// textRight); -// -// std::vector > normalizedPositionAndText; -// paletteColorMapping->getPaletteColorBarScaleText(statistics, -// normalizedPositionAndText); -// } -// else { -// float minMax[4] = { -1.0, 0.0, 0.0, 1.0 }; -// switch (paletteColorMapping->getScaleMode()) { -// case PaletteScaleModeEnum::MODE_AUTO_SCALE: -// { -// float dummy; -// statistics->getNonzeroRanges(minMax[0], dummy, dummy, minMax[3]); -// } -// break; -// case PaletteScaleModeEnum::MODE_AUTO_SCALE_ABSOLUTE_PERCENTAGE: -// { -// const float maxPct = paletteColorMapping->getAutoScaleAbsolutePercentageMaximum(); -// const float minPct = paletteColorMapping->getAutoScaleAbsolutePercentageMinimum(); -// -// minMax[0] = -statistics->getApproxAbsolutePercentile(maxPct); -// minMax[1] = -statistics->getApproxAbsolutePercentile(minPct); -// minMax[2] = statistics->getApproxAbsolutePercentile(minPct); -// minMax[3] = statistics->getApproxAbsolutePercentile(maxPct); -// } -// break; -// case PaletteScaleModeEnum::MODE_AUTO_SCALE_PERCENTAGE: -// { -// const float negMaxPct = paletteColorMapping->getAutoScalePercentageNegativeMaximum(); -// const float negMinPct = paletteColorMapping->getAutoScalePercentageNegativeMinimum(); -// const float posMinPct = paletteColorMapping->getAutoScalePercentagePositiveMinimum(); -// const float posMaxPct = paletteColorMapping->getAutoScalePercentagePositiveMaximum(); -// -// minMax[0] = statistics->getApproxNegativePercentile(negMaxPct); -// minMax[1] = statistics->getApproxNegativePercentile(negMinPct); -// minMax[2] = statistics->getApproxPositivePercentile(posMinPct); -// minMax[3] = statistics->getApproxPositivePercentile(posMaxPct); -// } -// break; -// case PaletteScaleModeEnum::MODE_USER_SCALE: -// minMax[0] = paletteColorMapping->getUserScaleNegativeMaximum(); -// minMax[1] = paletteColorMapping->getUserScaleNegativeMinimum(); -// minMax[2] = paletteColorMapping->getUserScalePositiveMinimum(); -// minMax[3] = paletteColorMapping->getUserScalePositiveMaximum(); -// break; -// } -// textLeft = AString::number(minMax[0], 'f', 1); -// AString textCenterNeg = AString::number(minMax[1], 'f', 1); -// if (textCenterNeg == "-0.0") { -// textCenterNeg = "0.0"; -// } -// AString textCenterPos = AString::number(minMax[2], 'f', 1); -// textCenter = textCenterPos; -// if (isNegativeDisplayed && isPositiveDisplayed) { -// if (textCenterNeg != textCenterPos) { -// textCenter = textCenterNeg + "/" + textCenterPos; -// } -// } -// else if (isNegativeDisplayed) { -// textCenter = textCenterNeg; -// } -// else if (isPositiveDisplayed) { -// textCenter = textCenterPos; -// } -// textRight = AString::number(minMax[3], 'f', 1); -// } -// -// /* -// * Reset to the models viewport for drawing text. -// */ -// glViewport(modelViewport[0], -// modelViewport[1], -// modelViewport[2], -// modelViewport[3]); -// -// /* -// * Account for margin around colorbar when calculating text locations -// */ -// int textCenterX = colorbarViewportX - modelViewport[0] + (colorbarViewportWidth / 2); -// const int textLeftX = colorbarViewportX - modelViewport[0]; -// const int textRightX = (colorbarViewportX - modelViewport[0] + colorbarViewportWidth); -// if (isPositiveOnly) { -// textCenterX = textLeftX; -// } -// else if (isNegativeOnly) { -// textCenterX = textRightX; -// } -// -// const int textY = 2 + colorbarViewportY - modelViewport[1] + (colorbarViewportHeight / 2); -// if (isNegativeDisplayed) { -// AnnotationPointSizeText annotationText(AnnotationAttributesDefaultTypeEnum::NORMAL); -// annotationText.setHorizontalAlignment(AnnotationTextAlignHorizontalEnum::LEFT); -// annotationText.setVerticalAlignment(AnnotationTextAlignVerticalEnum::BOTTOM); -// annotationText.setFontPointSize(AnnotationTextFontPointSizeEnum::SIZE12); -// annotationText.setTextColor(CaretColorEnum::CUSTOM); -// annotationText.setCustomLineColor(m_foregroundColorFloat); -// annotationText.setText(textLeft); -// this->drawTextAtViewportCoords(textLeftX, -// textY, -// annotationText); -// } -// -// if (isNegativeDisplayed -// || isZeroDisplayed -// || isPositiveDisplayed) { -// -// AnnotationPointSizeText annotationText(AnnotationAttributesDefaultTypeEnum::NORMAL); -// annotationText.setHorizontalAlignment(AnnotationTextAlignHorizontalEnum::CENTER); -// if (isNegativeOnly) { -// annotationText.setHorizontalAlignment(AnnotationTextAlignHorizontalEnum::RIGHT); -// } -// else if (isPositiveOnly) { -// annotationText.setHorizontalAlignment(AnnotationTextAlignHorizontalEnum::LEFT); -// } -// annotationText.setVerticalAlignment(AnnotationTextAlignVerticalEnum::BOTTOM); -// annotationText.setFontPointSize(AnnotationTextFontPointSizeEnum::SIZE12); -// annotationText.setTextColor(CaretColorEnum::CUSTOM); -// annotationText.setCustomLineColor(m_foregroundColorFloat); -// annotationText.setText(textCenter); -// this->drawTextAtViewportCoords(textCenterX, -// textY, -// annotationText); -// } -// -// if (isPositiveDisplayed) { -// AnnotationPointSizeText annotationText(AnnotationAttributesDefaultTypeEnum::NORMAL); -// annotationText.setHorizontalAlignment(AnnotationTextAlignHorizontalEnum::RIGHT); -// annotationText.setVerticalAlignment(AnnotationTextAlignVerticalEnum::BOTTOM); -// annotationText.setFontPointSize(AnnotationTextFontPointSizeEnum::SIZE12); -// annotationText.setTextColor(CaretColorEnum::CUSTOM); -// annotationText.setCustomLineColor(m_foregroundColorFloat); -// annotationText.setText(textRight); -// this->drawTextAtViewportCoords(textRightX, -// textY, -// annotationText); -// } -// -// return; -//} - -/** - * Draw a palette. - * @param palette - * Palette that is drawn. - * @param paletteColorMapping - * Controls mapping of data to colors. - * @param statistics - * Statistics describing the data that is mapped to the palette. - * @param paletteDrawingIndex - * Counts number of palettes being drawn for the Y-position - */ -void -BrainOpenGLFixedPipeline::drawPalette(const Palette* palette, - const PaletteColorMapping* paletteColorMapping, - const FastStatistics* statistics, - const int paletteDrawingIndex) -{ - /* - * Save viewport. - */ - GLint modelViewport[4]; - glGetIntegerv(GL_VIEWPORT, modelViewport); - - /* - * Create a viewport for drawing the palettes in the - * lower left corner of the window. Try to use - * 25% of the display's width. - */ - const GLint colorbarViewportWidth = std::max(static_cast(modelViewport[2] * 0.25), - (GLint)120); - const GLint colorbarViewportHeight = 35; - const GLint colorbarViewportX = modelViewport[0] + 10; - const GLint colorbarVerticalSpacing = 10; - GLint colorbarViewportY = (modelViewport[1] - + colorbarVerticalSpacing - + (paletteDrawingIndex * colorbarViewportHeight)); - - glViewport(colorbarViewportX, - colorbarViewportY, - colorbarViewportWidth, - colorbarViewportHeight); - - CaretLogFine("Palette " + palette->getName() + " Viewport: (" - + AString::number(colorbarViewportX) + ", " - + AString::number(colorbarViewportY) + ", " - + AString::number(colorbarViewportWidth) + ", " - + AString::number(colorbarViewportHeight) - + ")\n Model Viewport: " - + AString::fromNumbers(modelViewport, 4, ", ")); - /* - * Types of values for display - */ - const bool isPositiveDisplayed = paletteColorMapping->isDisplayPositiveDataFlag(); - const bool isNegativeDisplayed = paletteColorMapping->isDisplayNegativeDataFlag(); - const bool isZeroDisplayed = paletteColorMapping->isDisplayZeroDataFlag(); - const bool isPositiveOnly = (isPositiveDisplayed && (isNegativeDisplayed == false)); - const bool isNegativeOnly = ((isPositiveDisplayed == false) && isNegativeDisplayed); - - /* - * Create an orthographic projection that ranges in X: - * (-1, 1) If negative and positive displayed - * (-1, 0) If positive is NOT displayed - * (0, 1) If negative is NOT displayed - */ - const GLdouble halfHeight = static_cast(colorbarViewportHeight / 2); - const GLdouble orthoHeight = halfHeight; - GLdouble orthoLeft = -1.0; - GLdouble orthoRight = 1.0; - if (isPositiveOnly) { - orthoLeft = 0.0; - } - else if (isNegativeOnly) { - orthoRight = 0.0; - } - glMatrixMode(GL_PROJECTION); - glLoadIdentity(); - glOrtho(orthoLeft, orthoRight, - -orthoHeight, orthoHeight, - -1.0, 1.0); - - glMatrixMode (GL_MODELVIEW); - glLoadIdentity(); - - /* - * A little extra so viewport gets filled - */ - const GLdouble orthoLeftWithExtra = orthoLeft - 0.10; - const GLdouble orthoRightWithExtra = orthoRight + 0.10; - - /* - * Fill the palette viewport with the background color - * Add a little to left and right so viewport is filled (excess will get clipped) - */ - glColor3fv(m_backgroundColorFloat); - glRectf(orthoLeftWithExtra, -orthoHeight, orthoRightWithExtra, orthoHeight); - - /* - * Always interpolate if the palette has only two colors - */ - bool interpolateColor = paletteColorMapping->isInterpolatePaletteFlag(); - if (palette->getNumberOfScalarsAndColors() <= 2) { - interpolateColor = true; - } - - /* - * Draw the colorbar starting with the color assigned - * to the negative end of the palette. - * Colorbar scalars range from -1 to 1. - */ - const int iStart = palette->getNumberOfScalarsAndColors() - 1; - const int iEnd = 1; - const int iStep = -1; - for (int i = iStart; i >= iEnd; i += iStep) { - /* - * palette data for 'left' side of a color in the palette. - */ - const PaletteScalarAndColor* sc = palette->getScalarAndColor(i); - float scalar = sc->getScalar(); - float rgba[4]; - sc->getColor(rgba); - - /* - * palette data for 'right' side of a color in the palette. - */ - const PaletteScalarAndColor* nextSC = palette->getScalarAndColor(i - 1); - float nextScalar = nextSC->getScalar(); - float nextRGBA[4]; - nextSC->getColor(nextRGBA); - const bool isNoneColorFlag = nextSC->isNoneColor(); - - /* - * Exclude negative regions if not displayed. - */ - if (isNegativeDisplayed == false) { - if (nextScalar < 0.0) { - continue; - } - else if (scalar < 0.0) { - scalar = 0.0; - } - } - - /* - * Exclude positive regions if not displayed. - */ - if (isPositiveDisplayed == false) { - if (scalar > 0.0) { - continue; - } - else if (nextScalar > 0.0) { - nextScalar = 0.0; - } - } - - /* - * Normally, the first entry has a scalar value of -1. - * If it does not, use the first color draw from - * -1 to the first scalar value. - */ - if (i == iStart) { - if (sc->isNoneColor() == false) { - if (scalar > -1.0) { - const float xStart = -1.0; - const float xEnd = scalar; - glColor3fv(rgba); - glBegin(GL_POLYGON); - glVertex3f(xStart, 0.0, 0.0); - glVertex3f(xStart, -halfHeight, 0.0); - glVertex3f(xEnd, -halfHeight, 0.0); - glVertex3f(xEnd, 0.0, 0.0); - glEnd(); - } - } - } - - /* - * If the 'next' color is none, drawing - * is skipped to let the background show - * throw the 'none' region of the palette. - */ - if (isNoneColorFlag == false) { - /* - * left and right region of an entry in the palette - */ - const float xStart = scalar; - const float xEnd = nextScalar; - - /* - * Unless interpolating, use the 'next' color. - */ - float* startRGBA = nextRGBA; - float* endRGBA = nextRGBA; - if (interpolateColor) { - startRGBA = rgba; - } - - /* - * Draw the region in the palette. - */ - glBegin(GL_POLYGON); - glColor3fv(startRGBA); - glVertex3f(xStart, 0.0, 0.0); - glVertex3f(xStart, -halfHeight, 0.0); - glColor3fv(endRGBA); - glVertex3f(xEnd, -halfHeight, 0.0); - glVertex3f(xEnd, 0.0, 0.0); - glEnd(); - - /* - * The last scalar value is normally 1.0. If the last - * scalar is less than 1.0, then fill in the rest of - * the palette from the last scalar to 1.0. - */ - if (i == iEnd) { - if (nextScalar < 1.0) { - const float xStart = nextScalar; - const float xEnd = 1.0; - glColor3fv(nextRGBA); - glBegin(GL_POLYGON); - glVertex3f(xStart, 0.0, 0.0); - glVertex3f(xStart, -halfHeight, 0.0); - glVertex3f(xEnd, -halfHeight, 0.0); - glVertex3f(xEnd, 0.0, 0.0); - glEnd(); - } - } - } - } - - /* - * Draw over thresholded regions with background color - */ - const PaletteThresholdTypeEnum::Enum thresholdType = paletteColorMapping->getThresholdType(); - if (thresholdType != PaletteThresholdTypeEnum::THRESHOLD_TYPE_OFF) { - const float minMaxThresholds[2] = { - paletteColorMapping->getThresholdMinimum(thresholdType), - paletteColorMapping->getThresholdMaximum(thresholdType) - }; - float normalizedThresholds[2]; - - paletteColorMapping->mapDataToPaletteNormalizedValues(statistics, - minMaxThresholds, - normalizedThresholds, - 2); - - switch (paletteColorMapping->getThresholdTest()) { - case PaletteThresholdTestEnum::THRESHOLD_TEST_SHOW_INSIDE: - glColor3fv(m_backgroundColorFloat); - glRectf(orthoLeftWithExtra, -orthoHeight, normalizedThresholds[0], orthoHeight); - glRectf(normalizedThresholds[1], -orthoHeight, orthoRightWithExtra, orthoHeight); - break; - case PaletteThresholdTestEnum::THRESHOLD_TEST_SHOW_OUTSIDE: - glColor3fv(m_backgroundColorFloat); - glRectf(normalizedThresholds[0], -orthoHeight, normalizedThresholds[1], orthoHeight); - break; - } - } - - /* - * If zeros are not displayed, draw a line in the - * background color at zero in the palette. - */ - if (isZeroDisplayed == false) { - this->setLineWidth(1.0); - glColor3fv(m_backgroundColorFloat); - glBegin(GL_LINES); - glVertex2f(0.0, -halfHeight); - glVertex2f(0.0, 0.0); - glEnd(); - } - - /* - * Reset to the models viewport for drawing text. - */ - glViewport(modelViewport[0], - modelViewport[1], - modelViewport[2], - modelViewport[3]); - - /* - * Get the numeric text values for display above the color bar - */ - std::vector > normalizedPositionAndText; - paletteColorMapping->getPaletteColorBarScaleText(statistics, - normalizedPositionAndText); - - const int textViewportY = 2 + colorbarViewportY - modelViewport[1] + (colorbarViewportHeight / 2); - const int32_t numericTextCount = static_cast(normalizedPositionAndText.size()); - for (int32_t iText = 0; iText < numericTextCount; iText++) { - CaretAssertVectorIndex(normalizedPositionAndText, iText); - - const float textNormalizedX = normalizedPositionAndText[iText].first; - const float textViewportX = ((colorbarViewportX - modelViewport[0]) - + static_cast(textNormalizedX * colorbarViewportWidth)); - - AnnotationPointSizeText annotationText(AnnotationAttributesDefaultTypeEnum::NORMAL); - annotationText.setHorizontalAlignment(AnnotationTextAlignHorizontalEnum::CENTER); - if (iText == 0) { - annotationText.setHorizontalAlignment(AnnotationTextAlignHorizontalEnum::LEFT); - } - else if (iText == (numericTextCount - 1)) { - annotationText.setHorizontalAlignment(AnnotationTextAlignHorizontalEnum::RIGHT); - } - annotationText.setVerticalAlignment(AnnotationTextAlignVerticalEnum::BOTTOM); - annotationText.setFontPointSize(AnnotationTextFontPointSizeEnum::SIZE14); - annotationText.setTextColor(CaretColorEnum::CUSTOM); - annotationText.setCustomTextColor(m_foregroundColorFloat); - annotationText.setText(normalizedPositionAndText[iText].second); - this->drawTextAtViewportCoords(textViewportX, - textViewportY, - annotationText); - } -} /** * @return A string containing the state of OpenGL (depth testing, lighting, etc.) @@ -7531,6 +6727,7 @@ BrainOpenGLFixedPipeline::getStateOfOpenGL() const s.appendWithNewLine(" " + getOpenGLEnabledEnumAsText("GL_COLOR_MATERIAL", GL_COLOR_MATERIAL)); s.appendWithNewLine(" " + getOpenGLEnabledEnumAsText("GL_CULL_FACE", GL_CULL_FACE)); s.appendWithNewLine(" " + getOpenGLEnabledEnumAsText("GL_DEPTH_TEST", GL_DEPTH_TEST)); + s.appendWithNewLine(" " + getOpenGLBooleanAsText("GL_DOUBLEBUFFER", GL_DOUBLEBUFFER)); s.appendWithNewLine(" " + getOpenGLBooleanAsText("GL_LIGHT_MODEL_LOCAL_VIEWER", GL_LIGHT_MODEL_LOCAL_VIEWER)); s.appendWithNewLine(" " + getOpenGLBooleanAsText("GL_LIGHT_MODEL_TWO_SIDE", GL_LIGHT_MODEL_TWO_SIDE)); s.appendWithNewLine(" " + getOpenGLEnabledEnumAsText("GL_LIGHTING", GL_LIGHTING)); @@ -7590,7 +6787,7 @@ BrainOpenGLFixedPipeline::getStateOfOpenGL() const -//============================================================================ +/* ============================================================================ */ /** * Constructor. */ diff --git a/src/Brain/BrainOpenGLFixedPipeline.h b/src/Brain/BrainOpenGLFixedPipeline.h index f27ea2f080420c2b2d7b71b82af01df0911c86f7..32ed9dca7660f262df7228868c554ba67fd6f472 100644 --- a/src/Brain/BrainOpenGLFixedPipeline.h +++ b/src/Brain/BrainOpenGLFixedPipeline.h @@ -40,12 +40,9 @@ #include "VolumeSliceViewPlaneEnum.h" #include "WholeBrainVoxelDrawingMode.h" - -class QGLWidget; -class QImage; - namespace caret { + class Annotation; class AnnotationText; class BoundingBox; class Brain; @@ -54,9 +51,7 @@ namespace caret { class BrainOpenGLShapeCube; class BrainOpenGLShapeCylinder; class BrainOpenGLShapeRing; - class BrainOpenGLShapeRingOutline; class BrainOpenGLShapeSphere; - class BrainOpenGLTextureManager; class BrainOpenGLViewportContent; class BrowserTabContent; class CaretMappableDataFile; @@ -72,13 +67,13 @@ namespace caret { class Surface; class Model; class ModelChart; + class ModelChartTwo; class ModelSurface; class ModelSurfaceMontage; class ModelVolume; class ModelWholeBrain; class Palette; class PaletteColorMapping; - class PaletteFile; class SurfaceFile; class SurfaceMontageConfigurationCerebellar; class SurfaceMontageConfigurationCerebral; @@ -102,25 +97,10 @@ namespace caret { BrainOpenGLFixedPipeline& operator=(const BrainOpenGLFixedPipeline&); public: - BrainOpenGLFixedPipeline(const int32_t windowIndex, - BrainOpenGLTextRenderInterface* textRenderer); + BrainOpenGLFixedPipeline(BrainOpenGLTextRenderInterface* textRenderer); ~BrainOpenGLFixedPipeline(); - void drawModels(Brain* brain, - std::vector& viewportContents); - - void selectModel(Brain* brain, - BrainOpenGLViewportContent* viewportContent, - const int32_t mouseX, - const int32_t mouseY, - const bool applySelectionBackgroundFiltering); - - void projectToModel(Brain* brain, - BrainOpenGLViewportContent* viewportContent, - const int32_t mouseX, - const int32_t mouseY, - SurfaceProjectedItem& projectionOut); void initializeOpenGL(); @@ -133,7 +113,27 @@ namespace caret { int32_t& subViewportSizeOut, int32_t& gapOut); - virtual BrainOpenGLTextureManager* getTextureManager(); + protected: + void drawModelsImplementation(const int32_t windowIndex, + Brain* brain, + const std::vector& viewportContents) override; + + void selectModelImplementation(const int32_t windowIndex, + Brain* brain, + const BrainOpenGLViewportContent* viewportContent, + const int32_t mouseX, + const int32_t mouseY, + const bool applySelectionBackgroundFiltering) override; + + void projectToModelImplementation(const int32_t windowIndex, + Brain* brain, + const BrainOpenGLViewportContent* viewportContent, + const int32_t mouseX, + const int32_t mouseY, + SurfaceProjectedItem& projectionOut) override; + + protected: + virtual void loadObjectToWindowTransform(EventOpenGLObjectToWindowTransform* transformEvent) override; private: class VolumeDrawInfo { @@ -180,13 +180,17 @@ namespace caret { FiberOrientationDisplayInfo& dispInfo); void drawModelInternal(Mode mode, - BrainOpenGLViewportContent* viewportContent); + const BrainOpenGLViewportContent* viewportContent); void initializeMembersBrainOpenGL(); - void drawChartData(BrowserTabContent* browserTabContent, - ModelChart* chartData, - const int32_t viewport[4]); + void drawChartOneData(BrowserTabContent* browserTabContent, + ModelChart* chartData, + const int32_t viewport[4]); + + void drawChartTwoData(const BrainOpenGLViewportContent* viewportContent, + ModelChartTwo* chartData, + const int32_t viewport[4]); void drawSurfaceModel(ModelSurface* surfaceModel, const int32_t viewport[4]); @@ -213,6 +217,7 @@ namespace caret { struct BorderDrawInfo { Surface* anatomicalSurface; Surface* surface; + TopologyHelper* topologyHelper; Border* border; int32_t borderFileIndex; int32_t borderIndex; @@ -334,6 +339,11 @@ namespace caret { void disableLineAntiAliasing(); + bool getPixelDepthAndRGBA(const int32_t pixelX, + const int32_t pixelY, + float& depthOut, + float rgbaOut[4]); + void getIndexFromColorSelection(const SelectionItemDataTypeEnum::Enum dataType, const int32_t x, const int32_t y, @@ -379,21 +389,6 @@ namespace caret { void drawSurfaceAxes(); - void drawCircleOutline(const uint8_t rgba[4], - const double diameter); - - void drawCircleFilled(const uint8_t rgba[4], - const double diameter); - - void drawEllipseOutline(const uint8_t rgba[4], - const double majorAxis, - const double minorAxis, - const double lineThickness); - - void drawEllipseFilled(const uint8_t rgba[4], - const double majorAxis, - const double minorAxis); - void drawSphereWithDiameter(const uint8_t rgba[4], const double diameter); @@ -453,20 +448,13 @@ namespace caret { void drawWindowAnnotations(const int windowViewport[4]); -// void drawTabAnnotations(BrainOpenGLViewportContent* tabContent, -// const int32_t tabViewport[4]); - void drawTabAnnotations(BrainOpenGLViewportContent* tabContent); - - void drawAllPalettes(Brain* brain); + void drawTabAnnotations(const BrainOpenGLViewportContent* tabContent); - void drawPalette(const Palette* palette, - const PaletteColorMapping* paletteColorMapping, - const FastStatistics* statistics, - const int paletteDrawingIndex); + void drawChartCoordinateSpaceAnnotations(const BrainOpenGLViewportContent* viewportContent); - void drawBackgroundImage(BrainOpenGLViewportContent* vpContent); + void drawBackgroundImage(const BrainOpenGLViewportContent* vpContent); - void drawImage(BrainOpenGLViewportContent* vpContent, + void drawImage(const BrainOpenGLViewportContent* vpContent, ImageFile* imageFile, const float windowZ, const float frontZ, @@ -482,10 +470,6 @@ namespace caret { const int barycentricNodes[3], const int numberOfNodes); - void setLineWidth(const float lineWidth); - - void setPointSize(const float pointSize); - enum ClippingDataType { CLIPPING_DATA_TYPE_FEATURES, CLIPPING_DATA_TYPE_SURFACE, @@ -512,14 +496,18 @@ namespace caret { const VolumeSliceViewPlaneEnum::Enum slicePlane, const int64_t sliceIndex) const; - void updateForegroundAndBackgroundColors(BrainOpenGLViewportContent* vpContent); + void updateForegroundAndBackgroundColors(const BrainOpenGLViewportContent* vpContent); void setTabViewport(const BrainOpenGLViewportContent* vpContent); - void setAnnotationColorBarsForDrawing(std::vector& viewportContents); + void setAnnotationColorBarsForDrawing(const std::vector& viewportContents); + + void drawTabHighlighting(const float width, + const float height, + const float rgb[3]); /** Index of window */ - const int32_t m_windowIndex; + int32_t m_windowIndex = -1; /** Indicates OpenGL has been initialized */ bool initializedOpenGLFlag; @@ -586,15 +574,6 @@ namespace caret { /** Cube symbol */ BrainOpenGLShapeCube* m_shapeCube; - /** Outline circle symbol */ - BrainOpenGLShapeRing* m_shapeCircleOutline; - - /** Outline ellipse symbol. KEY is line thickness */ - std::map m_shapeEllipseOutlines; - - /** Filled circle symbol */ - BrainOpenGLShapeRing* m_shapeCircleFilled; - /** Rounded Cube symbol */ BrainOpenGLShapeCube* m_shapeCubeRounded; @@ -617,9 +596,9 @@ namespace caret { std::vector m_annotationColorBarsForDrawing; - /** The texture manager. */ - CaretPointer m_textureManager; - + /** Some graphics using annotations for some elements so user can select and edit them */ + std::vector m_specialCaseGraphicsAnnotations; + static bool s_staticInitialized; static const float s_gluLookAtCenterFromEyeOffsetDistance; @@ -630,6 +609,7 @@ namespace caret { friend class BrainOpenGLAnnotationDrawingFixedPipeline; friend class BrainOpenGLChartDrawingFixedPipeline; + friend class BrainOpenGLChartTwoDrawingFixedPipeline; friend class BrainOpenGLVolumeObliqueSliceDrawing; friend class BrainOpenGLVolumeSliceDrawing; friend class OldBrainOpenGLVolumeSliceDrawing; diff --git a/src/Brain/BrainOpenGLPrimitiveDrawing.cxx b/src/Brain/BrainOpenGLPrimitiveDrawing.cxx index 375a041b0b49eb68efc1b69b7ba5561ee6d4bca7..bc1bc193620f5a51f4e25703b4432e88f102ba48 100644 --- a/src/Brain/BrainOpenGLPrimitiveDrawing.cxx +++ b/src/Brain/BrainOpenGLPrimitiveDrawing.cxx @@ -170,12 +170,6 @@ BrainOpenGLPrimitiveDrawing::drawQuads(const std::vector& coordinates, rgbaColors, coordinateOffset, coordinateToDrawCount); - -// drawQuadsImmediateMode(coordinates, -// normals, -// rgbaColors, -// coordinateOffset, -// coordinateToDrawCount); } coordinateOffset += maximumCoordinatesToDraw; } @@ -185,84 +179,6 @@ BrainOpenGLPrimitiveDrawing::drawQuads(const std::vector& coordinates, } } -/** - * Draw quads using immediate mode. - * - * @param coordinates - * Coordinates of the quads. - * @param normals - * Normal vectors for the quads. - * @param rgbaColors - * RGBA colors for the quads. - */ -void -BrainOpenGLPrimitiveDrawing::drawQuadsImmediateMode(const std::vector& coordinates, - const std::vector& normals, - const std::vector& rgbaColors, - const int64_t coordinateOffset, - const int64_t coordinateCount) -{ -// const uint64_t numCoords = coordinates.size() / 3; -// const uint64_t numQuads = numCoords / 4; // 4 three-d coords per quad - - const float* coordPtr = &coordinates[0]; - const float* normalPtr = &normals[0]; - const uint8_t* colorPtr = &rgbaColors[0]; - - CaretAssertVectorIndex(coordinates, ((coordinateOffset * 3) - + (coordinateCount * 3) - 1)); - CaretAssertVectorIndex(normals, ((coordinateOffset * 3) - + (coordinateCount * 3) - 1)); - CaretAssertVectorIndex(rgbaColors, ((coordinateOffset * 4) - + (coordinateCount * 4) - 1)); - - glBegin(GL_QUADS); - uint64_t iColor = coordinateOffset * 4; - uint64_t iNormal = coordinateOffset * 3; - uint64_t iCoord = coordinateOffset * 3; - for (int64_t i = 0; i < coordinateCount; i++) { - glNormal3fv(&normalPtr[iNormal]); - iNormal += 3; - glColor4ubv(&colorPtr[iColor]); - iColor += 4; - glVertex3fv(&coordPtr[iCoord]); - iCoord += 3; - } - - glEnd(); -// for (uint64_t i = 0; i < numQuads; i++) { -// glNormal3fv(&normalPtr[iNormal]); -// iNormal += 3; -// glColor4ubv(&colorPtr[iColor]); -// iColor += 4; -// glVertex3fv(&coordPtr[iCoord]); -// iCoord += 3; -// -// glNormal3fv(&normalPtr[iNormal]); -// iNormal += 3; -// glColor4ubv(&colorPtr[iColor]); -// iColor += 4; -// glVertex3fv(&coordPtr[iCoord]); -// iCoord += 3; -// -// glNormal3fv(&normalPtr[iNormal]); -// iNormal += 3; -// glColor4ubv(&colorPtr[iColor]); -// iColor += 4; -// glVertex3fv(&coordPtr[iCoord]); -// iCoord += 3; -// -// glNormal3fv(&normalPtr[iNormal]); -// iNormal += 3; -// glColor4ubv(&colorPtr[iColor]); -// iColor += 4; -// glVertex3fv(&coordPtr[iCoord]); -// iCoord += 3; -// } -// glEnd(); - -} - /** * Draw quads using vertex arrays. * @@ -616,514 +532,3 @@ BrainOpenGLPrimitiveDrawing::drawQuadIndicesVertexArrays(const std::vector& coordinates, - const float rgba[4], - const float lineWidth) -{ - const uint8_t rgbaByte[4] = { - static_cast(rgba[0] * 255.0), - static_cast(rgba[1] * 255.0), - static_cast(rgba[2] * 255.0), - static_cast(rgba[3] * 255.0) - }; - - std::vector emptyNormals; - drawPrimitiveWithVertexArrays(GL_LINE_LOOP, - coordinates, - emptyNormals, - rgbaByte, - lineWidth); -} - -/** - * Draw a line between each pair of three-dimensional coordinates in the given color - * with optional normal vectors. - * - * @param coordinates - * Three-Dimensional coordinates (XYZ). - * @param rgba - * One RGBA colors (4 elements) ranging [0, 255] that is used for drawing all of the lines. - * @param lineWidth - * Line width used by line drawing modes. - */ -void -BrainOpenGLPrimitiveDrawing::drawLineLoop(const std::vector& coordinates, - const uint8_t rgba[4], - const float lineWidth) -{ - std::vector emptyNormals; - drawPrimitiveWithVertexArrays(GL_LINE_LOOP, - coordinates, - emptyNormals, - rgba, - lineWidth); -} - -/** - * Draw a line between each pair of three-dimensional coordinates in the given color - * with optional normal vectors. - * - * @param coordinates - * Three-Dimensional coordinates (XYZ). - * @param rgba - * One RGBA colors (4 elements) ranging [0.0, 1.0] that is used for drawing all of the lines. - * @param lineWidth - * Line width used by line drawing modes. - */ -void -BrainOpenGLPrimitiveDrawing::drawLines(const std::vector& coordinates, - const float rgba[4], - const float lineWidth) -{ - const uint8_t rgbaByte[4] = { - static_cast(rgba[0] * 255.0), - static_cast(rgba[1] * 255.0), - static_cast(rgba[2] * 255.0), - static_cast(rgba[3] * 255.0) - }; - - std::vector emptyNormals; - drawPrimitiveWithVertexArrays(GL_LINES, - coordinates, - emptyNormals, - rgbaByte, - lineWidth); -} - -/** - * Draw a line between each pair of three-dimensional coordinates in the given color - * with optional normal vectors. - * - * @param coordinates - * Three-Dimensional coordinates (XYZ). - * @param rgba - * One RGBA colors (4 elements) ranging [0, 255] that is used for drawing all of the lines. - * @param lineWidth - * Line width used by line drawing modes. - */ -void -BrainOpenGLPrimitiveDrawing::drawLines(const std::vector& coordinates, - const uint8_t rgba[4], - const float lineWidth) -{ - std::vector emptyNormals; - drawPrimitiveWithVertexArrays(GL_LINES, - coordinates, - emptyNormals, - rgba, - lineWidth); -} - -/** - * Draw a line strip with the given coordinates in the given color - * with optional normal vectors. - * - * @param coordinates - * Three-Dimensional coordinates (XYZ). - * @param rgba - * One RGBA colors (4 elements) ranging [0.0, 1.0] that is used for drawing all of the lines. - * @param lineWidth - * Line width used by line drawing modes. - */ -void -BrainOpenGLPrimitiveDrawing::drawLineStrip(const std::vector& coordinates, - const float rgba[4], - const float lineWidth) -{ - const uint8_t rgbaByte[4] = { - static_cast(rgba[0] * 255.0), - static_cast(rgba[1] * 255.0), - static_cast(rgba[2] * 255.0), - static_cast(rgba[3] * 255.0) - }; - - std::vector emptyNormals; - drawPrimitiveWithVertexArrays(GL_LINE_STRIP, - coordinates, - emptyNormals, - rgbaByte, - lineWidth); -} - -/** - * Draw a line strip with the given coordinates in the given color - * with optional normal vectors. - * - * @param coordinates - * Three-Dimensional coordinates (XYZ). - * @param rgba - * One RGBA colors (4 elements) ranging [0, 255] that is used for drawing all of the lines. - * @param lineWidth - * Line width used by line drawing modes. - */ -void -BrainOpenGLPrimitiveDrawing::drawLineStrip(const std::vector& coordinates, - const uint8_t rgba[4], - const float lineWidth) -{ - std::vector emptyNormals; - drawPrimitiveWithVertexArrays(GL_LINE_STRIP, - coordinates, - emptyNormals, - rgba, - lineWidth); -} - -/** - * Draw a polygon with optional normal vectors. - * - * @param coordinates - * Three-Dimensional coordinates (XYZ). - * @param normals - * Normal vectors. There number of elements must match the numer of - * coordinates (in which case normal vectors are applied) or empty - * (in which case normal vectors are not applied). - * @param rgba - * One RGBA colors (4 elements) ranging [0.0, 1.0] that is used for drawing all of the lines. - */ -void -BrainOpenGLPrimitiveDrawing::drawPolygon(const std::vector& coordinates, - const std::vector& normals, - const float rgba[4]) -{ - const uint8_t rgbaByte[4] = { - static_cast(rgba[0] * 255.0), - static_cast(rgba[1] * 255.0), - static_cast(rgba[2] * 255.0), - static_cast(rgba[3] * 255.0) - }; - - const float lineWidth = 1.0; - drawPrimitiveWithVertexArrays(GL_POLYGON, - coordinates, - normals, - rgbaByte, - lineWidth); -} - -/** - * Draw a polygon with optional normal vectors. - * - * @param coordinates - * Three-Dimensional coordinates (XYZ). - * @param normals - * Normal vectors. There number of elements must match the numer of - * coordinates (in which case normal vectors are applied) or empty - * (in which case normal vectors are not applied). - * @param rgba - * One RGBA colors (4 elements) ranging [0.0, 1.0] that is used for drawing all of the lines. - */ -void -BrainOpenGLPrimitiveDrawing::drawPolygon(const std::vector& coordinates, - const std::vector& normals, - const uint8_t rgba[4]) -{ - const float lineWidth = 1.0; - drawPrimitiveWithVertexArrays(GL_POLYGON, - coordinates, - normals, - rgba, - lineWidth); -} - -/** - * Draw a line between each pair of three-dimensional coordinates in the given color - * with optional normal vectors using the given OpenGL mode. - * - * @param mode - * The OpenGL drawing mode. - * @param coordinates - * Three-Dimensional coordinates (XYZ). - * @param normals - * Normal vectors. There number of elements must match the numer of - * coordinates (in which case normal vectors are applied) or empty - * (in which case normal vectors are not applied). - * @param rgba - * One RGBA color (size=4) that is used for drawing all of the lines. - * @param lineWidth - * Line width used by line drawing modes. - */ -void -BrainOpenGLPrimitiveDrawing::drawPrimitiveWithVertexArrays(GLenum mode, - const std::vector& coordinates, - const std::vector& normals, - const uint8_t rgba[4], - const float lineWidth) -{ - const int64_t numCoords = static_cast(coordinates.size() / 3); - if (numCoords < 1) { - CaretLogWarning("Invalid number of coordinates=" + QString::number(numCoords)); - return; - } - - std::vector rgbaVector; - rgbaVector.reserve(numCoords * 4); - for (int32_t i = 0; i < numCoords; i++) { - rgbaVector.push_back(rgba[0]); - rgbaVector.push_back(rgba[1]); - rgbaVector.push_back(rgba[2]); - rgbaVector.push_back(rgba[3]); - } - - CaretAssert(numCoords == (rgbaVector.size() / 4)); - - const int64_t numNormals = static_cast(normals.size()); - if (numNormals > 0) { - if (numCoords != numNormals) { - const AString msg("Number of coordinates " - + QString::number(numCoords) - + " does not equal number of normals=" - + QString::number(numNormals)); - CaretAssertMessage(0, msg); - CaretLogWarning(msg); - return; - } - } - - - if ((mode == GL_LINES) - || (mode == GL_LINE_LOOP) - || (mode == GL_LINE_STRIP)) { - glLineWidth(lineWidth); - } - - glVertexPointer(3, - GL_FLOAT, - 0, - reinterpret_cast(&coordinates[0])); - glColorPointer(4, - GL_UNSIGNED_BYTE, - 0, - reinterpret_cast(&rgbaVector[0])); - - glEnableClientState(GL_VERTEX_ARRAY); - glEnableClientState(GL_COLOR_ARRAY); - if (numNormals > 0) { - glNormalPointer(GL_FLOAT, - 0, - reinterpret_cast(&normals[0])); - glEnableClientState(GL_NORMAL_ARRAY); - } - - glDrawArrays(mode, 0, numCoords); - - glDisableClientState(GL_VERTEX_ARRAY); - glDisableClientState(GL_COLOR_ARRAY); - if (numNormals > 0) { - glDisableClientState(GL_NORMAL_ARRAY); - } -} - -static void -createRectangle(const float center[3], - const float leftToRightUnitVector[3], - const float bottomToTopUnitVector[3], - const float width, - const float height, - float bottomLeftOut[3], - float bottomRightOut[3], - float topRightOut[3], - float topLeftOut[3]) { - - const float halfWidth = width / 2.0; - const float halfHeight = height / 2.0; - - for (int32_t i = 0; i < 3; i++) { - bottomLeftOut[i] = (center[i] - - (bottomToTopUnitVector[i] * halfHeight) - - (leftToRightUnitVector[i] * halfWidth)); - bottomRightOut[i] = (center[i] - - (bottomToTopUnitVector[i] * halfHeight) - + (leftToRightUnitVector[i] * halfWidth)); - topRightOut[i] = (center[i] - + (bottomToTopUnitVector[i] * halfHeight) - + (leftToRightUnitVector[i] * halfWidth)); - topLeftOut[i] = (center[i] - + (bottomToTopUnitVector[i] * halfHeight) - - (leftToRightUnitVector[i] * halfWidth)); - } -} - -/** - * Draw a outlined rectangle. - * - * @param bottomLeft - * Bottom left corner. - * @param bottomRight - * Bottom right corner. - * @param topRight - * Top right corner. - * @param topLeft - * Top left corner. - * @param lineThickness - * Thickness of outline. - * @param rgba - * RGBA colors (4 elements) ranging [0.0, 1.0] that is used for drawing all of the lines. - */ -void -BrainOpenGLPrimitiveDrawing::drawRectangleOutline(const float bottomLeft[3], - const float bottomRight[3], - const float topRight[3], - const float topLeft[3], - const float lineThickness, - const float rgba[4]) -{ - float leftToRightUnitVector[3]; - MathFunctions::createUnitVector(bottomLeft, bottomRight, leftToRightUnitVector); - const float width = MathFunctions::distance3D(bottomLeft, bottomRight); - - float bottomToTopUnitVector[3]; - MathFunctions::createUnitVector(bottomLeft, topLeft, bottomToTopUnitVector); - const float height = MathFunctions::distance3D(bottomLeft, topLeft); - - const float center[3] = { - (bottomLeft[0] + bottomRight[0] + topRight[0] + topLeft[0]) / 4.0, - (bottomLeft[1] + bottomRight[1] + topRight[1] + topLeft[1]) / 4.0, - (bottomLeft[2] + bottomRight[2] + topRight[2] + topLeft[2]) / 4.0 - }; - - float normalVector[3]; - MathFunctions::normalVector(topLeft, bottomLeft, bottomRight, normalVector); - - const float halfThickness = std::min(lineThickness / 2.0f, - std::min(width / 2.0f, - height / 2.0f)); - - std::vector coords; - coords.reserve(8*3); - - { - float bli[3]; - float bri[3]; - float tri[3]; - float tli[3]; - createRectangle(center, - leftToRightUnitVector, bottomToTopUnitVector, - width - halfThickness*2, height - halfThickness*2, - bli, bri, tri, tli); - - coords.insert(coords.end(), - bli, bli + 3); - coords.insert(coords.end(), - bri, bri + 3); - coords.insert(coords.end(), - tri, tri + 3); - coords.insert(coords.end(), - tli, tli + 3); - - - -// std::vector innerRectCoords; -// innerRectCoords.insert(innerRectCoords.end(), -// bli, bli + 3); -// innerRectCoords.insert(innerRectCoords.end(), -// bri, bri + 3); -// innerRectCoords.insert(innerRectCoords.end(), -// tri, tri + 3); -// innerRectCoords.insert(innerRectCoords.end(), -// tli, tli + 3); -// drawLineLoop(innerRectCoords, rgba, 1); - } - - { - float blo[3]; - float bro[3]; - float tro[3]; - float tlo[3]; - createRectangle(center, - leftToRightUnitVector, bottomToTopUnitVector, - width + halfThickness*2, height + halfThickness*2, - blo, bro, tro, tlo); - - coords.insert(coords.end(), - blo, blo + 3); - coords.insert(coords.end(), - bro, bro + 3); - coords.insert(coords.end(), - tro, tro + 3); - coords.insert(coords.end(), - tlo, tlo + 3); -// std::vector outerRectCoords; -// outerRectCoords.insert(outerRectCoords.end(), -// blo, blo + 3); -// outerRectCoords.insert(outerRectCoords.end(), -// bro, bro + 3); -// outerRectCoords.insert(outerRectCoords.end(), -// tro, tro + 3); -// outerRectCoords.insert(outerRectCoords.end(), -// tlo, tlo + 3); -// drawLineLoop(outerRectCoords, rgba, 1); - } - - const int32_t numCoords = static_cast(coords.size() / 3); - - std::vector normals; - normals.reserve(numCoords*3); - std::vector rgbaBytes; - rgbaBytes.reserve(numCoords*4); - - const uint8_t colorBytes[4] = { - static_cast(rgba[0] * 255.0), - static_cast(rgba[1] * 255.0), - static_cast(rgba[2] * 255.0), - static_cast(rgba[3] * 255.0) - }; - - for (int32_t i = 0; i < numCoords; i++) { - normals.insert(normals.end(), - normalVector, normalVector + 3); - rgbaBytes.insert(rgbaBytes.end(), - colorBytes, colorBytes + 4); - } - - CaretAssert(coords.size() == normals.size()); - CaretAssert(coords.size() == ((rgbaBytes.size() / 4) * 3)); - - std::vector indices; - indices.push_back(0); - indices.push_back(4); - indices.push_back(1); - indices.push_back(5); - indices.push_back(2); - indices.push_back(6); - indices.push_back(3); - indices.push_back(7); - indices.push_back(0); - indices.push_back(4); - - const bool enableAntiAliasingFlag = false; - if (enableAntiAliasingFlag) { - glPushAttrib(GL_COLOR_BUFFER_BIT - | GL_HINT_BIT - | GL_POLYGON_BIT); - - glBlendFunc (GL_SRC_ALPHA_SATURATE, GL_ONE); - //glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); - glEnable(GL_BLEND); - - glHint(GL_POLYGON_SMOOTH_HINT, - GL_NICEST); - glEnable(GL_POLYGON_SMOOTH); - } - - drawQuadStrips(coords, - normals, - rgbaBytes, - indices); - - if (enableAntiAliasingFlag) { - glPopAttrib(); - } -} diff --git a/src/Brain/BrainOpenGLPrimitiveDrawing.h b/src/Brain/BrainOpenGLPrimitiveDrawing.h index fac59270cc10cd7284ca71c1865a5e4c1712323e..68b57d29f9048c0fc8a75e426c2b97c3ced5c8a7 100644 --- a/src/Brain/BrainOpenGLPrimitiveDrawing.h +++ b/src/Brain/BrainOpenGLPrimitiveDrawing.h @@ -50,45 +50,6 @@ namespace caret { const std::vector& rgbaColors, const std::vector& quadStripIndices); - static void drawLineLoop(const std::vector& coordinates, - const float rgba[4], - const float lineWidth); - - static void drawLineLoop(const std::vector& coordinates, - const uint8_t rgba[4], - const float lineWidth); - - static void drawLines(const std::vector& coordinates, - const float rgba[4], - const float lineWidth); - - static void drawLines(const std::vector& coordinates, - const uint8_t rgba[4], - const float lineWidth); - - static void drawLineStrip(const std::vector& coordinates, - const float rgba[4], - const float lineWidth); - - static void drawLineStrip(const std::vector& coordinates, - const uint8_t rgba[4], - const float lineWidth); - - static void drawPolygon(const std::vector& coordinates, - const std::vector& normals, - const float rgba[4]); - - static void drawPolygon(const std::vector& coordinates, - const std::vector& normals, - const uint8_t rgba[4]); - - static void drawRectangleOutline(const float bottomLeft[3], - const float bottomRight[3], - const float topRight[3], - const float topLeft[3], - const float lineThickness, - const float rgba[4]); - private: BrainOpenGLPrimitiveDrawing(); @@ -125,14 +86,6 @@ namespace caret { const std::vector& normals, const std::vector& rgbaColors, const std::vector& quadIndices); - - static void drawPrimitiveWithVertexArrays(GLenum mode, - const std::vector& coordinates, - const std::vector& normals, - const uint8_t rgba[4], - const float lineWidth); - - }; #ifdef __BRAIN_OPEN_G_L_PRIMITIVE_DRAWING_DECLARE__ diff --git a/src/Brain/BrainOpenGLShapeRingOutline.cxx b/src/Brain/BrainOpenGLShapeRingOutline.cxx deleted file mode 100644 index 0c0470579dc046edd0cb507cb4422f5142e238d5..0000000000000000000000000000000000000000 --- a/src/Brain/BrainOpenGLShapeRingOutline.cxx +++ /dev/null @@ -1,377 +0,0 @@ - -/*LICENSE_START*/ -/* - * Copyright (C) 2014 Washington University School of Medicine - * - * 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 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License along - * with this program; if not, write to the Free Software Foundation, Inc., - * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. - */ -/*LICENSE_END*/ - -#include - -#define __BRAIN_OPEN_GL_SHAPE_RING_OUTLINE_DECLARE__ -#include "BrainOpenGLShapeRingOutline.h" -#undef __BRAIN_OPEN_GL_SHAPE_RING_OUTLINE_DECLARE__ - -#include "CaretAssert.h" -#include "MathFunctions.h" - -using namespace caret; - - - -/** - * \class caret::BrainOpenGLShapeRingOutline - * \brief Create a ring shape drawn with a line for use with OpenGL. - */ - -/** - * Constructor. - * @param numberOfSides - * Number of sides in the ring. - */ -BrainOpenGLShapeRingOutline::BrainOpenGLShapeRingOutline(const int32_t numberOfSides, - const float radius, - const float lineThickness) -: BrainOpenGLShape(), -m_numberOfSides(numberOfSides), -m_radius(radius), -m_lineThickness(lineThickness) -{ - m_isApplyColoring = true; - - bool debugFlag = false; - - /* - * Setup step size based upon number of points around circle - */ - // const int32_t numberOfPoints = 8; - const float step = (2.0 * M_PI) / m_numberOfSides; - - /* - * Generate points around ring - */ - const float z = 0.0; - for (int32_t i = 0; i < m_numberOfSides; i++) { - const float t = step * i; - - // const float x = majorRadius * std::cos(t); - // const float y = minorRadius * std::sin(t); - const float x = m_radius * std::cos(t); - const float y = m_radius * std::sin(t); - - m_lineLoop.push_back(m_coordinates.size() / 3); - - m_coordinates.push_back(x); - m_coordinates.push_back(y); - m_coordinates.push_back(z); - - m_normals.push_back(0.0); - m_normals.push_back(0.0); - m_normals.push_back(1.0); - } - - if (debugFlag) { - CaretAssert(m_coordinates.size() == m_normals.size()); - const int32_t numPoints = static_cast(m_coordinates.size() / 3); - for (int32_t i = 0; i < numPoints; i++) { - const int32_t i3 = i * 3; - std::cout << "points[" << i << "]=(" << AString::fromNumbers(&m_coordinates[i3], 3, ",") - << ")" << std::endl; - std::cout << "side normal[" << i << "]=(" << AString::fromNumbers(&m_normals[i3], 3, ",") - << ")" << std::endl; - } - - //std::cout << "Sides: "; - //printTriangleStrip(m_triangleStrip); - //std::cout << std::endl; - } - - /* - * Create storage for colors - */ - const int64_t numCoords = static_cast(m_coordinates.size()) / 3; - const int64_t numRGBA = numCoords * 4; - m_rgbaByte.resize(numRGBA * 4, 0); - for (GLuint i = 0; i < numCoords; i++) { - const int32_t i4 = i * 4; - - CaretAssertVectorIndex(m_rgbaByte, i4+3); - m_rgbaByte[i4] = 0; - m_rgbaByte[i4+1] = 0; - m_rgbaByte[i4+2] = 0; - m_rgbaByte[i4+3] = 255; - } -} - -/** - * Destructor. - */ -BrainOpenGLShapeRingOutline::~BrainOpenGLShapeRingOutline() -{ - -} - -void -BrainOpenGLShapeRingOutline::setupOpenGLForShape(const BrainOpenGL::DrawMode drawMode) -{ - m_displayList = 0; - - m_coordinatesBufferID = 0; - m_coordinatesRgbaByteBufferID = 0; - m_normalBufferID = 0; - m_lineLoopBufferID = 0; - switch (drawMode) { - case BrainOpenGL::DRAW_MODE_DISPLAY_LISTS: - { - m_displayList = createDisplayList(); - - if (m_displayList > 0) { - glNewList(m_displayList, - GL_COMPILE); - uint8_t rgbaUnused[4] = { 0, 0, 0, 0 }; - m_isApplyColoring = false; - drawShape(BrainOpenGL::DRAW_MODE_IMMEDIATE, - rgbaUnused); - m_isApplyColoring = true; - glEndList(); - } - } - break; - case BrainOpenGL::DRAW_MODE_IMMEDIATE: - { - /* nothing to do for this case */ - } - break; - case BrainOpenGL::DRAW_MODE_INVALID: - { - CaretAssert(0); - } - break; - case BrainOpenGL::DRAW_MODE_VERTEX_BUFFERS: -#ifdef BRAIN_OPENGL_INFO_SUPPORTS_VERTEX_BUFFERS - /* - * Put coordinates into its buffer. - */ - m_coordinatesBufferID = createBufferID(); - glBindBuffer(GL_ARRAY_BUFFER, - m_coordinatesBufferID); - glBufferData(GL_ARRAY_BUFFER, - m_coordinates.size() * sizeof(GLfloat), - &m_coordinates[0], - GL_STATIC_DRAW); - - /* - * For RGBA coloring - */ - m_coordinatesRgbaByteBufferID = createBufferID(); - glBindBuffer(GL_ARRAY_BUFFER, - m_coordinatesRgbaByteBufferID); - glBufferData(GL_ARRAY_BUFFER, - m_rgbaByte.size() * sizeof(GLubyte), - &m_rgbaByte[0], - GL_DYNAMIC_DRAW); - - /* - * Put side normals into its buffer. - */ - m_normalBufferID = createBufferID(); - glBindBuffer(GL_ARRAY_BUFFER, - m_normalBufferID); - glBufferData(GL_ARRAY_BUFFER, - m_normals.size() * sizeof(GLfloat), - &m_normals[0], - GL_STATIC_DRAW); - - /* - * Put line loop into its buffer. - */ - m_lineLoopBufferID = createBufferID(); - glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, - m_lineLoopBufferID); - glBufferData(GL_ELEMENT_ARRAY_BUFFER, - m_lineLoop.size() * sizeof(GLuint), - &m_lineLoop[0], - GL_STATIC_DRAW); - - /* - * Deselect active buffer. - */ - glBindBuffer(GL_ARRAY_BUFFER, - 0); - glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, - 0); -#endif // BRAIN_OPENGL_INFO_SUPPORTS_VERTEX_BUFFERS - break; - } -} - -/** - * Draw the shape. - * - * @param drawMode - * How to draw the shape. - * @param rgba - * RGBA coloring ranging 0.0 to 1.0 - */ -void -BrainOpenGLShapeRingOutline::drawShape(const BrainOpenGL::DrawMode drawMode, - const float rgba[4]) -{ - const uint8_t rgbaByte[4] = { - static_cast(rgba[0] * 255.0), - static_cast(rgba[1] * 255.0), - static_cast(rgba[2] * 255.0), - static_cast(rgba[3] * 255.0) - }; - - drawShape(drawMode, - rgbaByte); -} - -/** - * Draw the shape. - * - * @param drawMode - * How to draw the shape. - * @param rgba - * RGBA coloring ranging 0 to 255. - */ -void -BrainOpenGLShapeRingOutline::drawShape(const BrainOpenGL::DrawMode drawMode, - const uint8_t rgba[4]) -{ - switch (drawMode) { - case BrainOpenGL::DRAW_MODE_DISPLAY_LISTS: - { - if (m_displayList > 0) { - if (m_isApplyColoring) { - glColor4ubv(rgba); - } - glCallList(m_displayList); - } - } - break; - case BrainOpenGL::DRAW_MODE_IMMEDIATE: - { - glLineWidth(m_lineThickness); - if (m_isApplyColoring) { - glColor4ubv(rgba); - } - const int32_t numVertices = static_cast(m_lineLoop.size()); - glBegin(GL_LINE_LOOP); - for (int32_t j = 0; j < numVertices; j++) { - const int32_t vertexIndex = m_lineLoop[j] * 3; - - CaretAssertVectorIndex(m_normals, vertexIndex); - CaretAssertVectorIndex(m_coordinates, vertexIndex); - - glNormal3fv(&m_normals[vertexIndex]); - glVertex3fv(&m_coordinates[vertexIndex]); - } - glEnd(); - } - break; - case BrainOpenGL::DRAW_MODE_INVALID: - { - CaretAssert(0); - } - break; - case BrainOpenGL::DRAW_MODE_VERTEX_BUFFERS: -#ifdef BRAIN_OPENGL_INFO_SUPPORTS_VERTEX_BUFFERS - /* - * Enable vertices and normals for buffers - */ - glEnableClientState(GL_VERTEX_ARRAY); - glEnableClientState(GL_NORMAL_ARRAY); - glEnableClientState(GL_COLOR_ARRAY); - - /* - * Set the vertices for drawing. - */ - glBindBuffer(GL_ARRAY_BUFFER, - m_coordinatesBufferID); - glVertexPointer(3, - GL_FLOAT, - 0, - (GLvoid*)0); - - /* - * Put BYTE colors into its buffer - */ - const int64_t numRGBA = static_cast(m_rgbaByte.size()) / 4; - for (int64_t ir = 0; ir < numRGBA; ir++) { - const int64_t ir4 = ir * 4; - CaretAssertVectorIndex(m_rgbaByte, ir4+3); - m_rgbaByte[ir4] = rgba[0]; - m_rgbaByte[ir4+1] = rgba[1]; - m_rgbaByte[ir4+2] = rgba[2]; - m_rgbaByte[ir4+3] = rgba[3]; - } - CaretAssert(glIsBuffer(m_coordinatesRgbaByteBufferID) == GL_TRUE); - glBindBuffer(GL_ARRAY_BUFFER, - m_coordinatesRgbaByteBufferID); - glBufferData(GL_ARRAY_BUFFER, - m_rgbaByte.size() * sizeof(GLubyte), - &m_rgbaByte[0], - GL_DYNAMIC_DRAW); - glColorPointer(4, - GL_UNSIGNED_BYTE, - 0, - (GLvoid*)0); - - /* - * Set the side normal vectors for drawing. - */ - glBindBuffer(GL_ARRAY_BUFFER, - m_normalBufferID); - glNormalPointer(GL_FLOAT, - 0, - (GLvoid*)0); - - /* - * Apply the line width - */ - glLineWidth(m_lineThickness); - - /* - * Draw the side triangle fans. - */ - glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, - m_lineLoopBufferID); - glDrawElements(GL_LINE_LOOP, - m_lineLoop.size(), - GL_UNSIGNED_INT, - (GLvoid*)0); - /* - * Deselect active buffer. - */ - glBindBuffer(GL_ARRAY_BUFFER, - 0); - glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, - 0); - - /* - * Disable vertices and normals for buffers. - * Otherwise, bad thing will happen. - */ - glDisableClientState(GL_VERTEX_ARRAY); - glDisableClientState(GL_NORMAL_ARRAY); - glDisableClientState(GL_COLOR_ARRAY); -#endif // __BRAIN_OPEN_GL_SHAPE_RING_OUTLINE_DECLARE__ - break; - } -} - diff --git a/src/Brain/BrainOpenGLShapeRingOutline.h b/src/Brain/BrainOpenGLShapeRingOutline.h deleted file mode 100644 index a79aa458cad7fd360c12c6677f77671bae1eabdd..0000000000000000000000000000000000000000 --- a/src/Brain/BrainOpenGLShapeRingOutline.h +++ /dev/null @@ -1,92 +0,0 @@ -#ifndef __BRAIN_OPEN_GL_SHAPE_RING_OUTLINE_H_ -#define __BRAIN_OPEN_GL_SHAPE_RING_OUTLINE_H_ - -/*LICENSE_START*/ -/* - * Copyright (C) 2014 Washington University School of Medicine - * - * 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 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License along - * with this program; if not, write to the Free Software Foundation, Inc., - * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. - */ -/*LICENSE_END*/ - -#include - -#include "BrainOpenGLShape.h" - -namespace caret { - - class BrainOpenGLShapeRingOutline : public BrainOpenGLShape { - - public: - BrainOpenGLShapeRingOutline(const int32_t numberOfSides, - const float radius, - const float lineThickness); - - virtual ~BrainOpenGLShapeRingOutline(); - - float getLineThickness() const; - - private: - BrainOpenGLShapeRingOutline(const BrainOpenGLShapeRingOutline&); - - BrainOpenGLShapeRingOutline& operator=(const BrainOpenGLShapeRingOutline&); - - public: - - // ADD_NEW_METHODS_HERE - - protected: - void drawShape(const BrainOpenGL::DrawMode drawMode, - const uint8_t rgba[4]); - - void drawShape(const BrainOpenGL::DrawMode drawMode, - const float rgba[4]); - - - void setupOpenGLForShape(const BrainOpenGL::DrawMode drawMode); - - private: - - // ADD_NEW_MEMBERS_HERE - - const int32_t m_numberOfSides; - const float m_radius; - const float m_lineThickness; - - GLuint m_coordinatesBufferID; - GLuint m_coordinatesRgbaByteBufferID; - GLuint m_normalBufferID; - GLuint m_lineLoopBufferID; - - - GLuint m_displayList; - - std::vector m_coordinates; - - std::vector m_rgbaByte; - - std::vector m_normals; - - std::vector m_lineLoop; - - bool m_isApplyColoring; - }; - -#ifdef __BRAIN_OPEN_GL_SHAPE_RING_OUTLINE_DECLARE__ - // -#endif // __BRAIN_OPEN_GL_SHAPE_RING_OUTLINE_DECLARE__ - -} // namespace -#endif //__BRAIN_OPEN_GL_SHAPE_RING_OUTLINE_H_ diff --git a/src/Brain/BrainOpenGLTextRenderInterface.cxx b/src/Brain/BrainOpenGLTextRenderInterface.cxx index 72a3d6456b0ccbf699a65c954a3397d3499c8f43..d9d88b42a8bff9eccc0b51b9c3f0591af197c92c 100644 --- a/src/Brain/BrainOpenGLTextRenderInterface.cxx +++ b/src/Brain/BrainOpenGLTextRenderInterface.cxx @@ -64,6 +64,8 @@ BrainOpenGLTextRenderInterface::~BrainOpenGLTextRenderInterface() * Viewport Y-coordinate. * @param viewportZ * Viewport Z-coordinate. + * @param viewportWidth + * Widgth of the viewport needed for percentage height text. * @param viewportHeight * Height of the viewport needed for percentage height text. * @param bottomLeftOut @@ -80,6 +82,7 @@ BrainOpenGLTextRenderInterface::getBoundsForTextAtViewportCoords(const Annotatio const float viewportX, const float viewportY, const float viewportZ, + const float viewportWidth, const float viewportHeight, float bottomLeftOut[3], float bottomRightOut[3], @@ -95,6 +98,7 @@ BrainOpenGLTextRenderInterface::getBoundsForTextAtViewportCoords(const Annotatio viewportX, viewportY, viewportZ, + viewportWidth, viewportHeight, bottomLeft, bottomRight, diff --git a/src/Brain/BrainOpenGLTextRenderInterface.h b/src/Brain/BrainOpenGLTextRenderInterface.h index 9da44305c398b347c5340484653d29e7cb4e6f0c..c8844cd8baa04c470fb243948093b09a1f3300f3 100644 --- a/src/Brain/BrainOpenGLTextRenderInterface.h +++ b/src/Brain/BrainOpenGLTextRenderInterface.h @@ -132,6 +132,8 @@ namespace caret { * * @param annotationText * Text for width and height estimation. + * @param viewportWidth + * Width of the viewport needed for percentage height text. * @param viewportHeight * Height of the viewport needed for percentage height text. * @param widthOut @@ -140,6 +142,7 @@ namespace caret { * Estimated height of text. */ virtual void getTextWidthHeightInPixels(const AnnotationText& annotationText, + const double viewportWidth, const double viewportHeight, double& widthOut, double& heightOut) = 0; @@ -158,6 +161,8 @@ namespace caret { * Viewport Y-coordinate. * @param viewportZ * Viewport Z-coordinate. + * @param viewportWidth + * Width of the viewport needed for percentage height text. * @param viewportHeight * Height of the viewport needed for percentage height text. * @param bottomLeftOut @@ -173,6 +178,7 @@ namespace caret { const double viewportX, const double viewportY, const double viewportZ, + const double viewportWidth, const double viewportHeight, double bottomLeftOut[3], double bottomRightOut[3], @@ -193,6 +199,8 @@ namespace caret { * Viewport Y-coordinate. * @param viewportZ * Viewport Z-coordinate. + * @param viewportWidth + * Width of the viewport needed for percentage height text. * @param viewportHeight * Height of the viewport needed for percentage height text. * @param bottomLeftOut @@ -208,6 +216,7 @@ namespace caret { const float viewportX, const float viewportY, const float viewportZ, + const float viewportWidth, const float viewportHeight, float bottomLeftOut[3], float bottomRightOut[3], diff --git a/src/Brain/BrainOpenGLTextureManager.cxx b/src/Brain/BrainOpenGLTextureManager.cxx deleted file mode 100644 index 38efe3d8db5eb117661111dde66116d09c0bede1..0000000000000000000000000000000000000000 --- a/src/Brain/BrainOpenGLTextureManager.cxx +++ /dev/null @@ -1,230 +0,0 @@ - -/*LICENSE_START*/ -/* - * Copyright (C) 2016 Washington University School of Medicine - * - * 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 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License along - * with this program; if not, write to the Free Software Foundation, Inc., - * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. - */ -/*LICENSE_END*/ - -#include - -#define __BRAIN_OPEN_G_L_TEXTURE_MANAGER_DECLARE__ -#include "BrainOpenGLTextureManager.h" -#undef __BRAIN_OPEN_G_L_TEXTURE_MANAGER_DECLARE__ - -#include "CaretAssert.h" -#include "CaretLogger.h" -#include "DrawnWithOpenGLTextureInfo.h" -#include "EventManager.h" -#include "EventOpenGLTexture.h" -using namespace caret; - - - -/** - * \class caret::BrainOpenGLTextureManager - * \brief Manages allocated OpenGL Textures. - * \ingroup Brain - * - * This texture manager helps out with tracking OpenGL - * textures. There are instance in which textures need - * to be deleted such as when QGLWidget::renderPixmap() - * is used to capture an image of the graphics region. - * - * Note that the FTGL text rendering code also uses - * texture and this texture manager does not interact - * in any way with FTGL. - */ - -/** - * Constructor. - * - * @param windowIndex - * Index of window for which textures are managed. - */ -BrainOpenGLTextureManager::BrainOpenGLTextureManager(const int32_t windowIndex) -: CaretObject(), -m_windowIndex(windowIndex) -{ - EventManager::get()->addEventListener(this, EventTypeEnum::EVENT_OPENGL_TEXTURE); -} - -/** - * Destructor. - */ -BrainOpenGLTextureManager::~BrainOpenGLTextureManager() -{ - /* - * We do not need to worry about deleting texture names - * since when a instance of this class is deleted, the - * OpenGL context is also deleted which deletes all - * of the texture names. - */ - EventManager::get()->removeAllEventsFromListener(this); -} - -/** - * Receive an event. - * - * @param event - * The event that the receive can respond to. - */ -void -BrainOpenGLTextureManager::receiveEvent(Event* event) -{ - if (event->getEventType() == EventTypeEnum::EVENT_OPENGL_TEXTURE) { - EventOpenGLTexture* textureEvent = dynamic_cast(event); - CaretAssert(textureEvent); - - switch (textureEvent->getMode()) { - case EventOpenGLTexture::MODE_NONE: - break; - case EventOpenGLTexture::MODE_DELETE_ALL_TEXTURES_IN_WINDOW: - break; - case EventOpenGLTexture::MODE_DELETE_TEXTURE: - { - int32_t windowIndex = -1; - int32_t textureName = 0; - textureEvent->getModeDeleteTexture(windowIndex, - textureName); - if (m_windowIndex == windowIndex) { - deleteTextureName(textureName); - - textureEvent->setEventProcessed(); - } - } - break; - } - } -} - -/** - * Delete all textures for a given window index. - * - * @param windowIndex - * Index of the window. - */ -void -BrainOpenGLTextureManager::deleteAllTexturesForWindow(const int32_t windowIndex) -{ - EventOpenGLTexture textureEvent; - textureEvent.setModeDeleteAllTexturesInWindow(windowIndex); - EventManager::get()->sendEvent(textureEvent.getPointer()); -} - - -/** - * Get the texture name for the given texture info. - * - * @param textureInfo - * The texture information. - * @param textureNameOut - * Output containing OpenGL texture name for use with glBindTexture. - * @param newTextureNameFlagOut - * If true, the texture name is new and the caller must create - * or recreate the texture image. Recreation may be necessary - * after an image capture operation due to it creating a new - * OpenGL context. - */ -void -BrainOpenGLTextureManager::getTextureName(DrawnWithOpenGLTextureInfo* textureInfo, - GLuint& textureNameOut, - bool& newTextureNameFlagOut) -{ - textureNameOut = 0; - newTextureNameFlagOut = false; - - CaretAssert(m_windowIndex >= 0); - textureNameOut = textureInfo->getTextureNameForWindow(m_windowIndex); - if (textureNameOut > 0) { - /* - * Verify texture is still valid (could be deleted during image capture - * which recreates the OpenGL context). - */ - if (glIsTexture(textureNameOut)) { - return; - } - } - - textureNameOut = createNewTextureName(); - textureInfo->setTextureNameForWindow(m_windowIndex, - textureNameOut); - newTextureNameFlagOut = true; -} - -/** - * @return A new texture name. - */ -GLuint -BrainOpenGLTextureManager::createNewTextureName() -{ - /* - * Each workbench window has its own, unique OpenGL context. - * Identical OpenGL texture names may exist in each OpenGL - * context. To simplify management of textures, we do not - * want the same texture name in more than one OpenGL - * context. - * - * So, we do not use glGenTexture() to create texture - * names. Instead, we use our own generator that - * ensures each name is never used in more than one - * OpenGL context. This simplifies management of textures - * and especially the removal of textures when an - * object drawn using a texture is deleted. - * - * In addition, other modules (such as FTGL font - * rendering) may use textures, so we need to - * ensure that a new name is not used elsewhere. - */ - s_textureNameGenerator++; - if (s_textureNameGenerator == std::numeric_limits::max()) { - s_textureNameGenerator = 1; - } - while (glIsTexture(s_textureNameGenerator) == GL_TRUE) { - s_textureNameGenerator++; - if (s_textureNameGenerator == std::numeric_limits::max()) { - s_textureNameGenerator = 1; - } - } - - return s_textureNameGenerator; -} - - -/** - * Delete a texture name. - * - * @param textureName - * Texture name that is deleted. - */ -void -BrainOpenGLTextureManager::deleteTextureName(GLuint textureName) -{ - if (glIsTexture(textureName) == GL_TRUE) { - glDeleteTextures(1, &textureName); - } -} - -/** - * Get a description of this object's content. - * @return String describing this object's content. - */ -AString -BrainOpenGLTextureManager::toString() const -{ - return "BrainOpenGLTextureManager"; -} - diff --git a/src/Brain/BrainOpenGLTextureManager.h b/src/Brain/BrainOpenGLTextureManager.h deleted file mode 100644 index 8180442963304c713ec5f648440eaa0f8aaa2b80..0000000000000000000000000000000000000000 --- a/src/Brain/BrainOpenGLTextureManager.h +++ /dev/null @@ -1,80 +0,0 @@ -#ifndef __BRAIN_OPEN_G_L_TEXTURE_MANAGER_H__ -#define __BRAIN_OPEN_G_L_TEXTURE_MANAGER_H__ - -/*LICENSE_START*/ -/* - * Copyright (C) 2016 Washington University School of Medicine - * - * 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 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License along - * with this program; if not, write to the Free Software Foundation, Inc., - * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. - */ -/*LICENSE_END*/ - -#include -#include - -#include "CaretObject.h" -#include "CaretOpenGLInclude.h" -#include "EventListenerInterface.h" - - -namespace caret { - - class DrawnWithOpenGLTextureInfo; - - class BrainOpenGLTextureManager : public CaretObject, public EventListenerInterface { - - public: - BrainOpenGLTextureManager(const int32_t windowIndex); - - virtual ~BrainOpenGLTextureManager(); - - virtual void receiveEvent(Event* event); - - void getTextureName(DrawnWithOpenGLTextureInfo* textureInfo, - GLuint& textureNameOut, - bool& newTextureNameFlagOut); - - void deleteAllTexturesForWindow(const int32_t windowIndex); - - // ADD_NEW_METHODS_HERE - - virtual AString toString() const; - - private: - BrainOpenGLTextureManager(const BrainOpenGLTextureManager&); - - BrainOpenGLTextureManager& operator=(const BrainOpenGLTextureManager&); - - GLuint createNewTextureName(); - - void deleteTextureName(GLuint textureName); - - const int32_t m_windowIndex; - - /** - * Generates the texture names - */ - static int32_t s_textureNameGenerator; - - // ADD_NEW_MEMBERS_HERE - - }; - -#ifdef __BRAIN_OPEN_G_L_TEXTURE_MANAGER_DECLARE__ - int32_t BrainOpenGLTextureManager::s_textureNameGenerator = 0; -#endif // __BRAIN_OPEN_G_L_TEXTURE_MANAGER_DECLARE__ - -} // namespace -#endif //__BRAIN_OPEN_G_L_TEXTURE_MANAGER_H__ diff --git a/src/Brain/BrainOpenGLViewportContent.cxx b/src/Brain/BrainOpenGLViewportContent.cxx index d5feb698ef41b82c686d389c181059c50588a845..69c0795e73974ee46df8beb3006b39005621d5d3 100644 --- a/src/Brain/BrainOpenGLViewportContent.cxx +++ b/src/Brain/BrainOpenGLViewportContent.cxx @@ -29,8 +29,11 @@ #include #include "BrowserTabContent.h" +#include "BrowserWindowContent.h" #include "CaretAssert.h" #include "CaretLogger.h" +#include "EventBrowserWindowContent.h" +#include "EventManager.h" #include "GapsAndMargins.h" #include "MathFunctions.h" #include "ModelSurfaceMontage.h" @@ -92,6 +95,14 @@ m_browserTabContent(browserTabContent) m_modelY = modelViewport[1]; m_modelWidth = modelViewport[2]; m_modelHeight = modelViewport[3]; + + m_chartDataProjectionMatrix.identity(); + m_chartDataModelViewMatrix.identity(); + m_chartDataX = 0; + m_chartDataY = 0; + m_chartDataWidth = 0; + m_chartDataHeight = 0; + m_chartDataViewportValidFlag = false; } /** @@ -139,6 +150,13 @@ BrainOpenGLViewportContent::operator=(const BrainOpenGLViewportContent& obj) void BrainOpenGLViewportContent::initializeMembersBrainOpenGLViewportContent() { + m_chartDataProjectionMatrix.identity(); + m_chartDataModelViewMatrix.identity(); + m_chartDataX = 0; + m_chartDataY = 0; + m_chartDataWidth = 0; + m_chartDataHeight = 0; + m_chartDataViewportValidFlag = false; m_modelX = 0; m_modelY = 0; m_modelWidth = 0; @@ -161,6 +179,13 @@ BrainOpenGLViewportContent::initializeMembersBrainOpenGLViewportContent() void BrainOpenGLViewportContent::copyHelperBrainOpenGLViewportContent(const BrainOpenGLViewportContent& obj) { + m_chartDataProjectionMatrix = obj.m_chartDataProjectionMatrix; + m_chartDataModelViewMatrix = obj.m_chartDataModelViewMatrix; + m_chartDataX = obj.m_chartDataX; + m_chartDataY = obj.m_chartDataY; + m_chartDataWidth = obj.m_chartDataWidth; + m_chartDataHeight = obj.m_chartDataHeight; + m_chartDataViewportValidFlag = obj.m_chartDataViewportValidFlag; m_modelX = obj.m_modelX; m_modelY = obj.m_modelY; m_modelWidth = obj.m_modelWidth; @@ -281,6 +306,58 @@ BrainOpenGLViewportContent::isTabHighlighted() const return m_highlightTab; } +/** + * Get the data bounds and viewport for drawing the chart data. + * + * @param chartDataProjectionMatrixOut + * Output into which projection matrix for drawing chart data loaded. + * @param chartDataModelViewMatrixOut + * Output into which model viewing matrix for drawing chart data loaded. + * @param chartViewportOut + * Output into which viewport is loaded. + * (x, y, width, height) + * @return + * True if the chart data viewport is valid. + */ +bool +BrainOpenGLViewportContent::getChartDataMatricesAndViewport(Matrix4x4& chartDataProjectionMatrixOut, + Matrix4x4& chartDataModelViewMatrixOut, + int chartViewportOut[4]) const +{ + chartDataProjectionMatrixOut = m_chartDataProjectionMatrix; + chartDataModelViewMatrixOut = m_chartDataModelViewMatrix; + chartViewportOut[0] = m_chartDataX; + chartViewportOut[1] = m_chartDataY; + chartViewportOut[2] = m_chartDataWidth; + chartViewportOut[3] = m_chartDataHeight; + return m_chartDataViewportValidFlag; +} + +/** + * Set the viewport for drawing the chart data. + * + * @param chartDataProjectionMatrix + * Pojection matrix for drawing chart data. + * @param chartDataModelViewMatrix + * Viewing matrix for drawing chart data. + * @param chartDataViewport + * Viewport (x, y, width, height). + */ +void +BrainOpenGLViewportContent::setChartDataMatricesAndViewport(const Matrix4x4& chartDataProjectionMatrix, + const Matrix4x4& chartDataModelViewMatrix, + const int chartViewport[4]) const +{ + m_chartDataProjectionMatrix = chartDataProjectionMatrix; + m_chartDataModelViewMatrix = chartDataModelViewMatrix; + m_chartDataX = chartViewport[0]; + m_chartDataY = chartViewport[1]; + m_chartDataWidth = chartViewport[2]; + m_chartDataHeight = chartViewport[3]; + m_chartDataViewportValidFlag = true; +} + + /** * Get the viewport for drawing the model (has been reduced * from tab viewport by applying the margin). @@ -373,7 +450,19 @@ BrainOpenGLViewportContent::toString() const const QString tabMsg = QString(" Tab x=%1 y=%2 w=%3 h=%4").arg(m_tabX).arg(m_tabY).arg(m_tabWidth).arg(m_tabHeight); const QString modelMsg = QString(" Model x=%1 y=%2 w=%3 h=%4").arg(m_modelX).arg(m_modelY).arg(m_modelWidth).arg(m_modelHeight); - AString msgOut(windowMsg); + AString msgOut; + if (m_chartDataViewportValidFlag) { + const QString chartProjectionMsg = QString(" Chart Projection=" + m_chartDataProjectionMatrix.toFormattedString(" ")); + const QString chartModelMsg = QString(" Chart Model View=" + m_chartDataModelViewMatrix.toFormattedString(" ")); + const QString chartViewportMsg = QString(" Chart x=%1 y=%2 w=%3 h=%4").arg(m_chartDataX).arg(m_chartDataY).arg(m_chartDataWidth).arg(m_chartDataHeight); + msgOut.appendWithNewLine(chartProjectionMsg); + msgOut.appendWithNewLine(chartModelMsg); + msgOut.appendWithNewLine(chartViewportMsg); + } + else { + msgOut.appendWithNewLine(" Chart Invalid."); + } + msgOut.appendWithNewLine(windowMsg); msgOut.appendWithNewLine(tabMsg); msgOut.appendWithNewLine(modelMsg); @@ -395,10 +484,11 @@ BrainOpenGLViewportContent::toString() const * Viewport content for the single tab. */ BrainOpenGLViewportContent* -BrainOpenGLViewportContent::createViewportForSingleTab(BrowserTabContent* browserTabContent, - const GapsAndMargins* gapsAndMargins, - const int32_t windowIndex, - const int32_t windowViewport[4]) +BrainOpenGLViewportContent::createViewportForSingleTab(std::vector& allTabContents, + BrowserTabContent* selectedTabContent, + const GapsAndMargins* gapsAndMargins, + const int32_t windowIndex, + const int32_t windowViewport[4]) { int tabViewport[4] = { windowViewport[0], @@ -414,14 +504,40 @@ BrainOpenGLViewportContent::createViewportForSingleTab(BrowserTabContent* browse tabViewport[3] }; - if (browserTabContent != NULL) { - if (browserTabContent->isAspectRatioLocked()) { - const float aspectRatio = browserTabContent->getAspectRatio(); + std::unique_ptr eventContent = EventBrowserWindowContent::getWindowContent(windowIndex); + EventManager::get()->sendEvent(eventContent->getPointer()); + + const BrowserWindowContent* browserWindowContent = eventContent->getBrowserWindowContent(); + CaretAssert(browserWindowContent); + if (browserWindowContent->isAllTabsInWindowAspectRatioLocked()) { + /** + * Update aspect locking for any tabs that have not bee locked + * Locking is done here so that it will work for new tabs and + * for tabs from old scenes before "lock all". + * Aspect locking is also performed here so that it works + * with both GUI and Command Line Show Scene + */ + for (auto btc : allTabContents) { + if ( ! btc->isAspectRatioLocked()) { + if (tabViewport[2] > 0) { + const float aspectRatio = (static_cast(tabViewport[3]) + / static_cast(tabViewport[2])); + + btc->setAspectRatio(aspectRatio); + btc->setAspectRatioLocked(true); + } + } + } + } + + if (selectedTabContent != NULL) { + if (selectedTabContent->isAspectRatioLocked()) { + const float aspectRatio = selectedTabContent->getAspectRatio(); BrainOpenGLViewportContent::adjustViewportForAspectRatio(tabViewport, aspectRatio); } - const int32_t tabIndex = browserTabContent->getTabNumber(); + const int32_t tabIndex = selectedTabContent->getTabNumber(); createModelViewport(tabViewport, tabIndex, gapsAndMargins, @@ -435,7 +551,7 @@ BrainOpenGLViewportContent::createViewportForSingleTab(BrowserTabContent* browse modelViewport, windowIndex, false, - browserTabContent); + selectedTabContent); return vpContent; } @@ -510,6 +626,13 @@ BrainOpenGLViewportContent::createViewportContentForTileTabs(std::vector rowHeights(numRows, 0); std::vector columnWidths(numColumns, 0); + + std::unique_ptr eventContent = EventBrowserWindowContent::getWindowContent(windowIndex); + EventManager::get()->sendEvent(eventContent->getPointer()); + const BrowserWindowContent* browserWindowContent = eventContent->getBrowserWindowContent(); + CaretAssert(browserWindowContent); + const bool allTabsAspectLockedFlag = browserWindowContent->isAllTabsInWindowAspectRatioLocked(); + /* * Create the sizes for the tabs before and after application * of aspect ratio. @@ -524,6 +647,28 @@ BrainOpenGLViewportContent::createViewportContentForTileTabs(std::vectorisAspectRatioLocked()) { + if (vpWidth > 0) { + const float aspectRatio = (static_cast(vpHeight) + / static_cast(vpWidth)); + + tabContents[iTab]->setAspectRatio(aspectRatio); + tabContents[iTab]->setAspectRatioLocked(true); + } + } + } + /* * Note: the constructor will adjust the width and * height if lock aspect ratio is enabled. @@ -782,7 +927,6 @@ BrainOpenGLViewportContent::getSurfaceMontageModelViewport(const int32_t montage } } - /* =================================================================================================== */ @@ -863,3 +1007,180 @@ BrainOpenGLViewportContent::TileTabsViewportSizingInfo::print(const int32_t x, std::cout << qPrintable(msg) << std::endl; } + +/** + * Get the viewport for a slice in all slices view. + * + * @param tabViewport + * The viewport for the tab containing all slices. + * @param sliceViewPlane + * The plane for slice drawing. Note: "ALL" is used for orientation axes in oblique view. + * @param allPlanesLayout + * The layout in ALL slices view. + * @param viewportOut + * Output viewport (region of graphics area) for drawing slices. + */ +void +BrainOpenGLViewportContent::getSliceAllViewViewport(const int32_t tabViewport[4], + const VolumeSliceViewPlaneEnum::Enum sliceViewPlane, + const VolumeSliceViewAllPlanesLayoutEnum::Enum allPlanesLayout, + int32_t viewportOut[4]) +{ + const int32_t gap = 2; + const int32_t tabViewportX = tabViewport[0]; + const int32_t tabViewportY = tabViewport[1]; + const int32_t tabViewportWidth = tabViewport[2]; + const int32_t tabViewportHeight = tabViewport[3]; + + switch (allPlanesLayout) { + case VolumeSliceViewAllPlanesLayoutEnum::COLUMN_LAYOUT: + { + const int32_t vpHeight = (tabViewportHeight - (gap * 2)) / 3; + const int32_t vpOffsetY = vpHeight + gap; + const int32_t vpWidth = tabViewportWidth; + + switch (sliceViewPlane) { + case VolumeSliceViewPlaneEnum::ALL: + viewportOut[0] = 0; + viewportOut[1] = 0; + viewportOut[2] = 0; + viewportOut[3] = 0; + break; + case VolumeSliceViewPlaneEnum::AXIAL: + viewportOut[0] = tabViewportX; + viewportOut[1] = tabViewportY; + viewportOut[2] = vpWidth; + viewportOut[3] = vpHeight; + break; + case VolumeSliceViewPlaneEnum::CORONAL: + viewportOut[0] = tabViewportX; + viewportOut[1] = tabViewportY + vpOffsetY; + viewportOut[2] = vpWidth; + viewportOut[3] = vpHeight; + break; + case VolumeSliceViewPlaneEnum::PARASAGITTAL: + viewportOut[0] = tabViewportX; + viewportOut[1] = tabViewportY + (vpOffsetY * 2); + viewportOut[2] = vpWidth; + viewportOut[3] = vpHeight; + break; + } + } + break; + case VolumeSliceViewAllPlanesLayoutEnum::GRID_LAYOUT: + { + const int32_t vpWidth = (tabViewportWidth - gap) / 2; + const int32_t vpHeight = (tabViewportHeight - gap) / 2; + const int32_t vpOffsetX = vpWidth + gap; + const int32_t vpOffsetY = vpHeight + gap; + switch (sliceViewPlane) { + case VolumeSliceViewPlaneEnum::ALL: + viewportOut[0] = tabViewportX; + viewportOut[1] = tabViewportY; + viewportOut[2] = vpWidth; + viewportOut[3] = vpHeight; + break; + case VolumeSliceViewPlaneEnum::AXIAL: + viewportOut[0] = tabViewportX + vpOffsetX; + viewportOut[1] = tabViewportY; + viewportOut[2] = vpWidth; + viewportOut[3] = vpHeight; + break; + case VolumeSliceViewPlaneEnum::CORONAL: + viewportOut[0] = tabViewportX + vpOffsetX; + viewportOut[1] = tabViewportY + vpOffsetY; + viewportOut[2] = vpWidth; + viewportOut[3] = vpHeight; + break; + case VolumeSliceViewPlaneEnum::PARASAGITTAL: + viewportOut[0] = tabViewportX; + viewportOut[1] = tabViewportY + vpOffsetY; + viewportOut[2] = vpWidth; + viewportOut[3] = vpHeight; + break; + } + } + break; + case VolumeSliceViewAllPlanesLayoutEnum::ROW_LAYOUT: + { + const int32_t vpWidth = (tabViewportWidth - (gap * 2)) / 3; + const int32_t vpOffsetX = vpWidth + gap; + const int32_t vpHeight = tabViewportHeight; + + switch (sliceViewPlane) { + case VolumeSliceViewPlaneEnum::ALL: + viewportOut[0] = 0; + viewportOut[1] = 0; + viewportOut[2] = 0; + viewportOut[3] = 0; + break; + case VolumeSliceViewPlaneEnum::AXIAL: + viewportOut[0] = tabViewportX + (vpOffsetX * 2); + viewportOut[1] = tabViewportY; + viewportOut[2] = vpWidth; + viewportOut[3] = vpHeight; + break; + case VolumeSliceViewPlaneEnum::CORONAL: + viewportOut[0] = tabViewportX; + viewportOut[1] = tabViewportY; + viewportOut[2] = vpWidth; + viewportOut[3] = vpHeight; + break; + case VolumeSliceViewPlaneEnum::PARASAGITTAL: + viewportOut[0] = tabViewportX + vpOffsetX; + viewportOut[1] = tabViewportY; + viewportOut[2] = vpWidth; + viewportOut[3] = vpHeight; + break; + } + } + break; + } +} + +/* + * @return The slice view plane for the given viewport coordinate. + * If ALL is returned, is indicates that the given viewport coordinate + * is in the bottom left region in which volume slices are not displayed. + * + * @param viewport + * The viewport. + * @param mousePressX + * X Location of the mouse press. + * @param mousePressY + * Y Location of the mouse press. + * @param allPlanesLayout + * The layout in ALL slices view. + * @param sliceViewportOut + * Output viewport (region of graphics area) for drawing slices. + */ +VolumeSliceViewPlaneEnum::Enum +BrainOpenGLViewportContent::getSliceViewPlaneForVolumeAllSliceView(const int32_t viewport[4], + const VolumeSliceViewAllPlanesLayoutEnum::Enum allPlanesLayout, + const int32_t mousePressX, + const int32_t mousePressY, + int32_t sliceViewportOut[4]) +{ + VolumeSliceViewPlaneEnum::Enum view = VolumeSliceViewPlaneEnum::ALL; + + std::vector allSlicePlanes; + VolumeSliceViewPlaneEnum::getAllEnums(allSlicePlanes); + for (auto slicePlane : allSlicePlanes) { + BrainOpenGLViewportContent::getSliceAllViewViewport(viewport, + slicePlane, + allPlanesLayout, + sliceViewportOut); + const int32_t vpX = mousePressX - sliceViewportOut[0]; + const int32_t vpY = mousePressY - sliceViewportOut[1]; + if ((vpX >= 0) + && (vpY >= 0) + && (vpX < sliceViewportOut[2]) + && (vpY < sliceViewportOut[3])) { + return slicePlane; + } + } + + CaretLogSevere("Failed to find slice plane in all sliced view"); + return view; +} + diff --git a/src/Brain/BrainOpenGLViewportContent.h b/src/Brain/BrainOpenGLViewportContent.h index e714685f67499e45cb4238fdd14e089eaa324894..65d6e488041d3e2dd1cae24690d91083c28e5942 100644 --- a/src/Brain/BrainOpenGLViewportContent.h +++ b/src/Brain/BrainOpenGLViewportContent.h @@ -21,8 +21,10 @@ */ /*LICENSE_END*/ - #include "CaretObject.h" +#include "Matrix4x4.h" +#include "VolumeSliceViewPlaneEnum.h" +#include "VolumeSliceViewAllPlanesLayoutEnum.h" namespace caret { @@ -39,6 +41,14 @@ namespace caret { BrainOpenGLViewportContent& operator=(const BrainOpenGLViewportContent& obj); + bool getChartDataMatricesAndViewport(Matrix4x4& chartDataProjectionMatrixOut, + Matrix4x4& chartDataModelViewMatrixOut, + int chartViewportOut[4]) const; + + void setChartDataMatricesAndViewport(const Matrix4x4& chartDataProjectionMatrix, + const Matrix4x4& chartDataModelViewMatrix, + const int chartViewport[4]) const; + void getModelViewport(int modelViewportOut[4]) const; void getSurfaceMontageModelViewport(const int32_t montageX, @@ -71,11 +81,22 @@ namespace caret { const int32_t windowViewport[4], const int32_t highlightTabIndex); - static BrainOpenGLViewportContent* createViewportForSingleTab(BrowserTabContent* browserTabContent, + static BrainOpenGLViewportContent* createViewportForSingleTab(std::vector& allTabContents, + BrowserTabContent* selectedTabContent, const GapsAndMargins* gapsAndMargins, const int32_t windowIndex, const int32_t windowViewport[4]); + static void getSliceAllViewViewport(const int32_t tabViewport[4], + const VolumeSliceViewPlaneEnum::Enum sliceViewPlane, + const VolumeSliceViewAllPlanesLayoutEnum::Enum allPlanesLayout, + int32_t viewportOut[4]); + + static VolumeSliceViewPlaneEnum::Enum getSliceViewPlaneForVolumeAllSliceView(const int viewport[4], + const VolumeSliceViewAllPlanesLayoutEnum::Enum allPlanesLayout, + const int32_t mousePressX, + const int32_t mousePressY, + int sliceViewportOut[4]); private: /** * Assists with creation of the tile tab viewports @@ -122,6 +143,9 @@ namespace caret { const GapsAndMargins* gapsAndMargins, int modelViewportOut[4]); + void updateTabLockedAspectRatios(const int32_t windowIndex, + const int32_t windowViewport[4]); + const int m_windowIndex; const bool m_highlightTab; @@ -135,6 +159,21 @@ namespace caret { /** Tab viewport's Height */ int m_tabHeight; + /** Chart data viewport's X-coordinate */ + mutable int m_chartDataX; + /** Chart data viewport's Y-coordinate */ + mutable int m_chartDataY; + /** Chart data viewport's Width */ + mutable int m_chartDataWidth; + /** Chart data viewport's Height */ + mutable int m_chartDataHeight; + /** Chart data viewport's validity */ + mutable bool m_chartDataViewportValidFlag = false; + /** Chart data transformation matrix */ + mutable Matrix4x4 m_chartDataModelViewMatrix; + /** Chart data transformation matrix */ + mutable Matrix4x4 m_chartDataProjectionMatrix; + /** Model viewport's X-coordinate */ int m_modelX; /** Model viewport's Y-coordinate */ diff --git a/src/Brain/BrainOpenGLVolumeObliqueSliceDrawing.cxx b/src/Brain/BrainOpenGLVolumeObliqueSliceDrawing.cxx index ab74c319f813297ace6457986542f781d9cdc1d6..323abb28654134935e174a8b8a5e3b87b651ba05 100644 --- a/src/Brain/BrainOpenGLVolumeObliqueSliceDrawing.cxx +++ b/src/Brain/BrainOpenGLVolumeObliqueSliceDrawing.cxx @@ -26,11 +26,13 @@ #undef __BRAIN_OPEN_GL_VOLUME_OBLIQUE_SLICE_DRAWING_DECLARE__ #include "AnnotationCoordinate.h" -#include "AnnotationPointSizeText.h" +#include "AnnotationPercentSizeText.h" #include "BoundingBox.h" #include "Brain.h" #include "BrainOpenGLAnnotationDrawingFixedPipeline.h" #include "BrainOpenGLPrimitiveDrawing.h" +#include "BrainOpenGLViewportContent.h" +#include "BrainOpenGLVolumeSliceDrawing.h" #include "BrowserTabContent.h" #include "CaretAssert.h" #include "CaretLogger.h" @@ -47,6 +49,8 @@ #include "GiftiLabel.h" #include "GiftiLabelTable.h" #include "GroupAndNameHierarchyModel.h" +#include "GraphicsEngineDataOpenGL.h" +#include "GraphicsPrimitiveV3fC4f.h" #include "IdentificationWithColor.h" #include "LabelDrawingProperties.h" #include "MathFunctions.h" @@ -60,6 +64,7 @@ #include "SelectionManager.h" #include "SessionManager.h" #include "Surface.h" +#include "SurfacePlaneIntersectionToContour.h" #include "VolumeFile.h" #include "VolumeSurfaceOutlineColorOrTabModel.h" #include "VolumeSurfaceOutlineModel.h" @@ -104,6 +109,8 @@ BrainOpenGLVolumeObliqueSliceDrawing::~BrainOpenGLVolumeObliqueSliceDrawing() * Type of slice drawing (montage, single) * @param sliceProjectionType * Type of projection for the slice drawing (oblique, orthogonal) + * @param obliqueSliceMaskingType + * Masking for oblique slice drawing * @param viewport * The viewport (region of graphics area) for drawing slices. */ @@ -113,6 +120,7 @@ BrainOpenGLVolumeObliqueSliceDrawing::draw(BrainOpenGLFixedPipeline* fixedPipeli std::vector& volumeDrawInfo, const VolumeSliceDrawingTypeEnum::Enum sliceDrawingType, const VolumeSliceProjectionTypeEnum::Enum sliceProjectionType, + const VolumeSliceInterpolationEdgeEffectsMaskingEnum::Enum obliqueSliceMaskingType, const int32_t viewport[4]) { CaretAssert(sliceProjectionType == VolumeSliceProjectionTypeEnum::VOLUME_SLICE_PROJECTION_OBLIQUE); @@ -125,7 +133,7 @@ BrainOpenGLVolumeObliqueSliceDrawing::draw(BrainOpenGLFixedPipeline* fixedPipeli CaretAssert(browserTabContent); m_browserTabContent = browserTabContent; m_fixedPipelineDrawing = fixedPipelineDrawing; - + m_obliqueSliceMaskingType = obliqueSliceMaskingType; /* * No lighting for drawing slices */ @@ -138,19 +146,22 @@ BrainOpenGLVolumeObliqueSliceDrawing::draw(BrainOpenGLFixedPipeline* fixedPipeli m_brain = NULL; m_modelVolume = NULL; m_modelWholeBrain = NULL; + m_modelType = ModelTypeEnum::MODEL_TYPE_INVALID; if (m_browserTabContent->getDisplayedVolumeModel() != NULL) { m_modelVolume = m_browserTabContent->getDisplayedVolumeModel(); m_brain = m_modelVolume->getBrain(); + m_modelType = m_modelVolume->getModelType(); } else if (m_browserTabContent->getDisplayedWholeBrainModel() != NULL) { m_modelWholeBrain = m_browserTabContent->getDisplayedWholeBrainModel(); m_brain = m_modelWholeBrain->getBrain(); + m_modelType = m_modelWholeBrain->getModelType(); } else { CaretAssertMessage(0, "Invalid model for volume slice drawing."); } CaretAssert(m_brain); - + CaretAssert(m_modelType != ModelTypeEnum::MODEL_TYPE_INVALID); m_volumeDrawInfo = volumeDrawInfo; if (m_volumeDrawInfo.empty()) { @@ -158,9 +169,6 @@ BrainOpenGLVolumeObliqueSliceDrawing::draw(BrainOpenGLFixedPipeline* fixedPipeli } m_underlayVolume = m_volumeDrawInfo[0].volumeFile; - m_paletteFile = m_browserTabContent->getModelForDisplay()->getBrain()->getPaletteFile(); - CaretAssert(m_paletteFile); - const DisplayPropertiesLabels* dsl = m_brain->getDisplayPropertiesLabels(); m_displayGroup = dsl->getDisplayGroupForTab(m_fixedPipelineDrawing->windowTabIndex); @@ -194,6 +202,7 @@ BrainOpenGLVolumeObliqueSliceDrawing::draw(BrainOpenGLFixedPipeline* fixedPipeli drawVolumeSliceViewPlane(sliceDrawingType, sliceProjectionType, browserTabContent->getSliceViewPlane(), + browserTabContent->getSlicePlanesAllViewLayout(), viewport); } else if (browserTabContent->getDisplayedWholeBrainModel() != NULL) { @@ -211,32 +220,41 @@ BrainOpenGLVolumeObliqueSliceDrawing::draw(BrainOpenGLFixedPipeline* fixedPipeli * Type of projection for the slice drawing (oblique, orthogonal) * @param sliceViewPlane * The plane for slice drawing. + * @param allPlanesLayout + * The layout in ALL slices view. * @param viewport * The viewport (region of graphics area) for drawing slices. */ void BrainOpenGLVolumeObliqueSliceDrawing::drawVolumeSliceViewPlane(const VolumeSliceDrawingTypeEnum::Enum sliceDrawingType, - const VolumeSliceProjectionTypeEnum::Enum sliceProjectionType, - const VolumeSliceViewPlaneEnum::Enum sliceViewPlane, - const int32_t viewport[4]) + const VolumeSliceProjectionTypeEnum::Enum sliceProjectionType, + const VolumeSliceViewPlaneEnum::Enum sliceViewPlane, + const VolumeSliceViewAllPlanesLayoutEnum::Enum allPlanesLayout, + const int32_t viewport[4]) { switch (sliceViewPlane) { case VolumeSliceViewPlaneEnum::ALL: { - const int32_t gap = 2; + int32_t paraVP[4] = { 0, 0, 0, 0 }; + int32_t coronalVP[4] = { 0, 0, 0, 0 }; + int32_t axialVP[4] = { 0, 0, 0, 0 }; - const int32_t vpHalfX = viewport[2] / 2; - const int32_t vpHalfY = viewport[3] / 2; + BrainOpenGLViewportContent::getSliceAllViewViewport(viewport, + VolumeSliceViewPlaneEnum::PARASAGITTAL, + allPlanesLayout, + paraVP); + BrainOpenGLViewportContent::getSliceAllViewViewport(viewport, + VolumeSliceViewPlaneEnum::CORONAL, + allPlanesLayout, + coronalVP); + BrainOpenGLViewportContent::getSliceAllViewViewport(viewport, + VolumeSliceViewPlaneEnum::AXIAL, + allPlanesLayout, + axialVP); /* * Draw parasagittal slice */ - const int32_t paraVP[4] = { - viewport[0], - viewport[1] + vpHalfY + gap, - vpHalfX - gap, - vpHalfY - gap - }; glPushMatrix(); drawVolumeSliceViewType(sliceDrawingType, sliceProjectionType, @@ -248,12 +266,6 @@ BrainOpenGLVolumeObliqueSliceDrawing::drawVolumeSliceViewPlane(const VolumeSlice /* * Draw coronal slice */ - const int32_t coronalVP[4] = { - viewport[0] + vpHalfX + gap, - viewport[1] + vpHalfY + gap, - vpHalfX - gap, - vpHalfY - gap - }; glPushMatrix(); drawVolumeSliceViewType(sliceDrawingType, sliceProjectionType, @@ -265,12 +277,6 @@ BrainOpenGLVolumeObliqueSliceDrawing::drawVolumeSliceViewPlane(const VolumeSlice /* * Draw axial slice */ - const int32_t axialVP[4] = { - viewport[0] + vpHalfX + gap, - viewport[1], - vpHalfX - gap, - vpHalfY - gap - }; glPushMatrix(); drawVolumeSliceViewType(sliceDrawingType, sliceProjectionType, @@ -278,22 +284,23 @@ BrainOpenGLVolumeObliqueSliceDrawing::drawVolumeSliceViewPlane(const VolumeSlice axialVP); glPopMatrix(); - /* - * 4th quadrant is used for axis showing orientation - */ - const int32_t allVP[4] = { - viewport[0], - viewport[1], - vpHalfX - gap, - vpHalfY - gap - }; - - switch (sliceProjectionType) { - case VolumeSliceProjectionTypeEnum::VOLUME_SLICE_PROJECTION_OBLIQUE: - drawOrientationAxes(allVP); - break; - case VolumeSliceProjectionTypeEnum::VOLUME_SLICE_PROJECTION_ORTHOGONAL: - break; + if (allPlanesLayout == VolumeSliceViewAllPlanesLayoutEnum::GRID_LAYOUT) { + /* + * 4th quadrant is used for axis showing orientation + */ + int32_t allVP[4] = { 0, 0, 0, 0 }; + BrainOpenGLViewportContent::getSliceAllViewViewport(viewport, + VolumeSliceViewPlaneEnum::ALL, + allPlanesLayout, + allVP); + + switch (sliceProjectionType) { + case VolumeSliceProjectionTypeEnum::VOLUME_SLICE_PROJECTION_OBLIQUE: + drawOrientationAxes(allVP); + break; + case VolumeSliceProjectionTypeEnum::VOLUME_SLICE_PROJECTION_ORTHOGONAL: + break; + } } } break; @@ -301,9 +308,9 @@ BrainOpenGLVolumeObliqueSliceDrawing::drawVolumeSliceViewPlane(const VolumeSlice case VolumeSliceViewPlaneEnum::CORONAL: case VolumeSliceViewPlaneEnum::PARASAGITTAL: drawVolumeSliceViewType(sliceDrawingType, - sliceProjectionType, - sliceViewPlane, - viewport); + sliceProjectionType, + sliceViewPlane, + viewport); break; } } @@ -457,8 +464,7 @@ BrainOpenGLVolumeObliqueSliceDrawing::drawVolumeSliceViewTypeMontage(const Volum const int32_t numCols = m_browserTabContent->getMontageNumberOfColumns(); CaretAssert(numCols > 0); - const CaretPreferences* caretPreferences = SessionManager::get()->getCaretPreferences(); - const int32_t montageCoordPrecision = caretPreferences->getVolumeMontageCoordinatePrecision(); + const int32_t montageCoordPrecision = m_browserTabContent->getVolumeMontageCoordinatePrecision(); const GapsAndMargins* gapsAndMargins = m_brain->getGapsAndMargins(); // const int32_t horizontalMargin = static_cast(viewport[2] * gapsAndMargins->getVolumeMontageHorizontalGap()); @@ -549,7 +555,7 @@ BrainOpenGLVolumeObliqueSliceDrawing::drawVolumeSliceViewTypeMontage(const Volum uint8_t backgroundRGBA[4]; prefs->getBackgroundAndForegroundColors()->getColorBackgroundVolumeView(backgroundRGBA); backgroundRGBA[3] = 255; - const bool showCoordinates = prefs->isVolumeMontageAxesCoordinatesDisplayed(); + const bool showCoordinates = m_browserTabContent->isVolumeMontageAxesCoordinatesDisplayed(); /* * Determine a slice offset to selected slices is in @@ -615,13 +621,12 @@ BrainOpenGLVolumeObliqueSliceDrawing::drawVolumeSliceViewTypeMontage(const Volum if (showCoordinates) { const AString coordText = (axisLetter + "=" - + AString::number(sliceCoord, 'f', montageCoordPrecision) - + "mm"); + + AString::number(sliceCoord, 'f', montageCoordPrecision)); - AnnotationPointSizeText annotationText(AnnotationAttributesDefaultTypeEnum::NORMAL); + AnnotationPercentSizeText annotationText(AnnotationAttributesDefaultTypeEnum::NORMAL); annotationText.setHorizontalAlignment(AnnotationTextAlignHorizontalEnum::RIGHT); annotationText.setVerticalAlignment(AnnotationTextAlignVerticalEnum::BOTTOM); - annotationText.setFontPointSize(AnnotationTextFontPointSizeEnum::SIZE12); + annotationText.setFontPercentViewportSize(10.0f); annotationText.setLineColor(CaretColorEnum::NONE); annotationText.setTextColor(CaretColorEnum::CUSTOM); annotationText.setBackgroundColor(CaretColorEnum::CUSTOM); @@ -643,8 +648,7 @@ BrainOpenGLVolumeObliqueSliceDrawing::drawVolumeSliceViewTypeMontage(const Volum */ glViewport(viewport[0], viewport[1], viewport[2], viewport[3]); - - if (prefs->isVolumeAxesLabelsDisplayed()) { + if (m_browserTabContent->isVolumeAxesCrosshairLabelsDisplayed()) { drawAxesCrosshairsOrthoAndOblique(sliceProjectionType, sliceViewPlane, sliceCoordinates, @@ -850,10 +854,8 @@ BrainOpenGLVolumeObliqueSliceDrawing::drawVolumeSliceViewProjection(const Volume BrainOpenGLAnnotationDrawingFixedPipeline::Inputs inputs(this->m_brain, m_fixedPipelineDrawing->mode, BrainOpenGLFixedPipeline::s_gluLookAtCenterFromEyeOffsetDistance, - m_fixedPipelineDrawing->m_tabViewport, m_fixedPipelineDrawing->m_windowIndex, m_fixedPipelineDrawing->windowTabIndex, - BrainOpenGLAnnotationDrawingFixedPipeline::Inputs::TEXT_HEIGHT_USE_OPENGL_VIEWPORT_HEIGHT, BrainOpenGLAnnotationDrawingFixedPipeline::Inputs::WINDOW_DRAWING_NO); m_fixedPipelineDrawing->m_annotationDrawing->drawModelSpaceAnnotationsOnVolumeSlice(&inputs, slicePlane, @@ -1207,7 +1209,8 @@ BrainOpenGLVolumeObliqueSliceDrawing::drawObliqueSlice(const VolumeSliceViewPlan std::vector volumeSlices; for (int32_t i = 0; i < numVolumes; i++) { volumeSlices.push_back(VolumeSlice(m_volumeDrawInfo[i].volumeFile, - m_volumeDrawInfo[i].mapIndex)); + m_volumeDrawInfo[i].mapIndex, + m_volumeDrawInfo[i].opacity)); } bool showFirstVoxelCoordFlag = debugFlag; @@ -1323,9 +1326,9 @@ BrainOpenGLVolumeObliqueSliceDrawing::drawObliqueSlice(const VolumeSliceViewPlan }; const float voxelCenter[3] = { - (bottomLeftVoxelCoord[0] + topRightVoxelCoord[0]) * 0.5, - (bottomLeftVoxelCoord[1] + topRightVoxelCoord[1]) * 0.5, - (bottomLeftVoxelCoord[2] + topRightVoxelCoord[2]) * 0.5 + (float)((bottomLeftVoxelCoord[0] + topRightVoxelCoord[0]) * 0.5), + (float)((bottomLeftVoxelCoord[1] + topRightVoxelCoord[1]) * 0.5), + (float)((bottomLeftVoxelCoord[2] + topRightVoxelCoord[2]) * 0.5) }; @@ -1446,6 +1449,43 @@ BrainOpenGLVolumeObliqueSliceDrawing::drawObliqueSlice(const VolumeSliceViewPlan VolumeFile::CUBIC, &valueValidFlag, vdi.mapIndex); + + if (valueValidFlag + && (values[0] != 0.0f)) { + /* + * Apply masking to oblique voxels (WB-750). + * In some instances, CUBIC interpolation may result in a voxel + * receiving a very small value (0.000000000000000210882405) and + * this will cause the slice drawing to look very unusual. Masking + * is used to prevent this from occurring. + * + */ + bool maskValidFlag = false; + float maskValue = 0.0f; + switch (m_obliqueSliceMaskingType) { + case VolumeSliceInterpolationEdgeEffectsMaskingEnum::OFF: + maskValidFlag = false; + break; + case VolumeSliceInterpolationEdgeEffectsMaskingEnum::LOOSE: + maskValue = volumeFile->interpolateValue(voxelCenter, + VolumeFile::TRILINEAR, + &maskValidFlag, + vdi.mapIndex); + break; + case VolumeSliceInterpolationEdgeEffectsMaskingEnum::TIGHT: + maskValue = volumeFile->interpolateValue(voxelCenter, + VolumeFile::ENCLOSING_VOXEL, + &maskValidFlag, + vdi.mapIndex); + break; + } + + if (maskValidFlag + && (maskValue == 0.0f)) { + values[0] = 0.0f; + valueValidFlag = false; + } + } } else if (ciftiMappableFile != NULL) { const int64_t voxelOffset = ciftiMappableFile->getMapDataOffsetForVoxelAtCoordinate(voxelCenter, @@ -1584,22 +1624,14 @@ BrainOpenGLVolumeObliqueSliceDrawing::drawObliqueSlice(const VolumeSliceViewPlan } else if (mappableFile->isMappedWithPalette()) { const PaletteColorMapping* paletteColorMapping = mappableFile->getMapPaletteColorMapping(mapIndex); - const AString paletteName = paletteColorMapping->getSelectedPaletteName(); - const Palette* palette = m_paletteFile->getPaletteByName(paletteName); - if (palette != NULL) { - CaretAssertVectorIndex(m_volumeDrawInfo, i); - NodeAndVoxelColoring::colorScalarsWithPalette(m_volumeDrawInfo[i].statistics, - paletteColorMapping, - palette, - values, - values, - numValues, - rgba); - } - else { - CaretLogWarning("Missing palette named: " - + paletteName); - } + CaretAssertVectorIndex(m_volumeDrawInfo, i); + NodeAndVoxelColoring::colorScalarsWithPalette(m_volumeDrawInfo[i].statistics, + paletteColorMapping, + values, + paletteColorMapping, + values, + numValues, + rgba); } else if (mappableFile->isMappedWithLabelTable()) { GiftiLabelTable* labelTable = mappableFile->getMapLabelTable(mapIndex); @@ -1678,12 +1710,12 @@ BrainOpenGLVolumeObliqueSliceDrawing::drawObliqueSlice(const VolumeSliceViewPlan const uint8_t* rgba = volumeSlices[sliceIndex].getRgbaForValueByIndex(voxelOffset); if (rgba[3] > 0) { - voxelRGBA[0] = rgba[0]; - voxelRGBA[1] = rgba[1]; - voxelRGBA[2] = rgba[2]; - voxelRGBA[3] = rgba[3]; - if (m_identificationModeFlag) { + voxelRGBA[0] = rgba[0]; + voxelRGBA[1] = rgba[1]; + voxelRGBA[2] = rgba[2]; + voxelRGBA[3] = rgba[3]; + VolumeMappableInterface* volMap = volumeSlices[sliceIndex].m_volumeMappableInterface; int64_t voxelI, voxelJ, voxelK; volMap->enclosingVoxel(vtd->m_center[0], vtd->m_center[1], vtd->m_center[2], @@ -1701,6 +1733,40 @@ BrainOpenGLVolumeObliqueSliceDrawing::drawObliqueSlice(const VolumeSliceViewPlan voxelRGBA); } } + else { + /* + * In oblique drawing, each drawn "voxel" is composited from + * an interpolated voxel from each slice. Therefore, we must + * do our own alpha (transparency) processing. + */ + bool noAlphaFlag = true; + if (iSlice > 0) { + float alpha = volumeSlices[sliceIndex].m_opacity; + if (alpha < 1.0f) { + if ((voxelRGBA[3] > 0) + && (rgba[3] > 0)) { + const float oneMinusAlpha = 1.0f - alpha; + voxelRGBA[0] = static_cast((rgba[0] * alpha) + (voxelRGBA[0] * oneMinusAlpha)); + voxelRGBA[1] = static_cast((rgba[1] * alpha) + (voxelRGBA[1] * oneMinusAlpha)); + voxelRGBA[2] = static_cast((rgba[2] * alpha) + (voxelRGBA[2] * oneMinusAlpha)); + voxelRGBA[3] = 255; + noAlphaFlag = false; + } + } + } + + if (volumeEditingDrawAllVoxelsFlag + && (volumeSlices[sliceIndex].m_volumeFile == voxelEditingVolumeFile)) { + noAlphaFlag = true; + } + + if (noAlphaFlag) { + voxelRGBA[0] = rgba[0]; + voxelRGBA[1] = rgba[1]; + voxelRGBA[2] = rgba[2]; + voxelRGBA[3] = rgba[3]; + } + } } } @@ -1955,8 +2021,7 @@ BrainOpenGLVolumeObliqueSliceDrawing::drawOrthogonalSlice(const VolumeSliceViewP * Get colors for all voxels in the slice. */ const int64_t validVoxelCount = - volumeFile->getVoxelColorsForSliceInMap(m_brain->getPaletteFile(), - mapIndex, + volumeFile->getVoxelColorsForSliceInMap(mapIndex, sliceViewPlane, sliceIndexForDrawing, displayGroup, @@ -2018,9 +2083,9 @@ BrainOpenGLVolumeObliqueSliceDrawing::drawOrthogonalSlice(const VolumeSliceViewP * Setup for drawing the voxels in the slice. */ float startCoordinate[3] = { - originX - (voxelStepX / 2.0), - originY - (voxelStepY / 2.0), - originZ - (voxelStepZ / 2.0) + originX - (voxelStepX / 2.0f), + originY - (voxelStepY / 2.0f), + originZ - (voxelStepZ / 2.0f) }; float rowStep[3] = { @@ -2267,8 +2332,7 @@ BrainOpenGLVolumeObliqueSliceDrawing::drawOrthogonalSliceWithCulling(const Volum }; const int64_t validVoxelCount = - volumeFile->getVoxelColorsForSubSliceInMap(m_brain->getPaletteFile(), - mapIndex, + volumeFile->getVoxelColorsForSubSliceInMap(mapIndex, sliceViewPlane, sliceIndexForDrawing, culledFirstVoxelIJK, @@ -2325,9 +2389,9 @@ BrainOpenGLVolumeObliqueSliceDrawing::drawOrthogonalSliceWithCulling(const Volum * Setup for drawing the voxels in the slice. */ float startCoordinate[3] = { - firstVoxelXYZ[0] - (voxelStepX / 2.0), - firstVoxelXYZ[1] - (voxelStepY / 2.0), - firstVoxelXYZ[2] - (voxelStepZ / 2.0) + firstVoxelXYZ[0] - (voxelStepX / 2.0f), + firstVoxelXYZ[1] - (voxelStepY / 2.0f), + firstVoxelXYZ[2] - (voxelStepZ / 2.0f) }; float rowStep[3] = { @@ -2644,7 +2708,10 @@ BrainOpenGLVolumeObliqueSliceDrawing::drawLayers(const VolumeSliceDrawingTypeEnu glPolygonOffset(0.0, 1.0); if (drawOutlineFlag) { - drawSurfaceOutline(slicePlane); + BrainOpenGLVolumeSliceDrawing::drawSurfaceOutline(m_modelType, + slicePlane, + m_browserTabContent->getVolumeSurfaceOutlineSet(), + m_fixedPipelineDrawing); } if (drawFibersFlag) { @@ -2697,109 +2764,6 @@ BrainOpenGLVolumeObliqueSliceDrawing::drawLayers(const VolumeSliceDrawingTypeEnu } } -/** - * Draw surface outlines on the volume slices - * - * @param plane - * Plane of the volume slice on which surface outlines are drawn. - */ -void -BrainOpenGLVolumeObliqueSliceDrawing::drawSurfaceOutline(const Plane& plane) -{ - if ( ! plane.isValidPlane()) { - return; - } - - float intersectionPoint1[3]; - float intersectionPoint2[3]; - - m_fixedPipelineDrawing->enableLineAntiAliasing(); - - VolumeSurfaceOutlineSetModel* outlineSet = m_browserTabContent->getVolumeSurfaceOutlineSet(); - - /* - * Process each surface outline - */ - const int32_t numberOfOutlines = outlineSet->getNumberOfDislayedVolumeSurfaceOutlines(); - for (int io = 0; - io < numberOfOutlines; - io++) { - VolumeSurfaceOutlineModel* outline = outlineSet->getVolumeSurfaceOutlineModel(io); - if (outline->isDisplayed()) { - Surface* surface = outline->getSurface(); - if (surface != NULL) { - const float thickness = outline->getThickness(); - - int numTriangles = surface->getNumberOfTriangles(); - - CaretColorEnum::Enum outlineColor = CaretColorEnum::BLACK; - int32_t colorSourceBrowserTabIndex = -1; - - VolumeSurfaceOutlineColorOrTabModel* colorOrTabModel = outline->getColorOrTabModel(); - VolumeSurfaceOutlineColorOrTabModel::Item* selectedColorOrTabItem = colorOrTabModel->getSelectedItem(); - switch (selectedColorOrTabItem->getItemType()) { - case VolumeSurfaceOutlineColorOrTabModel::Item::ITEM_TYPE_BROWSER_TAB: - colorSourceBrowserTabIndex = selectedColorOrTabItem->getBrowserTabIndex(); - break; - case VolumeSurfaceOutlineColorOrTabModel::Item::ITEM_TYPE_COLOR: - outlineColor = selectedColorOrTabItem->getColor(); - break; - } - const bool surfaceColorFlag = (colorSourceBrowserTabIndex >= 0); - - float* nodeColoringRGBA = NULL; - if (surfaceColorFlag) { - nodeColoringRGBA = m_fixedPipelineDrawing->surfaceNodeColoring->colorSurfaceNodes(NULL, - surface, - colorSourceBrowserTabIndex); - } - - glColor3fv(CaretColorEnum::toRGB(outlineColor)); - m_fixedPipelineDrawing->setLineWidth(thickness); - - /* - * Examine each triangle to see if it intersects the Plane - * in which the slice exists. - */ - glBegin(GL_LINES); - for (int it = 0; it < numTriangles; it++) { - const int32_t* triangleNodes = surface->getTriangle(it); - const float* c1 = surface->getCoordinate(triangleNodes[0]); - const float* c2 = surface->getCoordinate(triangleNodes[1]); - const float* c3 = surface->getCoordinate(triangleNodes[2]); - - if (plane.triangleIntersectPlane(c1, c2, c3, - intersectionPoint1, - intersectionPoint2)) { - if (surfaceColorFlag) { - /* - * Use coloring assigned to the first node in the triangle - * but only if Alpha is valid (greater than zero). - */ - const int64_t colorIndex = triangleNodes[0] * 4; - if (nodeColoringRGBA[colorIndex + 3] > 0.0) { - glColor3fv(&nodeColoringRGBA[triangleNodes[0] * 4]); - } - else { - continue; - } - } - - /* - * Draw the line where the triangle intersections the slice - */ - glVertex3fv(intersectionPoint1); - glVertex3fv(intersectionPoint2); - } - } - glEnd(); - } - } - } - - m_fixedPipelineDrawing->disableLineAntiAliasing(); -} - /** * Draw foci on volume slice. * @@ -2858,7 +2822,7 @@ BrainOpenGLVolumeObliqueSliceDrawing::drawVolumeSliceFoci(const Plane& plane) const CaretColorEnum::Enum caretColor = fociDisplayProperties->getStandardColorType(displayGroup, m_fixedPipelineDrawing->windowTabIndex); float caretColorRGBA[4]; - CaretColorEnum::toRGBFloat(caretColor, caretColorRGBA); + CaretColorEnum::toRGBAFloat(caretColor, caretColorRGBA); bool drawAsSpheres = false; switch (fociDisplayProperties->getDrawingType(displayGroup, @@ -3034,9 +2998,8 @@ BrainOpenGLVolumeObliqueSliceDrawing::drawAxesCrosshairs(const VolumeSliceProjec const VolumeSliceViewPlaneEnum::Enum sliceViewPlane, const float sliceCoordinates[3]) { - CaretPreferences* prefs = SessionManager::get()->getCaretPreferences(); - const bool drawCrosshairsFlag = prefs->isVolumeAxesCrosshairsDisplayed(); - bool drawCrosshairLabelsFlag = prefs->isVolumeAxesLabelsDisplayed(); + const bool drawCrosshairsFlag = m_browserTabContent->isVolumeAxesCrosshairsDisplayed(); + bool drawCrosshairLabelsFlag = m_browserTabContent->isVolumeAxesCrosshairLabelsDisplayed(); switch (sliceDrawingType) { case VolumeSliceDrawingTypeEnum::VOLUME_SLICE_DRAW_MONTAGE: @@ -3285,25 +3248,29 @@ BrainOpenGLVolumeObliqueSliceDrawing::drawAxesCrosshairsOrthoAndOblique(const Vo break; } + /* + * Offset text labels be a percentage of viewort width/height + */ GLint viewport[4]; glGetIntegerv(GL_VIEWPORT, viewport); - const int textOffset = 15; + const int textOffsetX = viewport[2] * 0.01f; + const int textOffsetY = viewport[3] * 0.01f; const int textLeftWindowXY[2] = { - textOffset, + textOffsetX, (viewport[3] / 2) }; const int textRightWindowXY[2] = { - viewport[2] - textOffset, + viewport[2] - textOffsetX, (viewport[3] / 2) }; const int textBottomWindowXY[2] = { viewport[2] / 2, - textOffset + textOffsetY }; const int textTopWindowXY[2] = { (viewport[2] / 2), - viewport[3] - textOffset + viewport[3] - textOffsetY }; /* @@ -3311,23 +3278,21 @@ BrainOpenGLVolumeObliqueSliceDrawing::drawAxesCrosshairsOrthoAndOblique(const Vo */ if (drawCrosshairsFlag) { glPushMatrix(); - glLineWidth(1.0); glTranslatef(horizTrans[0], horizTrans[1], horizTrans[2]); - glColor3fv(horizontalAxisRGBA); - glBegin(GL_LINES); - glVertex3fv(horizontalAxisStartXYZ); - glVertex3fv(horizontalAxisEndXYZ); - glEnd(); + std::unique_ptr horizHairPrimitive(GraphicsPrimitive::newPrimitiveV3fC4f(GraphicsPrimitive::PrimitiveType::POLYGONAL_LINES)); + horizHairPrimitive->addVertex(horizontalAxisStartXYZ, horizontalAxisRGBA); + horizHairPrimitive->addVertex(horizontalAxisEndXYZ, horizontalAxisRGBA); + horizHairPrimitive->setLineWidth(GraphicsPrimitive::LineWidthType::PIXELS, 2.0f); + GraphicsEngineDataOpenGL::draw(horizHairPrimitive.get()); glPopMatrix(); glPushMatrix(); - glLineWidth(1.0); glTranslatef(vertTrans[0], vertTrans[1], vertTrans[2]); - glColor3fv(verticalAxisRGBA); - glBegin(GL_LINES); - glVertex3fv(verticalAxisStartXYZ); - glVertex3fv(verticalAxisEndXYZ); - glEnd(); + std::unique_ptr vertHairPrimitive(GraphicsPrimitive::newPrimitiveV3fC4f(GraphicsPrimitive::PrimitiveType::POLYGONAL_LINES)); + vertHairPrimitive->addVertex(verticalAxisStartXYZ, verticalAxisRGBA); + vertHairPrimitive->addVertex(verticalAxisEndXYZ, verticalAxisRGBA); + vertHairPrimitive->setLineWidth(GraphicsPrimitive::LineWidthType::PIXELS, 2.0f); + GraphicsEngineDataOpenGL::draw(vertHairPrimitive.get()); glPopMatrix(); } @@ -3443,26 +3408,30 @@ BrainOpenGLVolumeObliqueSliceDrawing::drawAxesCrosshairsOrthoAndOblique(const Vo savedViewport[2], savedViewport[3]); - AnnotationPointSizeText annotationText(AnnotationAttributesDefaultTypeEnum::NORMAL); - annotationText.setHorizontalAlignment(AnnotationTextAlignHorizontalEnum::CENTER); - annotationText.setVerticalAlignment(AnnotationTextAlignVerticalEnum::MIDDLE); + AnnotationPercentSizeText annotationText(AnnotationAttributesDefaultTypeEnum::NORMAL); annotationText.setBoldStyleEnabled(true); - annotationText.setFontPointSize(fontSize); + annotationText.setFontPercentViewportSize(5.0f); annotationText.setTextColor(CaretColorEnum::CUSTOM); annotationText.setBackgroundColor(CaretColorEnum::CUSTOM); annotationText.setCustomTextColor(horizontalAxisRGBA); annotationText.setCustomBackgroundColor(backgroundRGBA); + annotationText.setHorizontalAlignment(AnnotationTextAlignHorizontalEnum::LEFT); + annotationText.setVerticalAlignment(AnnotationTextAlignVerticalEnum::MIDDLE); annotationText.setText(horizontalLeftText); m_fixedPipelineDrawing->drawTextAtViewportCoords(textLeftWindowXY[0], textLeftWindowXY[1], annotationText); annotationText.setText(horizontalRightText); + annotationText.setHorizontalAlignment(AnnotationTextAlignHorizontalEnum::RIGHT); + annotationText.setVerticalAlignment(AnnotationTextAlignVerticalEnum::MIDDLE); m_fixedPipelineDrawing->drawTextAtViewportCoords(textRightWindowXY[0], textRightWindowXY[1], annotationText); + annotationText.setHorizontalAlignment(AnnotationTextAlignHorizontalEnum::CENTER); + annotationText.setVerticalAlignment(AnnotationTextAlignVerticalEnum::BOTTOM); annotationText.setCustomTextColor(verticalAxisRGBA); annotationText.setText(verticalBottomText); m_fixedPipelineDrawing->drawTextAtViewportCoords(textBottomWindowXY[0], @@ -3470,6 +3439,8 @@ BrainOpenGLVolumeObliqueSliceDrawing::drawAxesCrosshairsOrthoAndOblique(const Vo annotationText); annotationText.setText(verticalTopText); + annotationText.setHorizontalAlignment(AnnotationTextAlignHorizontalEnum::CENTER); + annotationText.setVerticalAlignment(AnnotationTextAlignVerticalEnum::TOP); annotationText.getCoordinate()->setXYZ(textTopWindowXY[0], textTopWindowXY[1], 0.0); m_fixedPipelineDrawing->drawTextAtViewportCoords(textTopWindowXY[0], textTopWindowXY[1], @@ -3600,9 +3571,8 @@ BrainOpenGLVolumeObliqueSliceDrawing::getMinMaxVoxelSpacing(const VolumeMappable void BrainOpenGLVolumeObliqueSliceDrawing::drawOrientationAxes(const int viewport[4]) { - CaretPreferences* prefs = SessionManager::get()->getCaretPreferences(); - const bool drawCylindersFlag = prefs->isVolumeAxesCrosshairsDisplayed(); - const bool drawLabelsFlag = prefs->isVolumeAxesLabelsDisplayed(); + const bool drawCylindersFlag = m_browserTabContent->isVolumeAxesCrosshairsDisplayed(); + const bool drawLabelsFlag = m_browserTabContent->isVolumeAxesCrosshairLabelsDisplayed(); /* * Set the viewport @@ -3707,34 +3677,41 @@ BrainOpenGLVolumeObliqueSliceDrawing::drawOrientationAxes(const int viewport[4]) const double textMinCoord = -textMaxCoord; - const float axialPlaneMin[3] = { 0.0, 0.0, axisMinCoord }; - const float axialPlaneMax[3] = { 0.0, 0.0, axisMaxCoord }; - const double axialTextMin[3] = { 0.0, 0.0, textMinCoord }; - const double axialTextMax[3] = { 0.0, 0.0, textMaxCoord }; + const float axialPlaneMin[3] = { 0.0, 0.0, (float)axisMinCoord }; + const float axialPlaneMax[3] = { 0.0, 0.0, (float)axisMaxCoord }; + const double axialTextMin[3] = { 0.0, 0.0, (float)textMinCoord }; + const double axialTextMax[3] = { 0.0, 0.0, (float)textMaxCoord }; - const float coronalPlaneMin[3] = { axisMinCoord, 0.0, 0.0 }; - const float coronalPlaneMax[3] = { axisMaxCoord, 0.0, 0.0 }; - const double coronalTextMin[3] = { textMinCoord, 0.0, 0.0 }; - const double coronalTextMax[3] = { textMaxCoord, 0.0, 0.0 }; + const float coronalPlaneMin[3] = { (float)axisMinCoord, 0.0, 0.0 }; + const float coronalPlaneMax[3] = { (float)axisMaxCoord, 0.0, 0.0 }; + const double coronalTextMin[3] = { (float)textMinCoord, 0.0, 0.0 }; + const double coronalTextMax[3] = { (float)textMaxCoord, 0.0, 0.0 }; - const float paraPlaneMin[3] = { 0.0, axisMinCoord, 0.0 }; - const float paraPlaneMax[3] = { 0.0, axisMaxCoord, 0.0 }; - const double paraTextMin[3] = { 0.0, textMinCoord, 0.0 }; - const double paraTextMax[3] = { 0.0, textMaxCoord, 0.0 }; + const float paraPlaneMin[3] = { 0.0, (float)axisMinCoord, 0.0 }; + const float paraPlaneMax[3] = { 0.0, (float)axisMaxCoord, 0.0 }; + const double paraTextMin[3] = { 0.0, (float)textMinCoord, 0.0 }; + const double paraTextMax[3] = { 0.0, (float)textMaxCoord, 0.0 }; - const float axesCrosshairRadius = 1.0; + /* + * Set radius as percentage of viewport height + */ + float axesCrosshairRadius = 1.0; + if (viewportHeight > 0) { + const float percentageRadius = 0.005f; + axesCrosshairRadius = percentageRadius * viewportHeight; + } if (drawCylindersFlag) { m_fixedPipelineDrawing->drawCylinder(blue, axialPlaneMin, axialPlaneMax, - axesCrosshairRadius); + axesCrosshairRadius * 0.5f); } - AnnotationPointSizeText annotationText(AnnotationAttributesDefaultTypeEnum::NORMAL); + AnnotationPercentSizeText annotationText(AnnotationAttributesDefaultTypeEnum::NORMAL); annotationText.setHorizontalAlignment(AnnotationTextAlignHorizontalEnum::CENTER); annotationText.setVerticalAlignment(AnnotationTextAlignVerticalEnum::MIDDLE); - annotationText.setFontPointSize(AnnotationTextFontPointSizeEnum::SIZE14); + annotationText.setFontPercentViewportSize(5.0f); annotationText.setCoordinateSpace(AnnotationCoordinateSpaceEnum::STEREOTAXIC); annotationText.setTextColor(CaretColorEnum::CUSTOM); @@ -3753,7 +3730,7 @@ BrainOpenGLVolumeObliqueSliceDrawing::drawOrientationAxes(const int viewport[4]) m_fixedPipelineDrawing->drawCylinder(green, coronalPlaneMin, coronalPlaneMax, - axesCrosshairRadius); + axesCrosshairRadius * 0.5f); } if (drawLabelsFlag) { @@ -3771,7 +3748,7 @@ BrainOpenGLVolumeObliqueSliceDrawing::drawOrientationAxes(const int viewport[4]) m_fixedPipelineDrawing->drawCylinder(red, paraPlaneMin, paraPlaneMax, - axesCrosshairRadius); + axesCrosshairRadius * 0.5f); } if (drawLabelsFlag) { @@ -5194,7 +5171,8 @@ BrainOpenGLVolumeObliqueSliceDrawing::getVolumeDrawingViewDependentCulling(const * Index of selected map. */ BrainOpenGLVolumeObliqueSliceDrawing::VolumeSlice::VolumeSlice(VolumeMappableInterface* volumeMappableInterface, - const int32_t mapIndex) + const int32_t mapIndex, + const float opacity) { m_volumeMappableInterface = volumeMappableInterface; m_volumeFile = dynamic_cast(m_volumeMappableInterface); @@ -5204,6 +5182,9 @@ BrainOpenGLVolumeObliqueSliceDrawing::VolumeSlice::VolumeSlice(VolumeMappableInt m_mapIndex = mapIndex; CaretAssert(m_mapIndex >= 0); + m_opacity = opacity; + CaretAssert((m_opacity >= 0.0f) && (m_opacity <= 1.0f)); + const int64_t sliceDim = 300; const int64_t numVoxels = sliceDim * sliceDim; m_values.reserve(numVoxels); diff --git a/src/Brain/BrainOpenGLVolumeObliqueSliceDrawing.h b/src/Brain/BrainOpenGLVolumeObliqueSliceDrawing.h index 5dcf0e9a3895db9040dcee5c7c6815b5f8a96202..2258f87571b1fb85bb5c7ada73d95b231f547c47 100644 --- a/src/Brain/BrainOpenGLVolumeObliqueSliceDrawing.h +++ b/src/Brain/BrainOpenGLVolumeObliqueSliceDrawing.h @@ -24,11 +24,13 @@ #include "BrainOpenGLFixedPipeline.h" #include "CaretObject.h" #include "DisplayGroupEnum.h" +#include "ModelTypeEnum.h" +#include "VolumeSliceInterpolationEdgeEffectsMaskingEnum.h" #include "VolumeSliceProjectionTypeEnum.h" #include "VolumeSliceDrawingTypeEnum.h" +#include "VolumeSliceViewAllPlanesLayoutEnum.h" #include "VolumeSliceViewPlaneEnum.h" - namespace caret { class Brain; @@ -37,7 +39,6 @@ namespace caret { class Matrix4x4; class ModelVolume; class ModelWholeBrain; - class PaletteFile; class Plane; class VolumeMappableInterface; @@ -53,6 +54,7 @@ namespace caret { std::vector& volumeDrawInfo, const VolumeSliceDrawingTypeEnum::Enum sliceDrawingType, const VolumeSliceProjectionTypeEnum::Enum sliceProjectionType, + const VolumeSliceInterpolationEdgeEffectsMaskingEnum::Enum obliqueSliceMaskingType, const int32_t viewport[4]); // ADD_NEW_METHODS_HERE @@ -72,7 +74,8 @@ namespace caret { * Volume that contains the data values. */ VolumeSlice(VolumeMappableInterface* volumeMappableInterface, - const int32_t mapIndex); + const int32_t mapIndex, + const float opacity); /** * Add a value and return its index. @@ -130,6 +133,11 @@ namespace caret { */ int32_t m_mapIndex; + /** + * Opacity + */ + float m_opacity; + /** * The voxel values for single scalar or red if RGBA volume */ @@ -197,6 +205,7 @@ namespace caret { void drawVolumeSliceViewPlane(const VolumeSliceDrawingTypeEnum::Enum sliceDrawingType, const VolumeSliceProjectionTypeEnum::Enum sliceProjectionType, const VolumeSliceViewPlaneEnum::Enum sliceViewPlane, + const VolumeSliceViewAllPlanesLayoutEnum::Enum allPlanesLayout, const int32_t viewport[4]); void drawVolumeSliceViewType(const VolumeSliceDrawingTypeEnum::Enum sliceDrawingType, @@ -252,7 +261,7 @@ namespace caret { const Plane& slicePlane, const float sliceCoordinates[3]); - void drawSurfaceOutline(const Plane& plane); +// void drawSurfaceOutline(const Plane& plane); void drawVolumeSliceFoci(const Plane& plane); @@ -338,6 +347,8 @@ namespace caret { ModelWholeBrain* m_modelWholeBrain; + ModelTypeEnum::Enum m_modelType; + VolumeMappableInterface* m_underlayVolume; Brain* m_brain; @@ -350,8 +361,6 @@ namespace caret { BrowserTabContent* m_browserTabContent; - PaletteFile* m_paletteFile; - DisplayGroupEnum::Enum m_displayGroup; int32_t m_tabIndex; @@ -362,6 +371,8 @@ namespace caret { double m_orthographicBounds[6]; + VolumeSliceInterpolationEdgeEffectsMaskingEnum::Enum m_obliqueSliceMaskingType = VolumeSliceInterpolationEdgeEffectsMaskingEnum::OFF; + std::vector m_identificationIndices; bool m_identificationModeFlag; diff --git a/src/Brain/BrainOpenGLVolumeSliceDrawing.cxx b/src/Brain/BrainOpenGLVolumeSliceDrawing.cxx index 3725a1062f1a1ccfb83fc26f5f93d737c4fcf412..432d4541544bc4e6bfcbac98408df31212628258 100644 --- a/src/Brain/BrainOpenGLVolumeSliceDrawing.cxx +++ b/src/Brain/BrainOpenGLVolumeSliceDrawing.cxx @@ -26,11 +26,13 @@ #undef __BRAIN_OPEN_GL_VOLUME_SLICE_DRAWING_DECLARE__ #include "AnnotationCoordinate.h" +#include "AnnotationPercentSizeText.h" #include "AnnotationPointSizeText.h" #include "BoundingBox.h" #include "Brain.h" #include "BrainOpenGLAnnotationDrawingFixedPipeline.h" #include "BrainOpenGLPrimitiveDrawing.h" +#include "BrainOpenGLViewportContent.h" #include "BrainordinateRegionOfInterest.h" #include "BrowserTabContent.h" #include "CaretAssert.h" @@ -47,6 +49,9 @@ #include "GapsAndMargins.h" #include "GiftiLabel.h" #include "GiftiLabelTable.h" +#include "GraphicsEngineDataOpenGL.h" +#include "GraphicsPrimitiveV3fC4f.h" +#include "GraphicsUtilitiesOpenGL.h" #include "GroupAndNameHierarchyModel.h" #include "IdentificationManager.h" #include "IdentificationWithColor.h" @@ -63,6 +68,7 @@ #include "SelectionItemVoxelIdentificationSymbol.h" #include "SelectionManager.h" #include "SessionManager.h" +#include "SurfacePlaneIntersectionToContour.h" #include "Surface.h" #include "VolumeFile.h" #include "VolumeSurfaceOutlineColorOrTabModel.h" @@ -123,6 +129,7 @@ BrainOpenGLVolumeSliceDrawing::draw(BrainOpenGLFixedPipeline* fixedPipelineDrawi return; } + CaretAssert(sliceProjectionType == VolumeSliceProjectionTypeEnum::VOLUME_SLICE_PROJECTION_ORTHOGONAL); CaretAssert(fixedPipelineDrawing); CaretAssert(browserTabContent); m_browserTabContent = browserTabContent; @@ -140,18 +147,22 @@ BrainOpenGLVolumeSliceDrawing::draw(BrainOpenGLFixedPipeline* fixedPipelineDrawi m_brain = NULL; m_modelVolume = NULL; m_modelWholeBrain = NULL; + m_modelType = ModelTypeEnum::MODEL_TYPE_INVALID; if (m_browserTabContent->getDisplayedVolumeModel() != NULL) { m_modelVolume = m_browserTabContent->getDisplayedVolumeModel(); m_brain = m_modelVolume->getBrain(); + m_modelType = m_modelVolume->getModelType(); } else if (m_browserTabContent->getDisplayedWholeBrainModel() != NULL) { m_modelWholeBrain = m_browserTabContent->getDisplayedWholeBrainModel(); m_brain = m_modelWholeBrain->getBrain(); + m_modelType = m_modelWholeBrain->getModelType(); } else { CaretAssertMessage(0, "Invalid model for volume slice drawing."); } CaretAssert(m_brain); + CaretAssert(m_modelType != ModelTypeEnum::MODEL_TYPE_INVALID); m_volumeDrawInfo = volumeDrawInfo; if (m_volumeDrawInfo.empty()) { @@ -159,9 +170,6 @@ BrainOpenGLVolumeSliceDrawing::draw(BrainOpenGLFixedPipeline* fixedPipelineDrawi } m_underlayVolume = m_volumeDrawInfo[0].volumeFile; - m_paletteFile = m_browserTabContent->getModelForDisplay()->getBrain()->getPaletteFile(); - CaretAssert(m_paletteFile); - const DisplayPropertiesLabels* dsl = m_brain->getDisplayPropertiesLabels(); m_displayGroup = dsl->getDisplayGroupForTab(m_fixedPipelineDrawing->windowTabIndex); @@ -195,6 +203,7 @@ BrainOpenGLVolumeSliceDrawing::draw(BrainOpenGLFixedPipeline* fixedPipelineDrawi drawVolumeSliceViewPlane(sliceDrawingType, sliceProjectionType, browserTabContent->getSliceViewPlane(), + browserTabContent->getSlicePlanesAllViewLayout(), viewport); } else if (browserTabContent->getDisplayedWholeBrainModel() != NULL) { @@ -212,6 +221,8 @@ BrainOpenGLVolumeSliceDrawing::draw(BrainOpenGLFixedPipeline* fixedPipelineDrawi * Type of projection for the slice drawing (oblique, orthogonal) * @param sliceViewPlane * The plane for slice drawing. + * @param allPlanesLayout + * The layout in ALL slices view. * @param viewport * The viewport (region of graphics area) for drawing slices. */ @@ -219,25 +230,32 @@ void BrainOpenGLVolumeSliceDrawing::drawVolumeSliceViewPlane(const VolumeSliceDrawingTypeEnum::Enum sliceDrawingType, const VolumeSliceProjectionTypeEnum::Enum sliceProjectionType, const VolumeSliceViewPlaneEnum::Enum sliceViewPlane, + const VolumeSliceViewAllPlanesLayoutEnum::Enum allPlanesLayout, const int32_t viewport[4]) { switch (sliceViewPlane) { case VolumeSliceViewPlaneEnum::ALL: { - const int32_t gap = 2; + int32_t paraVP[4] = { 0, 0, 0, 0 }; + int32_t coronalVP[4] = { 0, 0, 0, 0 }; + int32_t axialVP[4] = { 0, 0, 0, 0 }; - const int32_t vpHalfX = viewport[2] / 2; - const int32_t vpHalfY = viewport[3] / 2; + BrainOpenGLViewportContent::getSliceAllViewViewport(viewport, + VolumeSliceViewPlaneEnum::PARASAGITTAL, + allPlanesLayout, + paraVP); + BrainOpenGLViewportContent::getSliceAllViewViewport(viewport, + VolumeSliceViewPlaneEnum::CORONAL, + allPlanesLayout, + coronalVP); + BrainOpenGLViewportContent::getSliceAllViewViewport(viewport, + VolumeSliceViewPlaneEnum::AXIAL, + allPlanesLayout, + axialVP); /* * Draw parasagittal slice */ - const int32_t paraVP[4] = { - viewport[0], - viewport[1] + vpHalfY + gap, - vpHalfX - gap, - vpHalfY - gap - }; glPushMatrix(); drawVolumeSliceViewType(sliceDrawingType, sliceProjectionType, @@ -249,12 +267,6 @@ BrainOpenGLVolumeSliceDrawing::drawVolumeSliceViewPlane(const VolumeSliceDrawing /* * Draw coronal slice */ - const int32_t coronalVP[4] = { - viewport[0] + vpHalfX + gap, - viewport[1] + vpHalfY + gap, - vpHalfX - gap, - vpHalfY - gap - }; glPushMatrix(); drawVolumeSliceViewType(sliceDrawingType, sliceProjectionType, @@ -266,12 +278,6 @@ BrainOpenGLVolumeSliceDrawing::drawVolumeSliceViewPlane(const VolumeSliceDrawing /* * Draw axial slice */ - const int32_t axialVP[4] = { - viewport[0] + vpHalfX + gap, - viewport[1], - vpHalfX - gap, - vpHalfY - gap - }; glPushMatrix(); drawVolumeSliceViewType(sliceDrawingType, sliceProjectionType, @@ -279,22 +285,23 @@ BrainOpenGLVolumeSliceDrawing::drawVolumeSliceViewPlane(const VolumeSliceDrawing axialVP); glPopMatrix(); - /* - * 4th quadrant is used for axis showing orientation - */ - const int32_t allVP[4] = { - viewport[0], - viewport[1], - vpHalfX - gap, - vpHalfY - gap - }; - - switch (sliceProjectionType) { - case VolumeSliceProjectionTypeEnum::VOLUME_SLICE_PROJECTION_OBLIQUE: - drawOrientationAxes(allVP); - break; - case VolumeSliceProjectionTypeEnum::VOLUME_SLICE_PROJECTION_ORTHOGONAL: - break; + if (allPlanesLayout == VolumeSliceViewAllPlanesLayoutEnum::GRID_LAYOUT) { + /* + * 4th quadrant is used for axis showing orientation + */ + int32_t allVP[4] = { 0, 0, 0, 0 }; + BrainOpenGLViewportContent::getSliceAllViewViewport(viewport, + VolumeSliceViewPlaneEnum::ALL, + allPlanesLayout, + allVP); + + switch (sliceProjectionType) { + case VolumeSliceProjectionTypeEnum::VOLUME_SLICE_PROJECTION_OBLIQUE: + drawOrientationAxes(allVP); + break; + case VolumeSliceProjectionTypeEnum::VOLUME_SLICE_PROJECTION_ORTHOGONAL: + break; + } } } break; @@ -458,8 +465,7 @@ BrainOpenGLVolumeSliceDrawing::drawVolumeSliceViewTypeMontage(const VolumeSliceD const int32_t numCols = m_browserTabContent->getMontageNumberOfColumns(); CaretAssert(numCols > 0); - const CaretPreferences* caretPreferences = SessionManager::get()->getCaretPreferences(); - const int32_t montageCoordPrecision = caretPreferences->getVolumeMontageCoordinatePrecision(); + const int32_t montageCoordPrecision = m_browserTabContent->getVolumeMontageCoordinatePrecision(); const GapsAndMargins* gapsAndMargins = m_brain->getGapsAndMargins(); // const int32_t horizontalMargin = static_cast(viewport[2] * gapsAndMargins->getVolumeMontageHorizontalGap()); @@ -547,7 +553,7 @@ BrainOpenGLVolumeSliceDrawing::drawVolumeSliceViewTypeMontage(const VolumeSliceD uint8_t foregroundRGBA[4]; prefs->getBackgroundAndForegroundColors()->getColorForegroundVolumeView(foregroundRGBA); foregroundRGBA[3] = 255; - const bool showCoordinates = prefs->isVolumeMontageAxesCoordinatesDisplayed(); + const bool showCoordinates = m_browserTabContent->isVolumeMontageAxesCoordinatesDisplayed(); uint8_t backgroundRGBA[4]; prefs->getBackgroundAndForegroundColors()->getColorBackgroundVolumeView(backgroundRGBA); backgroundRGBA[3] = 255; @@ -616,13 +622,12 @@ BrainOpenGLVolumeSliceDrawing::drawVolumeSliceViewTypeMontage(const VolumeSliceD if (showCoordinates) { const AString coordText = (axisLetter + "=" - + AString::number(sliceCoord, 'f', montageCoordPrecision) - + "mm"); + + AString::number(sliceCoord, 'f', montageCoordPrecision)); - AnnotationPointSizeText annotationText(AnnotationAttributesDefaultTypeEnum::NORMAL); + AnnotationPercentSizeText annotationText(AnnotationAttributesDefaultTypeEnum::NORMAL); annotationText.setHorizontalAlignment(AnnotationTextAlignHorizontalEnum::RIGHT); annotationText.setVerticalAlignment(AnnotationTextAlignVerticalEnum::BOTTOM); - annotationText.setFontPointSize(AnnotationTextFontPointSizeEnum::SIZE12); + annotationText.setFontPercentViewportSize(10.0f); annotationText.setTextColor(CaretColorEnum::CUSTOM); annotationText.setCustomTextColor(foregroundRGBA); annotationText.setBackgroundColor(CaretColorEnum::CUSTOM); @@ -644,7 +649,7 @@ BrainOpenGLVolumeSliceDrawing::drawVolumeSliceViewTypeMontage(const VolumeSliceD glViewport(viewport[0], viewport[1], viewport[2], viewport[3]); - if (prefs->isVolumeAxesLabelsDisplayed()) { + if (m_browserTabContent->isVolumeAxesCrosshairLabelsDisplayed()) { drawAxesCrosshairsOrthoAndOblique(sliceProjectionType, sliceViewPlane, sliceCoordinates, @@ -867,10 +872,8 @@ BrainOpenGLVolumeSliceDrawing::drawVolumeSliceViewProjection(const VolumeSliceDr BrainOpenGLAnnotationDrawingFixedPipeline::Inputs inputs(this->m_brain, m_fixedPipelineDrawing->mode, BrainOpenGLFixedPipeline::s_gluLookAtCenterFromEyeOffsetDistance, - m_fixedPipelineDrawing->m_tabViewport, m_fixedPipelineDrawing->m_windowIndex, m_fixedPipelineDrawing->windowTabIndex, - BrainOpenGLAnnotationDrawingFixedPipeline::Inputs::TEXT_HEIGHT_USE_OPENGL_VIEWPORT_HEIGHT, BrainOpenGLAnnotationDrawingFixedPipeline::Inputs::WINDOW_DRAWING_NO); m_fixedPipelineDrawing->m_annotationDrawing->drawModelSpaceAnnotationsOnVolumeSlice(&inputs, slicePlane, @@ -1339,9 +1342,9 @@ BrainOpenGLVolumeSliceDrawing::drawObliqueSlice(const VolumeSliceViewPlaneEnum:: }; const float voxelCenter[3] = { - (bottomLeftVoxelCoord[0] + topRightVoxelCoord[0]) * 0.5, - (bottomLeftVoxelCoord[1] + topRightVoxelCoord[1]) * 0.5, - (bottomLeftVoxelCoord[2] + topRightVoxelCoord[2]) * 0.5 + (float)((bottomLeftVoxelCoord[0] + topRightVoxelCoord[0]) * 0.5), + (float)((bottomLeftVoxelCoord[1] + topRightVoxelCoord[1]) * 0.5), + (float)((bottomLeftVoxelCoord[2] + topRightVoxelCoord[2]) * 0.5) }; @@ -1563,22 +1566,14 @@ BrainOpenGLVolumeSliceDrawing::drawObliqueSlice(const VolumeSliceViewPlaneEnum:: } else if (mappableFile->isMappedWithPalette()) { const PaletteColorMapping* paletteColorMapping = mappableFile->getMapPaletteColorMapping(mapIndex); - const AString paletteName = paletteColorMapping->getSelectedPaletteName(); - const Palette* palette = m_paletteFile->getPaletteByName(paletteName); - if (palette != NULL) { - CaretAssertVectorIndex(m_volumeDrawInfo, i); - NodeAndVoxelColoring::colorScalarsWithPalette(m_volumeDrawInfo[i].statistics, - paletteColorMapping, - palette, - values, - values, - numValues, - rgba); - } - else { - CaretLogWarning("Missing palette named: " - + paletteName); - } + CaretAssertVectorIndex(m_volumeDrawInfo, i); + NodeAndVoxelColoring::colorScalarsWithPalette(m_volumeDrawInfo[i].statistics, + paletteColorMapping, + values, + paletteColorMapping, + values, + numValues, + rgba); } else if (mappableFile->isMappedWithLabelTable()) { GiftiLabelTable* labelTable = mappableFile->getMapLabelTable(mapIndex); @@ -1923,8 +1918,7 @@ BrainOpenGLVolumeSliceDrawing::drawOrthogonalSlice_LPI_ONLY(const VolumeSliceVie * Get colors for all voxels in the slice. */ const int64_t validVoxelCount = - volumeFile->getVoxelColorsForSliceInMap(m_brain->getPaletteFile(), - mapIndex, + volumeFile->getVoxelColorsForSliceInMap(mapIndex, sliceViewPlane, sliceIndexForDrawing, displayGroup, @@ -1986,21 +1980,21 @@ BrainOpenGLVolumeSliceDrawing::drawOrthogonalSlice_LPI_ONLY(const VolumeSliceVie * Setup for drawing the voxels in the slice. */ float startCoordinate[3] = { - originX - (voxelStepX / 2.0), - originY - (voxelStepY / 2.0), - originZ - (voxelStepZ / 2.0) + originX - (voxelStepX / 2.0f), + originY - (voxelStepY / 2.0f), + originZ - (voxelStepZ / 2.0f) }; float rowStep[3] = { - 0.0, - 0.0, - 0.0 + 0.0f, + 0.0f, + 0.0f }; float columnStep[3] = { - 0.0, - 0.0, - 0.0 + 0.0f, + 0.0f, + 0.0f }; @@ -2791,47 +2785,42 @@ BrainOpenGLVolumeSliceDrawing::drawIdentificationSymbols(const Plane& plane) return; break; } - - uint8_t rgba[4]; - const int32_t numVoxelIdSymbols = static_cast(voxelIDs.size()); - for (int32_t iVoxel = 0; iVoxel < numVoxelIdSymbols; iVoxel++) { - CaretAssertVectorIndex(voxelIDs, iVoxel); - const IdentifiedItemVoxel& voxel = voxelIDs[iVoxel]; - - /* - * Show symbol for node ID? - */ - if ( ! voxel.isShowIdentificationSymbol()) { - continue; - } - - - float xyz[3]; - voxel.getXYZ(xyz); - - const float symbolDiameter = voxel.getSymbolSize(); - const float halfSymbolSize = symbolDiameter / 2.0; - - const float dist = plane.signedDistanceToPlane(xyz); - if (dist < halfSymbolSize) { - if (isSelect) { - m_fixedPipelineDrawing->colorIdentification->addItem(rgba, - SelectionItemDataTypeEnum::VOXEL_IDENTIFICATION_SYMBOL, - iVoxel); - rgba[3] = 255; - } - else { - voxel.getSymbolRGBA(rgba); - } + + if (idManager->isShowVolumeIdentificationSymbols()) { + uint8_t rgba[4]; + const int32_t numVoxelIdSymbols = static_cast(voxelIDs.size()); + for (int32_t iVoxel = 0; iVoxel < numVoxelIdSymbols; iVoxel++) { + CaretAssertVectorIndex(voxelIDs, iVoxel); + const IdentifiedItemVoxel& voxel = voxelIDs[iVoxel]; - glPushMatrix(); - glTranslatef(xyz[0], xyz[1], xyz[2]); - m_fixedPipelineDrawing->drawSphereWithDiameter(rgba, - symbolDiameter); - glPopMatrix(); + float xyz[3]; + voxel.getXYZ(xyz); + + const float symbolDiameter = voxel.getSymbolSize(); + const float halfSymbolSize = symbolDiameter / 2.0; + + const float dist = plane.signedDistanceToPlane(xyz); + if (dist < halfSymbolSize) { + if (isSelect) { + m_fixedPipelineDrawing->colorIdentification->addItem(rgba, + SelectionItemDataTypeEnum::VOXEL_IDENTIFICATION_SYMBOL, + iVoxel); + rgba[3] = 255; + } + else { + voxel.getSymbolRGBA(rgba); + } + + glPushMatrix(); + glTranslatef(xyz[0], xyz[1], xyz[2]); + m_fixedPipelineDrawing->drawSphereWithDiameter(rgba, + symbolDiameter); + glPopMatrix(); + } } } + if (isSelect) { int voxelIdIndex = -1; float depth = -1.0; @@ -3050,8 +3039,7 @@ BrainOpenGLVolumeSliceDrawing::drawOrthogonalSliceWithCulling(const VolumeSliceV };//only used to multiply them all together to get an element count for the presumed array size, so just provide them as XYZ const int64_t validVoxelCount = - volumeFile->getVoxelColorsForSubSliceInMap(m_brain->getPaletteFile(), - mapIndex, + volumeFile->getVoxelColorsForSubSliceInMap(mapIndex, sliceViewPlane, sliceIndexForDrawing, culledFirstVoxelIJK, @@ -3108,9 +3096,9 @@ BrainOpenGLVolumeSliceDrawing::drawOrthogonalSliceWithCulling(const VolumeSliceV * Setup for drawing the voxels in the slice. */ float startCoordinate[3] = { - firstVoxelXYZ[0] - (voxelStepX / 2.0), - firstVoxelXYZ[1] - (voxelStepY / 2.0), - firstVoxelXYZ[2] - (voxelStepZ / 2.0) + firstVoxelXYZ[0] - (voxelStepX / 2.0f), + firstVoxelXYZ[1] - (voxelStepY / 2.0f), + firstVoxelXYZ[2] - (voxelStepZ / 2.0f) }; float rowStep[3] = { @@ -3485,7 +3473,10 @@ BrainOpenGLVolumeSliceDrawing::drawLayers(const VolumeSliceDrawingTypeEnum::Enum glPolygonOffset(0.0, 1.0); if (drawOutlineFlag) { - drawSurfaceOutline(slicePlane); + drawSurfaceOutline(m_modelType, + slicePlane, + m_browserTabContent->getVolumeSurfaceOutlineSet(), + m_fixedPipelineDrawing); } if (drawFibersFlag) { @@ -3541,37 +3532,78 @@ BrainOpenGLVolumeSliceDrawing::drawLayers(const VolumeSliceDrawingTypeEnum::Enum /** * Draw surface outlines on the volume slices * + * @param modelType + * Type of model being drawn. * @param plane - * Plane of the volume slice on which surface outlines are drawn. + * Plane of the volume slice on which surface outlines are drawn. + * @param outlineSet + * The surface outline set. + * @param fixedPipelineDrawing + * The fixed pipeline drawing. */ void -BrainOpenGLVolumeSliceDrawing::drawSurfaceOutline(const Plane& plane) +BrainOpenGLVolumeSliceDrawing::drawSurfaceOutline(const ModelTypeEnum::Enum modelType, + const Plane& plane, + VolumeSurfaceOutlineSetModel* outlineSet, + BrainOpenGLFixedPipeline* fixedPipelineDrawing) { - if ( ! plane.isValidPlane()) { - return; + glPushAttrib(GL_ENABLE_BIT); + glDisable(GL_DEPTH_TEST); + glDisable(GL_LIGHTING); + + switch (modelType) { + case ModelTypeEnum::MODEL_TYPE_CHART: + break; + case ModelTypeEnum::MODEL_TYPE_CHART_TWO: + break; + case ModelTypeEnum::MODEL_TYPE_INVALID: + break; + case ModelTypeEnum::MODEL_TYPE_SURFACE: + break; + case ModelTypeEnum::MODEL_TYPE_SURFACE_MONTAGE: + break; + case ModelTypeEnum::MODEL_TYPE_VOLUME_SLICES: + break; + case ModelTypeEnum::MODEL_TYPE_WHOLE_BRAIN: + /* + * Enable depth so outlines in front or in back + * of the slices. Without this the volume surface + * outlines "behind" the slices are visible and + * it looks weird + */ + glEnable(GL_DEPTH_TEST); + break; } - - float intersectionPoint1[3]; - float intersectionPoint2[3]; - - m_fixedPipelineDrawing->enableLineAntiAliasing(); - - VolumeSurfaceOutlineSetModel* outlineSet = m_browserTabContent->getVolumeSurfaceOutlineSet(); - + /* * Process each surface outline + * As of 24 May, "zero" is on top so draw in reverse order */ const int32_t numberOfOutlines = outlineSet->getNumberOfDislayedVolumeSurfaceOutlines(); - for (int io = 0; - io < numberOfOutlines; - io++) { + for (int32_t io = (numberOfOutlines - 1); + io >= 0; + io--) { + std::vector contourPrimitives; + VolumeSurfaceOutlineModel* outline = outlineSet->getVolumeSurfaceOutlineModel(io); if (outline->isDisplayed()) { Surface* surface = outline->getSurface(); if (surface != NULL) { - const float thickness = outline->getThickness(); + float thicknessPercentage = outline->getThicknessPercentageViewportHeight(); + const float thicknessPixels = outline->getThicknessPixelsObsolete(); - int numTriangles = surface->getNumberOfTriangles(); + /* + * Thickness was changed from pixels to percentage viewport height on Feb 02, 2018 + * If thickness percentage is negative, it was not present in an old + * scene so convert pixels to percentage using viewports dimensions + */ + if (thicknessPercentage < 0.0f) { + thicknessPercentage = GraphicsUtilitiesOpenGL::convertPixelsToPercentageOfViewportHeight(thicknessPixels); + if (thicknessPercentage > 0.0f) { + //std::cout << "Converted pixel thickness=" << thicknessPixels << " to millimeters=" << thicknessMillimeters << std::endl; + outline->setThicknessPercentageViewportHeight(thicknessPercentage); + } + } CaretColorEnum::Enum outlineColor = CaretColorEnum::BLACK; int32_t colorSourceBrowserTabIndex = -1; @@ -3581,6 +3613,7 @@ BrainOpenGLVolumeSliceDrawing::drawSurfaceOutline(const Plane& plane) switch (selectedColorOrTabItem->getItemType()) { case VolumeSurfaceOutlineColorOrTabModel::Item::ITEM_TYPE_BROWSER_TAB: colorSourceBrowserTabIndex = selectedColorOrTabItem->getBrowserTabIndex(); + outlineColor = CaretColorEnum::CUSTOM; break; case VolumeSurfaceOutlineColorOrTabModel::Item::ITEM_TYPE_COLOR: outlineColor = selectedColorOrTabItem->getColor(); @@ -3590,57 +3623,42 @@ BrainOpenGLVolumeSliceDrawing::drawSurfaceOutline(const Plane& plane) float* nodeColoringRGBA = NULL; if (surfaceColorFlag) { - nodeColoringRGBA = m_fixedPipelineDrawing->surfaceNodeColoring->colorSurfaceNodes(NULL, + nodeColoringRGBA = fixedPipelineDrawing->surfaceNodeColoring->colorSurfaceNodes(NULL, surface, colorSourceBrowserTabIndex); } - glColor3fv(CaretColorEnum::toRGB(outlineColor)); - m_fixedPipelineDrawing->setLineWidth(thickness); - - /* - * Examine each triangle to see if it intersects the Plane - * in which the slice exists. - */ - glBegin(GL_LINES); - for (int it = 0; it < numTriangles; it++) { - const int32_t* triangleNodes = surface->getTriangle(it); - const float* c1 = surface->getCoordinate(triangleNodes[0]); - const float* c2 = surface->getCoordinate(triangleNodes[1]); - const float* c3 = surface->getCoordinate(triangleNodes[2]); - - if (plane.triangleIntersectPlane(c1, c2, c3, - intersectionPoint1, - intersectionPoint2)) { - if (surfaceColorFlag) { - /* - * Use coloring assigned to the first node in the triangle - * but only if Alpha is valid (greater than zero). - */ - const int64_t colorIndex = triangleNodes[0] * 4; - if (nodeColoringRGBA[colorIndex + 3] > 0.0) { - glColor3fv(&nodeColoringRGBA[triangleNodes[0] * 4]); - } - else { - continue; - } - } - - /* - * Draw the line where the triangle intersections the slice - */ - glVertex3fv(intersectionPoint1); - glVertex3fv(intersectionPoint2); - } + //const float thicknessPercentage = GraphicsUtilitiesOpenGL::convertPixelsToPercentageOfViewportHeight(thicknessMillimeters); + SurfacePlaneIntersectionToContour contour(surface, + plane, + outlineColor, + nodeColoringRGBA, + thicknessPercentage); + AString errorMessage; + if ( ! contour.createContours(contourPrimitives, + errorMessage)) { + CaretLogSevere(errorMessage); } - glEnd(); } } + + /** + * Draw the contours. + */ + for (auto primitive : contourPrimitives) { + glPolygonOffset(1.0, 1.0); + glEnable(GL_POLYGON_OFFSET_FILL); + + GraphicsEngineDataOpenGL::draw(primitive); + delete primitive; + + glDisable(GL_POLYGON_OFFSET_FILL); + } } - m_fixedPipelineDrawing->disableLineAntiAliasing(); + glPopAttrib(); } - + /** * Draw foci on volume slice. * @@ -3690,7 +3708,7 @@ BrainOpenGLVolumeSliceDrawing::drawVolumeSliceFoci(const Plane& plane) const CaretColorEnum::Enum caretColor = fociDisplayProperties->getStandardColorType(displayGroup, m_fixedPipelineDrawing->windowTabIndex); float caretColorRGBA[4]; - CaretColorEnum::toRGBFloat(caretColor, caretColorRGBA); + CaretColorEnum::toRGBAFloat(caretColor, caretColorRGBA); if (fociDisplayProperties->isDisplayed(displayGroup, m_fixedPipelineDrawing->windowTabIndex) == false) { @@ -3875,9 +3893,8 @@ BrainOpenGLVolumeSliceDrawing::drawAxesCrosshairs(const VolumeSliceProjectionTyp const VolumeSliceViewPlaneEnum::Enum sliceViewPlane, const float sliceCoordinates[3]) { - CaretPreferences* prefs = SessionManager::get()->getCaretPreferences(); - const bool drawCrosshairsFlag = prefs->isVolumeAxesCrosshairsDisplayed(); - bool drawCrosshairLabelsFlag = prefs->isVolumeAxesLabelsDisplayed(); + const bool drawCrosshairsFlag = m_browserTabContent->isVolumeAxesCrosshairsDisplayed(); + bool drawCrosshairLabelsFlag = m_browserTabContent->isVolumeAxesCrosshairLabelsDisplayed(); switch (sliceDrawingType) { case VolumeSliceDrawingTypeEnum::VOLUME_SLICE_DRAW_MONTAGE: @@ -4104,56 +4121,45 @@ BrainOpenGLVolumeSliceDrawing::drawAxesCrosshairsOrthoAndOblique(const VolumeSli break; } + /* + * Offset text labels be a percentage of viewort width/height + */ GLint viewport[4]; glGetIntegerv(GL_VIEWPORT, viewport); - const int textOffset = 15; + const int textOffsetX = viewport[2] * 0.01f; + const int textOffsetY = viewport[3] * 0.01f; const int textLeftWindowXY[2] = { - textOffset, + textOffsetX, (viewport[3] / 2) }; const int textRightWindowXY[2] = { - viewport[2] - textOffset, + viewport[2] - textOffsetX, (viewport[3] / 2) }; const int textBottomWindowXY[2] = { viewport[2] / 2, - textOffset + textOffsetY }; const int textTopWindowXY[2] = { (viewport[2] / 2), - viewport[3] - textOffset + viewport[3] - textOffsetY }; /* * Crosshairs */ if (drawCrosshairsFlag) { - glLineWidth(1.0); - glColor3fv(horizontalAxisRGBA); - glBegin(GL_LINES); - glVertex3fv(horizontalAxisStartXYZ); - glVertex3fv(horizontalAxisEndXYZ); - glEnd(); - - glLineWidth(1.0); - glColor3fv(verticalAxisRGBA); - glBegin(GL_LINES); - glVertex3fv(verticalAxisStartXYZ); - glVertex3fv(verticalAxisEndXYZ); - glEnd(); + std::unique_ptr xhairPrimitive(GraphicsPrimitive::newPrimitiveV3fC4f(GraphicsPrimitive::PrimitiveType::POLYGONAL_LINES)); + xhairPrimitive->addVertex(horizontalAxisStartXYZ, horizontalAxisRGBA); + xhairPrimitive->addVertex(horizontalAxisEndXYZ, horizontalAxisRGBA); + xhairPrimitive->addVertex(verticalAxisStartXYZ, verticalAxisRGBA); + xhairPrimitive->addVertex(verticalAxisEndXYZ, verticalAxisRGBA); + xhairPrimitive->setLineWidth(GraphicsPrimitive::LineWidthType::PIXELS, 2.0f); + GraphicsEngineDataOpenGL::draw(xhairPrimitive.get()); } if (drawCrosshairLabelsFlag) { - const AnnotationTextFontPointSizeEnum::Enum fontSize = AnnotationTextFontPointSizeEnum::SIZE18; -// const int32_t fontSizeInt = AnnotationTextFontPointSizeEnum::toSizeNumeric(fontSize); -// -// const int textCenter[2] = { -// textLeftWindowXY[0], -// textLeftWindowXY[1] -// }; -// const int halfFontSize = fontSizeInt / 2; - uint8_t backgroundRGBA[4] = { m_fixedPipelineDrawing->m_backgroundColorByte[0], m_fixedPipelineDrawing->m_backgroundColorByte[1], @@ -4161,127 +4167,39 @@ BrainOpenGLVolumeSliceDrawing::drawAxesCrosshairsOrthoAndOblique(const VolumeSli m_fixedPipelineDrawing->m_backgroundColorByte[3] }; -// GLint savedViewport[4]; -// glGetIntegerv(GL_VIEWPORT, savedViewport); -// -// int vpLeftX = savedViewport[0] + textCenter[0] - halfFontSize; -// int vpRightX = savedViewport[0] + textCenter[0] + halfFontSize; -// int vpBottomY = savedViewport[1] + textCenter[1] - halfFontSize; -// int vpTopY = savedViewport[1] + textCenter[1] + halfFontSize; -// MathFunctions::limitRange(vpLeftX, -// savedViewport[0], -// savedViewport[0] + savedViewport[2]); -// MathFunctions::limitRange(vpRightX, -// savedViewport[0], -// savedViewport[0] + savedViewport[2]); -// MathFunctions::limitRange(vpBottomY, -// savedViewport[1], -// savedViewport[1] + savedViewport[3]); -// MathFunctions::limitRange(vpTopY, -// savedViewport[1], -// savedViewport[1] + savedViewport[3]); -// -// const int vpSizeX = vpRightX - vpLeftX; -// const int vpSizeY = vpTopY - vpBottomY; -// glViewport(vpLeftX, vpBottomY, vpSizeX, vpSizeY); - -// glMatrixMode(GL_PROJECTION); -// glPushMatrix(); -// glLoadIdentity(); -// glOrtho(-1.0, 1.0, -1.0, 1.0, -1.0, 1.0); -// -// glMatrixMode(GL_MODELVIEW); -// glPushMatrix(); -// glLoadIdentity(); - -// std::vector rgba; -// std::vector coords, normals; -// -// coords.push_back(-1.0); -// coords.push_back(-1.0); -// coords.push_back( 0.0); -// normals.push_back(0.0); -// normals.push_back(0.0); -// normals.push_back(1.0); -// rgba.push_back(backgroundRGBA[0]); -// rgba.push_back(backgroundRGBA[1]); -// rgba.push_back(backgroundRGBA[2]); -// rgba.push_back(backgroundRGBA[3]); -// -// coords.push_back( 1.0); -// coords.push_back(-1.0); -// coords.push_back( 0.0); -// normals.push_back(0.0); -// normals.push_back(0.0); -// normals.push_back(1.0); -// rgba.push_back(backgroundRGBA[0]); -// rgba.push_back(backgroundRGBA[1]); -// rgba.push_back(backgroundRGBA[2]); -// rgba.push_back(backgroundRGBA[3]); -// -// coords.push_back( 1.0); -// coords.push_back( 1.0); -// coords.push_back( 0.0); -// normals.push_back(0.0); -// normals.push_back(0.0); -// normals.push_back(1.0); -// rgba.push_back(backgroundRGBA[0]); -// rgba.push_back(backgroundRGBA[1]); -// rgba.push_back(backgroundRGBA[2]); -// rgba.push_back(backgroundRGBA[3]); -// -// coords.push_back(-1.0); -// coords.push_back( 1.0); -// coords.push_back( 0.0); -// normals.push_back(0.0); -// normals.push_back(0.0); -// normals.push_back(1.0); -// rgba.push_back(backgroundRGBA[0]); -// rgba.push_back(backgroundRGBA[1]); -// rgba.push_back(backgroundRGBA[2]); -// rgba.push_back(backgroundRGBA[3]); - - -// BrainOpenGLPrimitiveDrawing::drawQuads(coords, -// normals, -// rgba); - -// glPopMatrix(); -// -// glMatrixMode(GL_PROJECTION); -// glPopMatrix(); -// glMatrixMode(GL_MODELVIEW); - -// glViewport(savedViewport[0], -// savedViewport[1], -// savedViewport[2], -// savedViewport[3]); - - AnnotationPointSizeText annotationText(AnnotationAttributesDefaultTypeEnum::NORMAL); - annotationText.setHorizontalAlignment(AnnotationTextAlignHorizontalEnum::CENTER); - annotationText.setVerticalAlignment(AnnotationTextAlignVerticalEnum::MIDDLE); + AnnotationPercentSizeText annotationText(AnnotationAttributesDefaultTypeEnum::NORMAL); annotationText.setBoldStyleEnabled(true); - annotationText.setFontPointSize(fontSize); + annotationText.setFontPercentViewportSize(5.0f); annotationText.setBackgroundColor(CaretColorEnum::CUSTOM); annotationText.setTextColor(CaretColorEnum::CUSTOM); annotationText.setCustomTextColor(horizontalAxisRGBA); annotationText.setCustomBackgroundColor(backgroundRGBA); + + annotationText.setHorizontalAlignment(AnnotationTextAlignHorizontalEnum::LEFT); + annotationText.setVerticalAlignment(AnnotationTextAlignVerticalEnum::MIDDLE); annotationText.setText(horizontalLeftText); m_fixedPipelineDrawing->drawTextAtViewportCoords(textLeftWindowXY[0], textLeftWindowXY[1], annotationText); annotationText.setText(horizontalRightText); + annotationText.setHorizontalAlignment(AnnotationTextAlignHorizontalEnum::RIGHT); + annotationText.setVerticalAlignment(AnnotationTextAlignVerticalEnum::MIDDLE); m_fixedPipelineDrawing->drawTextAtViewportCoords(textRightWindowXY[0], textRightWindowXY[1], annotationText); annotationText.setCustomTextColor(verticalAxisRGBA); + + annotationText.setHorizontalAlignment(AnnotationTextAlignHorizontalEnum::CENTER); + annotationText.setVerticalAlignment(AnnotationTextAlignVerticalEnum::BOTTOM); annotationText.setText(verticalBottomText); m_fixedPipelineDrawing->drawTextAtViewportCoords(textBottomWindowXY[0], textBottomWindowXY[1], annotationText); + annotationText.setHorizontalAlignment(AnnotationTextAlignHorizontalEnum::CENTER); + annotationText.setVerticalAlignment(AnnotationTextAlignVerticalEnum::TOP); annotationText.setText(verticalTopText); m_fixedPipelineDrawing->drawTextAtViewportCoords(textTopWindowXY[0], textTopWindowXY[1], @@ -4412,9 +4330,8 @@ BrainOpenGLVolumeSliceDrawing::getMinMaxVoxelSpacing(const VolumeMappableInterfa void BrainOpenGLVolumeSliceDrawing::drawOrientationAxes(const int viewport[4]) { - CaretPreferences* prefs = SessionManager::get()->getCaretPreferences(); - const bool drawCylindersFlag = prefs->isVolumeAxesCrosshairsDisplayed(); - const bool drawLabelsFlag = prefs->isVolumeAxesLabelsDisplayed(); + const bool drawCylindersFlag = m_browserTabContent->isVolumeAxesCrosshairsDisplayed(); + const bool drawLabelsFlag = m_browserTabContent->isVolumeAxesCrosshairLabelsDisplayed(); /* * Set the viewport @@ -4519,22 +4436,22 @@ BrainOpenGLVolumeSliceDrawing::drawOrientationAxes(const int viewport[4]) const double textMinCoord = -textMaxCoord; - const float axialPlaneMin[3] = { 0.0, 0.0, axisMinCoord }; - const float axialPlaneMax[3] = { 0.0, 0.0, axisMaxCoord }; - const double axialTextMin[3] = { 0.0, 0.0, textMinCoord }; - const double axialTextMax[3] = { 0.0, 0.0, textMaxCoord }; + const float axialPlaneMin[3] = { 0.0, 0.0, (float)axisMinCoord }; + const float axialPlaneMax[3] = { 0.0, 0.0, (float)axisMaxCoord }; + const double axialTextMin[3] = { 0.0, 0.0, (float)textMinCoord }; + const double axialTextMax[3] = { 0.0, 0.0, (float)textMaxCoord }; - const float coronalPlaneMin[3] = { axisMinCoord, 0.0, 0.0 }; - const float coronalPlaneMax[3] = { axisMaxCoord, 0.0, 0.0 }; - const double coronalTextMin[3] = { textMinCoord, 0.0, 0.0 }; - const double coronalTextMax[3] = { textMaxCoord, 0.0, 0.0 }; + const float coronalPlaneMin[3] = { (float)axisMinCoord, 0.0, 0.0 }; + const float coronalPlaneMax[3] = { (float)axisMaxCoord, 0.0, 0.0 }; + const double coronalTextMin[3] = { (float)textMinCoord, 0.0, 0.0 }; + const double coronalTextMax[3] = { (float)textMaxCoord, 0.0, 0.0 }; - const float paraPlaneMin[3] = { 0.0, axisMinCoord, 0.0 }; - const float paraPlaneMax[3] = { 0.0, axisMaxCoord, 0.0 }; - const double paraTextMin[3] = { 0.0, textMinCoord, 0.0 }; - const double paraTextMax[3] = { 0.0, textMaxCoord, 0.0 }; + const float paraPlaneMin[3] = { 0.0, (float)axisMinCoord, 0.0 }; + const float paraPlaneMax[3] = { 0.0, (float)axisMaxCoord, 0.0 }; + const double paraTextMin[3] = { 0.0, (float)textMinCoord, 0.0 }; + const double paraTextMax[3] = { 0.0, (float)textMaxCoord, 0.0 }; - const float axesCrosshairRadius = 1.0; + const float axesCrosshairRadius = 1.0f; if (drawCylindersFlag) { m_fixedPipelineDrawing->drawCylinder(blue, @@ -4737,6 +4654,10 @@ BrainOpenGLVolumeSliceDrawing::drawOrthogonalSliceVoxels(const float sliceNormal const int32_t mapIndex, const uint8_t sliceOpacity) { + if (validVoxelCount <= 0) { + return; + } + /* * There are two ways to draw the voxels. * @@ -4773,9 +4694,10 @@ BrainOpenGLVolumeSliceDrawing::drawOrthogonalSliceVoxels(const float sliceNormal const int64_t totalVertexBytes = ((numberOfColumns + 1) * (numberOfRows + 1) * bytesPerVertex); - const int64_t singleQuadBytes = (validVoxelCount * bytesPerVertex * 4); + const int64_t numberOfVoxels = numberOfColumns * numberOfRows; + const int64_t singleQuadBytes = (numberOfVoxels * bytesPerVertex * 4); const int64_t indexQuadBytes = (totalVertexBytes - + (16 * validVoxelCount)); + + (16 * numberOfVoxels)); bool drawWithQuadIndicesFlag = false; if (indexQuadBytes < singleQuadBytes) { diff --git a/src/Brain/BrainOpenGLVolumeSliceDrawing.h b/src/Brain/BrainOpenGLVolumeSliceDrawing.h index 00cfc95ec2df0521ebf351979a1f279232d863da..d11872f199e79178c77ecf184d5892ea9f2a970e 100644 --- a/src/Brain/BrainOpenGLVolumeSliceDrawing.h +++ b/src/Brain/BrainOpenGLVolumeSliceDrawing.h @@ -24,8 +24,10 @@ #include "BrainOpenGLFixedPipeline.h" #include "CaretObject.h" #include "DisplayGroupEnum.h" +#include "ModelTypeEnum.h" #include "VolumeSliceProjectionTypeEnum.h" #include "VolumeSliceDrawingTypeEnum.h" +#include "VolumeSliceViewAllPlanesLayoutEnum.h" #include "VolumeSliceViewPlaneEnum.h" @@ -37,9 +39,9 @@ namespace caret { class Matrix4x4; class ModelVolume; class ModelWholeBrain; - class PaletteFile; class Plane; class VolumeMappableInterface; + class VolumeSurfaceOutlineSetModel; class BrainOpenGLVolumeSliceDrawing : public CaretObject { @@ -178,6 +180,7 @@ namespace caret { void drawVolumeSliceViewPlane(const VolumeSliceDrawingTypeEnum::Enum sliceDrawingType, const VolumeSliceProjectionTypeEnum::Enum sliceProjectionType, const VolumeSliceViewPlaneEnum::Enum sliceViewPlane, + const VolumeSliceViewAllPlanesLayoutEnum::Enum allPlanesLayout, const int32_t viewport[4]); void drawVolumeSliceViewType(const VolumeSliceDrawingTypeEnum::Enum sliceDrawingType, @@ -236,7 +239,10 @@ namespace caret { const Plane& slicePlane, const float sliceCoordinates[3]); - void drawSurfaceOutline(const Plane& plane); + static void drawSurfaceOutline(const ModelTypeEnum::Enum modelType, + const Plane& plane, + VolumeSurfaceOutlineSetModel* outlineSet, + BrainOpenGLFixedPipeline* fixedPipelineDrawing); void drawVolumeSliceFoci(const Plane& plane); @@ -324,6 +330,8 @@ namespace caret { void resetIdentification(); + ModelTypeEnum::Enum m_modelType; + ModelVolume* m_modelVolume; ModelWholeBrain* m_modelWholeBrain; @@ -340,8 +348,6 @@ namespace caret { BrowserTabContent* m_browserTabContent; - PaletteFile* m_paletteFile; - DisplayGroupEnum::Enum m_displayGroup; int32_t m_tabIndex; @@ -358,6 +364,8 @@ namespace caret { static const int32_t IDENTIFICATION_INDICES_PER_VOXEL; + friend class BrainOpenGLVolumeObliqueSliceDrawing; + // ADD_NEW_MEMBERS_HERE }; diff --git a/src/Brain/BrainOpenGLWindowContent.cxx b/src/Brain/BrainOpenGLWindowContent.cxx new file mode 100644 index 0000000000000000000000000000000000000000..89eebccab233ecc37b9b000f38fe89e65c81f846 --- /dev/null +++ b/src/Brain/BrainOpenGLWindowContent.cxx @@ -0,0 +1,232 @@ + +/*LICENSE_START*/ +/* + * Copyright (C) 2017 Washington University School of Medicine + * + * 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 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ +/*LICENSE_END*/ + +#define __BRAIN_OPEN_G_L_WINDOW_CONTENT_DECLARE__ +#include "BrainOpenGLWindowContent.h" +#undef __BRAIN_OPEN_G_L_WINDOW_CONTENT_DECLARE__ + +#include "CaretAssert.h" +using namespace caret; + + + +/** + * \class caret::BrainOpenGLWindowContent + * \brief Content of window and its tabs. + * \ingroup Brain + */ + +/** + * Constructor. + */ +BrainOpenGLWindowContent::BrainOpenGLWindowContent() +: CaretObject() +{ + +} + +/** + * Destructor. + */ +BrainOpenGLWindowContent::~BrainOpenGLWindowContent() +{ +} + +/** + * Copy constructor. + * @param obj + * Object that is copied. + */ +BrainOpenGLWindowContent::BrainOpenGLWindowContent(const BrainOpenGLWindowContent& obj) +: CaretObject(obj) +{ + this->copyHelperBrainOpenGLWindowContent(obj); +} + +/** + * Assignment operator. + * @param obj + * Data copied from obj to this. + * @return + * Reference to this object. + */ +BrainOpenGLWindowContent& +BrainOpenGLWindowContent::operator=(const BrainOpenGLWindowContent& obj) +{ + if (this != &obj) { + CaretObject::operator=(obj); + this->copyHelperBrainOpenGLWindowContent(obj); + } + return *this; +} + +/** + * Clear the content of the window and tabs. + */ +void +BrainOpenGLWindowContent::clear() +{ + m_tabViewports.clear(); + m_windowViewport.reset(); +} + +/** + * Add a tab viewport. + * + * @param tabViewport + * Tab viewport to add. + */ +void +BrainOpenGLWindowContent::addTabViewport(BrainOpenGLViewportContent* tabViewport) +{ + std::unique_ptr ptr(tabViewport); + m_tabViewports.push_back(std::move(ptr)); +} + +/** + * Set the window viewport. + * + * @param windowViewport + * The new window viewport. + */ +void +BrainOpenGLWindowContent::setWindowViewport(BrainOpenGLViewportContent* windowViewport) +{ + m_windowViewport.reset(windowViewport); +} + +/** + * @return Number of tab viewports. + */ +int32_t +BrainOpenGLWindowContent::getNumberOfTabViewports() const +{ + return m_tabViewports.size(); +} + +/** + * Get the viewport content for the tab at the given index. + * + * @param index + * Index of element in viewports BUT IS NOT the TAB INDEX + * @return + * Tab for the given index or NULL if not available. + */ +const BrainOpenGLViewportContent* +BrainOpenGLWindowContent::getTabViewportAtIndex(const int32_t index) const +{ + if ((index >= 0) + && (index < static_cast(m_tabViewports.size()))) { + CaretAssertVectorIndex(m_tabViewports, index); + return m_tabViewports[index].get(); + } + + return NULL; +} + +/** + * Get the tab viewport containing the given X/Y coordinates + * after lock aspect ratio has been performed on the tab viewport. + * + * @param x + * The X-coordinate + * @param y + * The Y-coordinate + * @return + * Tab viewport containing the coordinate or NULL if not found. + */ +const BrainOpenGLViewportContent* +BrainOpenGLWindowContent::getTabViewportWithLockAspectXY(const int32_t x, + const int32_t y) const +{ + for (const auto& vp : m_tabViewports) { + int32_t viewport[4]; + vp->getTabViewportBeforeApplyingMargins(viewport); + if ((x >= viewport[0]) + && (x < (viewport[0] + viewport[2])) + && (y >= viewport[1]) + && (y < (viewport[1] + viewport[3]))) { + return vp.get(); + } + } + return NULL; +} + + +/** + * All viewports for all tabs. + */ +std::vector +BrainOpenGLWindowContent::getAllTabViewports() const +{ + std::vector allTabs; + + for (auto& vp : m_tabViewports) { + allTabs.push_back(vp.get()); + } + + return allTabs; +} + + +/** + * @return The viewport for the window (could be NULL). + */ +const BrainOpenGLViewportContent* +BrainOpenGLWindowContent::getWindowViewport() const +{ + return m_windowViewport.get(); +} + +/** + * Helps with copying an object of this type. + * @param obj + * Object that is copied. + */ +void +BrainOpenGLWindowContent::copyHelperBrainOpenGLWindowContent(const BrainOpenGLWindowContent& obj) +{ + clear(); + + if (obj.m_windowViewport) { + setWindowViewport(new BrainOpenGLViewportContent(*obj.m_windowViewport.get())); + } + + for (const auto& tabvp : obj.m_tabViewports) { + BrainOpenGLViewportContent* vp(NULL); + const BrainOpenGLViewportContent* ptr = tabvp.get(); + if (ptr != NULL) { + vp = new BrainOpenGLViewportContent(*ptr); + } + addTabViewport(vp); + } +} + +/** + * Get a description of this object's content. + * @return String describing this object's content. + */ +AString +BrainOpenGLWindowContent::toString() const +{ + return "BrainOpenGLWindowContent"; +} + diff --git a/src/Brain/BrainOpenGLWindowContent.h b/src/Brain/BrainOpenGLWindowContent.h new file mode 100644 index 0000000000000000000000000000000000000000..9109f795d25367fff7c0d2e7b7cd35b0a9ceb94a --- /dev/null +++ b/src/Brain/BrainOpenGLWindowContent.h @@ -0,0 +1,83 @@ +#ifndef __BRAIN_OPEN_G_L_WINDOW_CONTENT_H__ +#define __BRAIN_OPEN_G_L_WINDOW_CONTENT_H__ + +/*LICENSE_START*/ +/* + * Copyright (C) 2017 Washington University School of Medicine + * + * 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 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ +/*LICENSE_END*/ + + + +#include + +#include "BrainOpenGLViewportContent.h" +#include "CaretObject.h" + + + +namespace caret { + + class BrainOpenGLWindowContent : public CaretObject { + + public: + BrainOpenGLWindowContent(); + + virtual ~BrainOpenGLWindowContent(); + + BrainOpenGLWindowContent(const BrainOpenGLWindowContent& obj); + + BrainOpenGLWindowContent& operator=(const BrainOpenGLWindowContent& obj); + + void clear(); + + void addTabViewport(BrainOpenGLViewportContent* tabsViewportContent); + + void setWindowViewport(BrainOpenGLViewportContent* windowViewportContent); + + int32_t getNumberOfTabViewports() const; + + const BrainOpenGLViewportContent* getTabViewportAtIndex(const int32_t index) const; + + const BrainOpenGLViewportContent* getTabViewportWithLockAspectXY(const int32_t x, + const int32_t y) const; + + const BrainOpenGLViewportContent* getWindowViewport() const; + + std::vector getAllTabViewports() const; + + // ADD_NEW_METHODS_HERE + + virtual AString toString() const; + + private: + void copyHelperBrainOpenGLWindowContent(const BrainOpenGLWindowContent& obj); + + std::vector> m_tabViewports; + + std::unique_ptr m_windowViewport; + + // ADD_NEW_MEMBERS_HERE + + }; + +#ifdef __BRAIN_OPEN_G_L_WINDOW_CONTENT_DECLARE__ + // +#endif // __BRAIN_OPEN_G_L_WINDOW_CONTENT_DECLARE__ + +} // namespace +#endif //__BRAIN_OPEN_G_L_WINDOW_CONTENT_H__ diff --git a/src/Brain/BrainStructure.cxx b/src/Brain/BrainStructure.cxx index 2dc4807ef759731c4ed56c86d838e2c14f5b84b6..59165150b4a76802e36529dcabc5c9f96359f997 100644 --- a/src/Brain/BrainStructure.cxx +++ b/src/Brain/BrainStructure.cxx @@ -1109,6 +1109,19 @@ BrainStructure::getNodeAttributes() const return m_nodeAttributes; } +/** + * @return True if this brain structure contains any data files, else false. + */ +bool +BrainStructure::hasDataFiles() const +{ + std::vector allDataFiles; + getAllDataFiles(allDataFiles); + + return ( ! allDataFiles.empty()); +} + + /** * Get all loaded data files. * @param allDataFilesOut diff --git a/src/Brain/BrainStructure.h b/src/Brain/BrainStructure.h index 58d535a489ed0678d6038f666c1908309dbd5faa..1a9f7f5818fc10389daf7321c249a4667753d48e 100644 --- a/src/Brain/BrainStructure.h +++ b/src/Brain/BrainStructure.h @@ -149,6 +149,8 @@ namespace caret { const BrainStructureNodeAttributes* getNodeAttributes() const; + bool hasDataFiles() const; + void getAllDataFiles(std::vector& allDataFilesOut) const; bool removeWithoutDeleteDataFile(const CaretDataFile* caretDataFile); diff --git a/src/Brain/BrowserTabContent.cxx b/src/Brain/BrowserTabContent.cxx index 042f0c395219766ab85688aafae5fc05de3d5565..537991dae9f294e7bd136f2e0a35d50679799271 100644 --- a/src/Brain/BrowserTabContent.cxx +++ b/src/Brain/BrowserTabContent.cxx @@ -40,6 +40,9 @@ #include "CaretPreferences.h" #include "ChartableMatrixInterface.h" #include "ChartModelDataSeries.h" +#include "ChartTwoMatrixDisplayProperties.h" +#include "ChartTwoOverlay.h" +#include "ChartTwoOverlaySet.h" #include "CiftiBrainordinateDataSeriesFile.h" #include "CiftiConnectivityMatrixDenseDynamicFile.h" #include "ClippingPlaneGroup.h" @@ -60,6 +63,7 @@ #include "MathFunctions.h" #include "Matrix4x4.h" #include "ModelChart.h" +#include "ModelChartTwo.h" #include "ModelSurface.h" #include "ModelSurfaceMontage.h" #include "ModelSurfaceSelector.h" @@ -111,12 +115,21 @@ BrowserTabContent::BrowserTabContent(const int32_t tabNumber) m_wholeBrainModel = NULL; m_surfaceMontageModel = NULL; m_chartModel = NULL; + m_chartTwoModel = NULL; m_guiName = ""; m_userName = ""; m_volumeSurfaceOutlineSetModel = new VolumeSurfaceOutlineSetModel(); - m_yokingGroup = YokingGroupEnum::YOKING_GROUP_OFF; + m_brainModelYokingGroup = YokingGroupEnum::YOKING_GROUP_OFF; + m_chartModelYokingGroup = YokingGroupEnum::YOKING_GROUP_OFF; m_identificationUpdatesVolumeSlices = prefs->isVolumeIdentificationDefaultedOn(); + m_displayVolumeAxesCrosshairs = prefs->isVolumeAxesCrosshairsDisplayed(); + m_displayVolumeAxesCrosshairLabels = prefs->isVolumeAxesLabelsDisplayed(); + m_displayVolumeMontageAxesCoordinates = prefs->isVolumeMontageAxesCoordinatesDisplayed(); + m_volumeMontageCoordinatePrecision = prefs->getVolumeMontageCoordinatePrecision(); + + m_lightingEnabled = true; + m_aspectRatio = 1.0; m_aspectRatioLocked = false; @@ -124,6 +137,8 @@ BrowserTabContent::BrowserTabContent(const int32_t tabNumber) m_flatSurfaceViewingTransformation = new ViewingTransformations(); m_viewingTransformation = new ViewingTransformations(); m_volumeSliceViewingTransformation = new ViewingTransformationsVolume(); + m_chartTwoMatrixViewingTranformation = new ViewingTransformations(); + m_chartTwoMatrixDisplayProperties = new ChartTwoMatrixDisplayProperties(); m_wholeBrainSurfaceSettings = new WholeBrainSurfaceSettings(); @@ -168,6 +183,14 @@ BrowserTabContent::BrowserTabContent(const int32_t tabNumber) "ViewingTransformations", m_volumeSliceViewingTransformation); + m_sceneClassAssistant->add("m_chartTwoMatrixViewingTranformation", + "ViewingTransformations", + m_chartTwoMatrixViewingTranformation); + + m_sceneClassAssistant->add("m_chartTwoMatrixDisplayProperties", + "ChartTwoMatrixDisplayProperties", + m_chartTwoMatrixDisplayProperties); + m_sceneClassAssistant->add("m_volumeSliceSettings", "VolumeSliceSettings", m_volumeSliceSettings); @@ -179,13 +202,26 @@ BrowserTabContent::BrowserTabContent(const int32_t tabNumber) m_sceneClassAssistant->add("m_identificationUpdatesVolumeSlices", &m_identificationUpdatesVolumeSlices); + m_sceneClassAssistant->add("m_displayVolumeAxesCrosshairs", + &m_displayVolumeAxesCrosshairs); + m_sceneClassAssistant->add("m_displayVolumeAxesCrosshairLabels", + &m_displayVolumeAxesCrosshairLabels); + m_sceneClassAssistant->add("m_displayVolumeMontageAxesCoordinates", + &m_displayVolumeMontageAxesCoordinates); + m_sceneClassAssistant->add("m_volumeMontageCoordinatePrecision", + &m_volumeMontageCoordinatePrecision); + + m_sceneClassAssistant->add("m_lightingEnabled", + &m_lightingEnabled); m_sceneClassAssistant->add("m_aspectRatio", &m_aspectRatio); m_sceneClassAssistant->add("m_aspectRatioLocked", &m_aspectRatioLocked); - m_sceneClassAssistant->add("m_yokingGroup", - &m_yokingGroup); + m_sceneClassAssistant->add("m_brainModelYokingGroup", + &m_brainModelYokingGroup); + m_sceneClassAssistant->add("m_chartModelYokingGroup", + &m_chartModelYokingGroup); EventManager::get()->addEventListener(this, EventTypeEnum::EVENT_ANNOTATION_COLOR_BAR_GET); EventManager::get()->addEventListener(this, @@ -200,7 +236,8 @@ BrowserTabContent::BrowserTabContent(const int32_t tabNumber) * Need to be done from here */ if (prefs->isYokingDefaultedOn()) { - setYokingGroup(YokingGroupEnum::YOKING_GROUP_A); + setBrainModelYokingGroup(YokingGroupEnum::YOKING_GROUP_A); + setChartModelYokingGroup(YokingGroupEnum::YOKING_GROUP_OFF); } } @@ -218,6 +255,8 @@ BrowserTabContent::~BrowserTabContent() delete m_cerebellumViewingTransformation; delete m_viewingTransformation; delete m_volumeSliceViewingTransformation; + delete m_chartTwoMatrixViewingTranformation; + delete m_chartTwoMatrixDisplayProperties; delete m_obliqueVolumeRotationMatrix; delete m_surfaceModelSelector; @@ -264,18 +303,30 @@ BrowserTabContent::cloneBrowserTabContent(BrowserTabContent* tabToClone) *m_clippingPlaneGroup = *tabToClone->m_clippingPlaneGroup; - m_yokingGroup = tabToClone->m_yokingGroup; + m_brainModelYokingGroup = tabToClone->m_brainModelYokingGroup; + m_chartModelYokingGroup = tabToClone->m_chartModelYokingGroup; + m_aspectRatio = tabToClone->m_aspectRatio; + m_aspectRatioLocked = tabToClone->m_aspectRatioLocked; *m_cerebellumViewingTransformation = *tabToClone->m_cerebellumViewingTransformation; *m_flatSurfaceViewingTransformation = *tabToClone->m_flatSurfaceViewingTransformation; *m_viewingTransformation = *tabToClone->m_viewingTransformation; *m_volumeSliceViewingTransformation = *tabToClone->m_volumeSliceViewingTransformation; + *m_chartTwoMatrixViewingTranformation = *tabToClone->m_chartTwoMatrixViewingTranformation; + *m_chartTwoMatrixDisplayProperties = *tabToClone->m_chartTwoMatrixDisplayProperties; *m_volumeSliceSettings = *tabToClone->m_volumeSliceSettings; *m_wholeBrainSurfaceSettings = *tabToClone->m_wholeBrainSurfaceSettings; *m_obliqueVolumeRotationMatrix = *tabToClone->m_obliqueVolumeRotationMatrix; m_identificationUpdatesVolumeSlices = tabToClone->m_identificationUpdatesVolumeSlices; + + m_displayVolumeAxesCrosshairs = tabToClone->m_displayVolumeAxesCrosshairs; + m_displayVolumeAxesCrosshairLabels = tabToClone->m_displayVolumeAxesCrosshairLabels; + m_displayVolumeMontageAxesCoordinates = tabToClone->m_displayVolumeMontageAxesCoordinates; + m_volumeMontageCoordinatePrecision = tabToClone->m_volumeMontageCoordinatePrecision; + + m_lightingEnabled = tabToClone->m_lightingEnabled; Model* model = getModelForDisplay(); @@ -405,14 +456,17 @@ BrowserTabContent::getDescriptionOfContent(PlainTextStringBuilder& descriptionOu const Model* model = getModelForDisplay(); if (model != NULL) { - bool chartFlag = false; - bool surfaceFlag = false; + bool chartOneFlag = false; + bool chartTwoFlag = false; + bool surfaceFlag = false; bool surfaceMontageFlag = false; bool wholeBrainFlag = false; bool volumeFlag = false; switch (model->getModelType()) { case ModelTypeEnum::MODEL_TYPE_CHART: - chartFlag = true; + chartOneFlag = true; + case ModelTypeEnum::MODEL_TYPE_CHART_TWO: + chartTwoFlag = true; break; case ModelTypeEnum::MODEL_TYPE_INVALID: break; @@ -430,7 +484,7 @@ BrowserTabContent::getDescriptionOfContent(PlainTextStringBuilder& descriptionOu break; } - if (chartFlag) { + if (chartOneFlag) { model->getDescriptionOfContent(tabIndex, descriptionOut); } @@ -482,7 +536,13 @@ BrowserTabContent::getDescriptionOfContent(PlainTextStringBuilder& descriptionOu descriptionOut.popIndentation(); } - if ( ! chartFlag) { + if (chartOneFlag) { + /* nothing */ + } + else if (chartTwoFlag) { + getChartTwoOverlaySet()->getDescriptionOfContent(descriptionOut); + } + else { getOverlaySet()->getDescriptionOfContent(descriptionOut); } } @@ -543,6 +603,9 @@ BrowserTabContent::getModelForDisplay() case ModelTypeEnum::MODEL_TYPE_CHART: mdc = m_chartModel; break; + case ModelTypeEnum::MODEL_TYPE_CHART_TWO: + mdc = m_chartTwoModel; + break; } return mdc; @@ -577,6 +640,9 @@ BrowserTabContent::getModelForDisplay() const case ModelTypeEnum::MODEL_TYPE_CHART: mdc = m_chartModel; break; + case ModelTypeEnum::MODEL_TYPE_CHART_TWO: + mdc = m_chartTwoModel; + break; } return mdc; @@ -590,7 +656,7 @@ BrowserTabContent::getModelForDisplay() const * chart. */ ModelChart* -BrowserTabContent::getDisplayedChartModel() +BrowserTabContent::getDisplayedChartOneModel() { ModelChart* mc = dynamic_cast(getModelForDisplay()); return mc; @@ -604,12 +670,40 @@ BrowserTabContent::getDisplayedChartModel() * chart. */ const ModelChart* -BrowserTabContent::getDisplayedChartModel() const +BrowserTabContent::getDisplayedChartOneModel() const { const ModelChart* mc = dynamic_cast(getModelForDisplay()); return mc; } +/** + * Get the displayed chart model two. + * + * @return Pointer to displayed chart model or + * NULL if the displayed model is NOT a + * chart. + */ +ModelChartTwo* +BrowserTabContent::getDisplayedChartTwoModel() +{ + ModelChartTwo* mc = dynamic_cast(getModelForDisplay()); + return mc; +} + +/** + * Get the displayed chart model two. + * + * @return Pointer to displayed chart model or + * NULL if the displayed model is NOT a + * chart. + */ +const ModelChartTwo* +BrowserTabContent::getDisplayedChartTwoModel() const +{ + const ModelChartTwo* mc = dynamic_cast(getModelForDisplay()); + return mc; +} + /** * Get the displayed surface model. * @@ -716,12 +810,26 @@ BrowserTabContent::isFlatSurfaceDisplayed() const } /** - * @return True if the displayed model is a chart + * @return True if the displayed model is a chart one + */ +bool +BrowserTabContent::isChartOneDisplayed() const +{ + const ModelChart* chartModel = getDisplayedChartOneModel(); + if (chartModel != NULL) { + return true; + } + + return false; +} + +/** + * @return True if the displayed model is a chart two */ bool -BrowserTabContent::isChartDisplayed() const +BrowserTabContent::isChartTwoDisplayed() const { - const ModelChart* chartModel = getDisplayedChartModel(); + const ModelChartTwo* chartModel = getDisplayedChartTwoModel(); if (chartModel != NULL) { return true; } @@ -729,6 +837,7 @@ BrowserTabContent::isChartDisplayed() const return false; } + /** * @return Is the displayed model a volume slice model? */ @@ -845,6 +954,51 @@ BrowserTabContent::getOverlaySet() const return NULL; } +/** + * @return Chart overlay set for this tab. + */ +ChartTwoOverlaySet* +BrowserTabContent::getChartTwoOverlaySet() +{ + if (m_chartTwoModel == NULL) { + return NULL; + } + + CaretAssert(m_chartTwoModel); + return m_chartTwoModel->getChartTwoOverlaySet(m_tabNumber); +} + +/** + * @return Chart overlay set for this tab. + */ +const ChartTwoOverlaySet* +BrowserTabContent::getChartTwoOverlaySet() const +{ + if (m_chartTwoModel == NULL) { + return NULL; + } + CaretAssert(m_chartTwoModel); + return m_chartTwoModel->getChartTwoOverlaySet(m_tabNumber); +} + +/** + * @return Chart two matrix display properties. + */ +ChartTwoMatrixDisplayProperties* +BrowserTabContent::getChartTwoMatrixDisplayProperties() +{ + return m_chartTwoMatrixDisplayProperties; +} + +/** + * @return Chart two matrix display properties (const method) + */ +const ChartTwoMatrixDisplayProperties* +BrowserTabContent::getChartTwoMatrixDisplayProperties() const +{ + return m_chartTwoMatrixDisplayProperties; +} + /** * Get the tab number for this content. * @@ -874,6 +1028,7 @@ BrowserTabContent::update(const std::vector models) m_wholeBrainModel = NULL; m_surfaceMontageModel = NULL; m_chartModel = NULL; + m_chartTwoModel = NULL; for (int i = 0; i < numModels; i++) { Model* mdc = models[i]; @@ -883,6 +1038,7 @@ BrowserTabContent::update(const std::vector models) ModelWholeBrain* mdcwb = dynamic_cast(mdc); ModelSurfaceMontage* mdcsm = dynamic_cast(mdc); ModelChart* mdch = dynamic_cast(mdc); + ModelChartTwo* mdchTwo = dynamic_cast(mdc); if (mdcs != NULL) { /* nothing to do since the surface model selector handles surfaces */ @@ -900,9 +1056,13 @@ BrowserTabContent::update(const std::vector models) m_surfaceMontageModel = mdcsm; } else if (mdch != NULL) { - CaretAssertMessage((m_chartModel == NULL), "There is more than one surface chart model."); + CaretAssertMessage((m_chartModel == NULL), "There is more than one chart model."); m_chartModel = mdch; } + else if (mdchTwo != NULL) { + CaretAssertMessage((m_chartTwoModel == NULL), "There is more than one chart two model."); + m_chartTwoModel = mdchTwo; + } else { CaretAssertMessage(0, (AString("Unknown type of brain model.") + mdc->getNameForGUI(true))); } @@ -936,11 +1096,16 @@ BrowserTabContent::update(const std::vector models) m_selectedModelType = ModelTypeEnum::MODEL_TYPE_INVALID; } break; + case ModelTypeEnum::MODEL_TYPE_CHART_TWO: + if (m_chartTwoModel == NULL) { + m_selectedModelType = ModelTypeEnum::MODEL_TYPE_INVALID; + } + break; } if (m_selectedModelType == ModelTypeEnum::MODEL_TYPE_INVALID) { - if (m_surfaceModelSelector->getSelectedSurfaceModel() != NULL) { - m_selectedModelType = ModelTypeEnum::MODEL_TYPE_SURFACE; + if (m_surfaceMontageModel != NULL) { + m_selectedModelType = ModelTypeEnum::MODEL_TYPE_SURFACE_MONTAGE; } else if (m_volumeModel != NULL) { m_selectedModelType = ModelTypeEnum::MODEL_TYPE_VOLUME_SLICES; @@ -948,8 +1113,11 @@ BrowserTabContent::update(const std::vector models) else if (m_wholeBrainModel != NULL) { m_selectedModelType = ModelTypeEnum::MODEL_TYPE_WHOLE_BRAIN; } - else if (m_surfaceMontageModel != NULL) { - m_selectedModelType = ModelTypeEnum::MODEL_TYPE_SURFACE_MONTAGE; + else if (m_chartTwoModel != NULL) { + m_selectedModelType = ModelTypeEnum::MODEL_TYPE_CHART_TWO; + } + else if (m_surfaceModelSelector->getSelectedSurfaceModel() != NULL) { + m_selectedModelType = ModelTypeEnum::MODEL_TYPE_SURFACE; } else if (m_chartModel != NULL) { m_selectedModelType = ModelTypeEnum::MODEL_TYPE_CHART; @@ -989,17 +1157,29 @@ BrowserTabContent::update(const std::vector models) } /** - * Is the chart model selection valid? + * Is the chart one model selection valid? * * @return bool indicating validity. */ bool -BrowserTabContent::isChartModelValid() const +BrowserTabContent::isChartOneModelValid() const { bool valid = (m_chartModel != NULL); return valid; } +/** + * Is the chart two model selection valid? + * + * @return bool indicating validity. + */ +bool +BrowserTabContent::isChartTwoModelValid() const +{ + bool valid = (m_chartTwoModel != NULL); + return valid; +} + /** * Is the surface model selection valid? * @@ -1227,12 +1407,16 @@ BrowserTabContent::getAnnotationColorBars(std::vector& colo } bool useOverlayFlag = false; - bool useChartsFlag = false; + bool useChartOneFlag = false; + bool useChartTwoFlag = false; switch (getSelectedModelType()) { case ModelTypeEnum::MODEL_TYPE_INVALID: break; case ModelTypeEnum::MODEL_TYPE_CHART: - useChartsFlag = true; + useChartOneFlag = true; + break; + case ModelTypeEnum::MODEL_TYPE_CHART_TWO: + useChartTwoFlag = true; break; case ModelTypeEnum::MODEL_TYPE_SURFACE: useOverlayFlag = true; @@ -1269,27 +1453,27 @@ BrowserTabContent::getAnnotationColorBars(std::vector& colo } } - if (useChartsFlag) { - ModelChart* modelChart = getDisplayedChartModel(); + if (useChartOneFlag) { + ModelChart* modelChart = getDisplayedChartOneModel(); if (modelChart != NULL) { CaretDataFileSelectionModel* fileModel = NULL; - switch (modelChart->getSelectedChartDataType(m_tabNumber)) { - case ChartDataTypeEnum::CHART_DATA_TYPE_INVALID: + switch (modelChart->getSelectedChartOneDataType(m_tabNumber)) { + case ChartOneDataTypeEnum::CHART_DATA_TYPE_INVALID: break; - case ChartDataTypeEnum::CHART_DATA_TYPE_LINE_DATA_SERIES: + case ChartOneDataTypeEnum::CHART_DATA_TYPE_LINE_DATA_SERIES: break; - case ChartDataTypeEnum::CHART_DATA_TYPE_LINE_FREQUENCY_SERIES: + case ChartOneDataTypeEnum::CHART_DATA_TYPE_LINE_FREQUENCY_SERIES: break; - case ChartDataTypeEnum::CHART_DATA_TYPE_LINE_TIME_SERIES: + case ChartOneDataTypeEnum::CHART_DATA_TYPE_LINE_TIME_SERIES: break; - case ChartDataTypeEnum::CHART_DATA_TYPE_MATRIX_LAYER: - if (modelChart->getSelectedChartDataType(m_tabNumber) == ChartDataTypeEnum::CHART_DATA_TYPE_MATRIX_LAYER) { + case ChartOneDataTypeEnum::CHART_DATA_TYPE_MATRIX_LAYER: + if (modelChart->getSelectedChartOneDataType(m_tabNumber) == ChartOneDataTypeEnum::CHART_DATA_TYPE_MATRIX_LAYER) { fileModel = modelChart->getChartableMatrixParcelFileSelectionModel(m_tabNumber); } break; - case ChartDataTypeEnum::CHART_DATA_TYPE_MATRIX_SERIES: - if (modelChart->getSelectedChartDataType(m_tabNumber) == ChartDataTypeEnum::CHART_DATA_TYPE_MATRIX_SERIES) { + case ChartOneDataTypeEnum::CHART_DATA_TYPE_MATRIX_SERIES: + if (modelChart->getSelectedChartOneDataType(m_tabNumber) == ChartOneDataTypeEnum::CHART_DATA_TYPE_MATRIX_SERIES) { fileModel = modelChart->getChartableMatrixSeriesFileSelectionModel(m_tabNumber); } } @@ -1315,6 +1499,72 @@ BrowserTabContent::getAnnotationColorBars(std::vector& colo } } + if (useChartTwoFlag) { + ModelChartTwo* modelChartTwo = getDisplayedChartTwoModel(); + if (modelChartTwo != NULL) { + ChartTwoOverlaySet* overlaySet = m_chartTwoModel->getChartTwoOverlaySet(m_tabNumber); + if (overlaySet != NULL) { + const int32_t numOverlays = overlaySet->getNumberOfDisplayedOverlays(); + for (int32_t i = 0; i < numOverlays; i++) { + ChartTwoOverlay* chartOverlay = overlaySet->getOverlay(i); + if (chartOverlay->isEnabled()) { + switch (chartOverlay->getChartTwoDataType()) { + case ChartTwoDataTypeEnum::CHART_DATA_TYPE_HISTOGRAM: + { + CaretMappableDataFile* mapFile = NULL; + ChartTwoOverlay::SelectedIndexType selectedIndexType = ChartTwoOverlay::SelectedIndexType::INVALID; + int32_t selectedIndex = -1; + chartOverlay->getSelectionData(mapFile, + selectedIndexType, + selectedIndex); + + + if (mapFile != NULL) { + if (mapFile->isMappedWithPalette()) { + AnnotationColorBar* colorBar = chartOverlay->getColorBar(); + colorBarMapFileInfo.push_back(ColorBarFileMap(colorBar, + mapFile, + selectedIndex)); + } + } + } + break; + case ChartTwoDataTypeEnum::CHART_DATA_TYPE_INVALID: + break; + case ChartTwoDataTypeEnum::CHART_DATA_TYPE_LINE_SERIES: + break; + case ChartTwoDataTypeEnum::CHART_DATA_TYPE_MATRIX: + { + CaretMappableDataFile* mapFile = NULL; + ChartTwoOverlay::SelectedIndexType selectedIndexType = ChartTwoOverlay::SelectedIndexType::INVALID; + int32_t selectedIndex = -1; + chartOverlay->getSelectionData(mapFile, + selectedIndexType, + selectedIndex); + + + if (mapFile != NULL) { + if (mapFile->isMappedWithPalette()) { + AnnotationColorBar* colorBar = chartOverlay->getColorBar(); + /* + * Matrix is all maps and uses map index 0 + */ + selectedIndex = 0; + + colorBarMapFileInfo.push_back(ColorBarFileMap(colorBar, + mapFile, + selectedIndex)); + } + } + } + break; + } + } + } + } + } + } + const int32_t numColorBarMapInfo = static_cast(colorBarMapFileInfo.size()); for (int32_t i = 0; i < numColorBarMapInfo; i++) { CaretAssertVectorIndex(colorBarMapFileInfo, i); @@ -1337,11 +1587,17 @@ BrowserTabContent::getAnnotationColorBars(std::vector& colo break; } - paletteColorMapping->setupAnnotationColorBar(statistics, - info.m_colorBar); - - info.m_colorBar->setTabIndex(m_tabNumber); - colorBarsOut.push_back(info.m_colorBar); + /* + * Statistics may be NULL for some instances of histograms + * from dynamically loaded files (dynconn) + */ + if (statistics != NULL) { + paletteColorMapping->setupAnnotationColorBar(statistics, + info.m_colorBar); + + info.m_colorBar->setTabIndex(m_tabNumber); + colorBarsOut.push_back(info.m_colorBar); + } } } } @@ -1372,12 +1628,16 @@ BrowserTabContent::getDisplayedPaletteMapFiles(std::vectorgetSelectedChartDataType(m_tabNumber)) { - case ChartDataTypeEnum::CHART_DATA_TYPE_INVALID: + switch (modelChart->getSelectedChartOneDataType(m_tabNumber)) { + case ChartOneDataTypeEnum::CHART_DATA_TYPE_INVALID: break; - case ChartDataTypeEnum::CHART_DATA_TYPE_LINE_DATA_SERIES: + case ChartOneDataTypeEnum::CHART_DATA_TYPE_LINE_DATA_SERIES: break; - case ChartDataTypeEnum::CHART_DATA_TYPE_LINE_FREQUENCY_SERIES: + case ChartOneDataTypeEnum::CHART_DATA_TYPE_LINE_FREQUENCY_SERIES: break; - case ChartDataTypeEnum::CHART_DATA_TYPE_LINE_TIME_SERIES: + case ChartOneDataTypeEnum::CHART_DATA_TYPE_LINE_TIME_SERIES: break; - case ChartDataTypeEnum::CHART_DATA_TYPE_MATRIX_LAYER: - if (modelChart->getSelectedChartDataType(m_tabNumber) == ChartDataTypeEnum::CHART_DATA_TYPE_MATRIX_LAYER) { + case ChartOneDataTypeEnum::CHART_DATA_TYPE_MATRIX_LAYER: + if (modelChart->getSelectedChartOneDataType(m_tabNumber) == ChartOneDataTypeEnum::CHART_DATA_TYPE_MATRIX_LAYER) { fileModel = modelChart->getChartableMatrixParcelFileSelectionModel(m_tabNumber); } break; - case ChartDataTypeEnum::CHART_DATA_TYPE_MATRIX_SERIES: - if (modelChart->getSelectedChartDataType(m_tabNumber) == ChartDataTypeEnum::CHART_DATA_TYPE_MATRIX_SERIES) { + case ChartOneDataTypeEnum::CHART_DATA_TYPE_MATRIX_SERIES: + if (modelChart->getSelectedChartOneDataType(m_tabNumber) == ChartOneDataTypeEnum::CHART_DATA_TYPE_MATRIX_SERIES) { fileModel = modelChart->getChartableMatrixSeriesFileSelectionModel(m_tabNumber); } } @@ -1465,6 +1725,67 @@ BrowserTabContent::getDisplayedPaletteMapFiles(std::vectorgetChartTwoOverlaySet(m_tabNumber); + const int32_t numOverlays = overlaySet->getNumberOfDisplayedOverlays(); + for (int32_t i = (numOverlays - 1); i >= 0; i--) { + ChartTwoOverlay* chartOverlay = overlaySet->getOverlay(i); + if (chartOverlay->isEnabled()) { + switch (chartOverlay->getChartTwoDataType()) { + case ChartTwoDataTypeEnum::CHART_DATA_TYPE_HISTOGRAM: + { + CaretMappableDataFile* mapFile = NULL; + ChartTwoOverlay::SelectedIndexType selectedIndexType = ChartTwoOverlay::SelectedIndexType::INVALID; + int32_t selectedIndex = -1; + chartOverlay->getSelectionData(mapFile, + selectedIndexType, + selectedIndex); + + if (mapFile != NULL) { + if (mapFile->isMappedWithPalette()) { + AnnotationColorBar* colorBar = chartOverlay->getColorBar(); + if (colorBar->isDisplayed()) { + mapFiles.push_back(mapFile); + mapIndices.push_back(selectedIndex); + } + } + } + } + break; + case ChartTwoDataTypeEnum::CHART_DATA_TYPE_INVALID: + break; + case ChartTwoDataTypeEnum::CHART_DATA_TYPE_LINE_SERIES: + break; + case ChartTwoDataTypeEnum::CHART_DATA_TYPE_MATRIX: + { + CaretMappableDataFile* mapFile = NULL; + ChartTwoOverlay::SelectedIndexType selectedIndexType = ChartTwoOverlay::SelectedIndexType::INVALID; + int32_t selectedIndex = -1; + chartOverlay->getSelectionData(mapFile, + selectedIndexType, + selectedIndex); + + if (mapFile != NULL) { + if (mapFile->isMappedWithPalette()) { + AnnotationColorBar* colorBar = chartOverlay->getColorBar(); + if (colorBar->isDisplayed()) { + mapFiles.push_back(mapFile); + mapIndices.push_back(0); + } + } + } + } + break; + } + } + } + } + } + + CaretAssert(mapFiles.size() == mapIndices.size()); } /** @@ -1619,6 +1940,29 @@ BrowserTabContent::getFilesDisplayedInTab(std::vector& displayed break; case ModelTypeEnum::MODEL_TYPE_CHART: break; + case ModelTypeEnum::MODEL_TYPE_CHART_TWO: + { + const ChartTwoOverlaySet* overlaySet = m_chartTwoModel->getChartTwoOverlaySet(tabIndex); + if (overlaySet != NULL) { + const int32_t numOverlays = overlaySet->getNumberOfDisplayedOverlays(); + for (int32_t i = 0; i < numOverlays; i++) { + const ChartTwoOverlay* chartOverlay = overlaySet->getOverlay(i); + if (chartOverlay->isEnabled()) { + CaretMappableDataFile* mapFile = NULL; + ChartTwoOverlay::SelectedIndexType selectedIndexType = ChartTwoOverlay::SelectedIndexType::INVALID; + int32_t selectedIndex = -1; + chartOverlay->getSelectionData(mapFile, + selectedIndexType, + selectedIndex); + + if (mapFile != NULL) { + displayedDataFiles.insert(mapFile); + } + } + } + } + } + break; } /* @@ -1646,6 +1990,21 @@ BrowserTabContent::getFilesDisplayedInTab(std::vector& displayed } displayedDataFiles.insert(overlayDataFile); + + if (overlayDataFile->isMappedWithPalette()) { + /* + * If mapped with palette, there may be thresholding with another file + * so need to include that file + */ + if (overlayDataFile->getMapPaletteColorMapping(mapIndex)->getThresholdType() == PaletteThresholdTypeEnum::THRESHOLD_TYPE_FILE) { + CaretMappableDataFileAndMapSelectionModel* mapFileSelector = overlayDataFile->getMapThresholdFileSelectionModel(mapIndex); + CaretAssert(mapFileSelector); + CaretMappableDataFile* thresholdFile = mapFileSelector->getSelectedFile(); + if (thresholdFile != NULL) { + displayedDataFiles.insert(thresholdFile); + } + } + } } } } @@ -1715,6 +2074,9 @@ BrowserTabContent::getViewingTransformation() else if (isCerebellumDisplayed()) { return m_cerebellumViewingTransformation; } + else if (isChartTwoDisplayed()) { + return m_chartTwoMatrixViewingTranformation; + } return m_viewingTransformation; } @@ -1730,6 +2092,9 @@ BrowserTabContent::getViewingTransformation() const else if (isCerebellumDisplayed()) { return m_cerebellumViewingTransformation; } + else if (isChartTwoDisplayed()) { + return m_chartTwoMatrixViewingTranformation; + } return m_viewingTransformation; } @@ -1765,7 +2130,7 @@ void BrowserTabContent::setTranslation( const float translation[3]) { getViewingTransformation()->setTranslation(translation); - updateYokedBrowserTabs(); + updateBrainModelYokedBrowserTabs(); } /** @@ -1786,7 +2151,7 @@ BrowserTabContent::setTranslation(const float translationX, getViewingTransformation()->setTranslation(translationX, translationY, translationZ); - updateYokedBrowserTabs(); + updateYokedModelBrowserTabs(); } /** @@ -1807,7 +2172,7 @@ void BrowserTabContent::setScaling(const float scaling) { return getViewingTransformation()->setScaling(scaling); - updateYokedBrowserTabs(); + updateYokedModelBrowserTabs(); } /** @@ -1829,7 +2194,7 @@ void BrowserTabContent::setRotationMatrix(const Matrix4x4& rotationMatrix) { getViewingTransformation()->setRotationMatrix(rotationMatrix); - updateYokedBrowserTabs(); + updateYokedModelBrowserTabs(); } /** @@ -1852,7 +2217,7 @@ BrowserTabContent::setObliqueVolumeRotationMatrix(const Matrix4x4& obliqueRotati { *m_obliqueVolumeRotationMatrix = obliqueRotationMatrix; - updateYokedBrowserTabs(); + updateBrainModelYokedBrowserTabs(); } /** @@ -1883,7 +2248,7 @@ BrowserTabContent::setRightCortexFlatMapOffset(const float offsetX, const float offsetY) { getViewingTransformation()->setRightCortexFlatMapOffset(offsetX, offsetY); - updateYokedBrowserTabs(); + updateBrainModelYokedBrowserTabs(); } /** @@ -1917,7 +2282,7 @@ BrowserTabContent::resetView() if (isVolumeSlicesDisplayed()) { m_obliqueVolumeRotationMatrix->identity(); } - updateYokedBrowserTabs(); + updateYokedModelBrowserTabs(); } /** @@ -1930,7 +2295,7 @@ BrowserTabContent::rightView() return; } getViewingTransformation()->rightView(); - updateYokedBrowserTabs(); + updateYokedModelBrowserTabs(); } /** @@ -1943,7 +2308,7 @@ BrowserTabContent::leftView() return; } getViewingTransformation()->leftView(); - updateYokedBrowserTabs(); + updateYokedModelBrowserTabs(); } /** @@ -1956,7 +2321,7 @@ BrowserTabContent::anteriorView() return; } getViewingTransformation()->anteriorView(); - updateYokedBrowserTabs(); + updateYokedModelBrowserTabs(); } /** @@ -1969,7 +2334,7 @@ BrowserTabContent::posteriorView() return; } getViewingTransformation()->posteriorView(); - updateYokedBrowserTabs(); + updateYokedModelBrowserTabs(); } /** @@ -1982,7 +2347,7 @@ BrowserTabContent::dorsalView() return; } getViewingTransformation()->dorsalView(); - updateYokedBrowserTabs(); + updateYokedModelBrowserTabs(); } /** @@ -1995,87 +2360,7 @@ BrowserTabContent::ventralView() return; } getViewingTransformation()->ventralView(); - updateYokedBrowserTabs(); -} - -/* - * @return The slice view plane for the given viewport coordinate. - * If ALL is returned, is indicates that the given viewport coordinate - * is in the bottom left region in which volume slices are not displayed. - * - * @param viewport - * The viewport. - * @param mousePressX - * X Location of the mouse press. - * @param mousePressY - * Y Location of the mouse press. - */ -VolumeSliceViewPlaneEnum::Enum -BrowserTabContent::getSliceViewPlaneForVolumeAllSliceView(const int32_t viewport[4], - const int32_t mousePressX, - const int32_t mousePressY, - int32_t sliceViewportOut[4]) const -{ - VolumeSliceViewPlaneEnum::Enum view = VolumeSliceViewPlaneEnum::ALL; - - const int32_t halfWidth = viewport[2] / 2; - const int32_t halfHeight = viewport[3] / 2; - const int32_t viewportMousePressedX = mousePressX - viewport[0]; - const int32_t viewportMousePressedY = mousePressY - viewport[1]; - bool isRight = false; - bool isTop = false; - if (viewportMousePressedX > halfWidth) { - isRight = true; - } - if (viewportMousePressedY > halfHeight) { - isTop = true; - } - - /* - * Top Right is Coronal - * Top Left is Parasagittal - * Bottom Right is Axial - * Bottom Left is Empty or Surfaces - */ - if (isTop) { - if (isRight) { - view = VolumeSliceViewPlaneEnum::CORONAL; - } - else { - view = VolumeSliceViewPlaneEnum::PARASAGITTAL; - } - } - else { - if (isRight) { - view = VolumeSliceViewPlaneEnum::AXIAL; - } - else { - - } - } - - sliceViewportOut[0] = viewport[0]; - sliceViewportOut[1] = viewport[1]; - sliceViewportOut[2] = halfWidth; - sliceViewportOut[3] = halfHeight; - - switch (view) { - case VolumeSliceViewPlaneEnum::ALL: - sliceViewportOut[2] = viewport[2]; - sliceViewportOut[3] = viewport[3]; - break; - case VolumeSliceViewPlaneEnum::AXIAL: - sliceViewportOut[0] = halfWidth; - break; - case VolumeSliceViewPlaneEnum::CORONAL: - sliceViewportOut[0] = halfWidth; - sliceViewportOut[1] = halfHeight; - break; - case VolumeSliceViewPlaneEnum::PARASAGITTAL: - sliceViewportOut[1] = halfHeight; - break; - } - return view; + updateYokedModelBrowserTabs(); } /** @@ -2117,10 +2402,11 @@ BrowserTabContent::applyMouseRotation(BrainOpenGLViewportContent* viewportConten viewport[3] }; if (slicePlane == VolumeSliceViewPlaneEnum::ALL) { - slicePlane = getSliceViewPlaneForVolumeAllSliceView(viewport, - mousePressX, - mousePressY, - sliceViewport); + slicePlane = BrainOpenGLViewportContent::getSliceViewPlaneForVolumeAllSliceView(viewport, + getSlicePlanesAllViewLayout(), + mousePressX, + mousePressY, + sliceViewport); } Matrix4x4 rotationMatrix = getObliqueVolumeRotationMatrix(); @@ -2140,20 +2426,20 @@ BrowserTabContent::applyMouseRotation(BrainOpenGLViewportContent* viewportConten * Need to account for the quadrants!!!! */ const float viewportCenter[3] = { - sliceViewport[0] + sliceViewport[2] / 2, - sliceViewport[1] + sliceViewport[3] / 2, + (float)(sliceViewport[0] + sliceViewport[2] / 2), + ((float)sliceViewport[1] + sliceViewport[3] / 2), 0.0 }; const float oldPos[3] = { - previousMouseX, - previousMouseY, + (float)previousMouseX, + (float)previousMouseY, 0.0 }; const float newPos[3] = { - mouseX, - mouseY, + (float)mouseX, + (float)mouseY, 0.0 }; @@ -2247,7 +2533,8 @@ BrowserTabContent::applyMouseRotation(BrainOpenGLViewportContent* viewportConten break; } } - else if (isChartDisplayed()) { + else if (isChartOneDisplayed() + || isChartTwoDisplayed()) { /* no rotation for chart */ } else if (isCerebellumDisplayed()) { @@ -2420,7 +2707,7 @@ BrowserTabContent::applyMouseRotation(BrainOpenGLViewportContent* viewportConten viewingTransform->setRotationMatrix(rotationMatrix); } } - updateYokedBrowserTabs(); + updateYokedModelBrowserTabs(); } /** @@ -2435,33 +2722,43 @@ void BrowserTabContent::applyMouseScaling(const int32_t /*mouseDX*/, const int32_t mouseDY) { - if (isChartDisplayed()) { - ModelChart* modelChart = getDisplayedChartModel(); + if (isChartOneDisplayed()) { + ModelChart* modelChart = getDisplayedChartOneModel(); CaretAssert(modelChart); CaretDataFileSelectionModel* matrixSelectionModel = NULL; - if (modelChart->getSelectedChartDataType(m_tabNumber) == ChartDataTypeEnum::CHART_DATA_TYPE_MATRIX_LAYER) { + if (modelChart->getSelectedChartOneDataType(m_tabNumber) == ChartOneDataTypeEnum::CHART_DATA_TYPE_MATRIX_LAYER) { matrixSelectionModel = modelChart->getChartableMatrixParcelFileSelectionModel(m_tabNumber); } - if (modelChart->getSelectedChartDataType(m_tabNumber) == ChartDataTypeEnum::CHART_DATA_TYPE_MATRIX_SERIES) { + if (modelChart->getSelectedChartOneDataType(m_tabNumber) == ChartOneDataTypeEnum::CHART_DATA_TYPE_MATRIX_SERIES) { matrixSelectionModel = modelChart->getChartableMatrixSeriesFileSelectionModel(m_tabNumber); } - if (matrixSelectionModel != NULL) { - ChartableMatrixInterface* chartableInterface = matrixSelectionModel->getSelectedFileOfType(); - if (chartableInterface != NULL) { - ChartMatrixDisplayProperties* matrixProperties = chartableInterface->getChartMatrixDisplayProperties(m_tabNumber); - matrixProperties->setScaleMode(ChartMatrixScaleModeEnum::CHART_MATRIX_SCALE_MANUAL); - float scaling = matrixProperties->getViewZooming(); - if (mouseDY != 0.0) { - scaling *= (1.0f + (mouseDY * 0.01)); - } - if (scaling < 0.01) { - scaling = 0.01; - } - matrixProperties->setViewZooming(scaling); + if (matrixSelectionModel != NULL) { + ChartableMatrixInterface* chartableInterface = matrixSelectionModel->getSelectedFileOfType(); + if (chartableInterface != NULL) { + ChartMatrixDisplayProperties* matrixProperties = chartableInterface->getChartMatrixDisplayProperties(m_tabNumber); + matrixProperties->setScaleMode(ChartMatrixScaleModeEnum::CHART_MATRIX_SCALE_MANUAL); + float scaling = matrixProperties->getViewZooming(); + if (mouseDY != 0.0) { + scaling *= (1.0f + (mouseDY * 0.01)); + } + if (scaling < 0.01) { + scaling = 0.01; } + matrixProperties->setViewZooming(scaling); } + } + } + else if (isChartTwoDisplayed()) { + float scaling = getViewingTransformation()->getScaling(); + if (mouseDY != 0.0) { + scaling *= (1.0f + (mouseDY * 0.01)); + } + if (scaling < 0.01) { + scaling = 0.01; + } + getViewingTransformation()->setScaling(scaling); } else { float scaling = getViewingTransformation()->getScaling(); @@ -2473,7 +2770,7 @@ BrowserTabContent::applyMouseScaling(const int32_t /*mouseDX*/, } getViewingTransformation()->setScaling(scaling); } - updateYokedBrowserTabs(); + updateYokedModelBrowserTabs(); } /** @@ -2512,44 +2809,24 @@ BrowserTabContent::applyMouseTranslation(BrainOpenGLViewportContent* viewportCon float dx = 0.0; float dy = 0.0; float dz = 0.0; - switch (this->getSliceViewPlane()) - { + + int viewport[4]; + viewportContent->getModelViewport(viewport); + int sliceViewport[4]; + viewportContent->getModelViewport(sliceViewport); + + VolumeSliceViewPlaneEnum::Enum slicePlane = getSliceViewPlane(); + if (slicePlane == VolumeSliceViewPlaneEnum::ALL) { + slicePlane = BrainOpenGLViewportContent::getSliceViewPlaneForVolumeAllSliceView(viewport, + getSlicePlanesAllViewLayout(), + mousePressX, + mousePressY, + sliceViewport); + } + + switch (slicePlane) { case VolumeSliceViewPlaneEnum::ALL: - { - int viewport[4]; - viewportContent->getModelViewport(viewport); - const int32_t halfWidth = viewport[2] / 2; - const int32_t halfHeight = viewport[3] / 2; - const int32_t viewportMousePressedX = mousePressX - viewport[0]; - const int32_t viewportMousePressedY = mousePressY - viewport[1]; - bool isRight = false; - bool isTop = false; - if (viewportMousePressedX > halfWidth) { - isRight = true; - } - if (viewportMousePressedY > halfHeight) { - isTop = true; - } - //CaretLogInfo("right: " + AString::fromBool(isRight) + " top: " + AString::fromBool(isTop)); - if (isTop) - { - if (isRight)//coronal - { - dx = mouseDX * slowdown; - dz = mouseDY * slowdown; - } else {//parasaggital - dy = -mouseDX * slowdown; - dz = mouseDY * slowdown; - } - } else { - if (isRight)//axial - { - dx = mouseDX * slowdown; - dy = mouseDY * slowdown; - }//bottom left has no slice - } break; - } case VolumeSliceViewPlaneEnum::AXIAL: dx = mouseDX * slowdown; dy = mouseDY * slowdown; @@ -2571,16 +2848,16 @@ BrowserTabContent::applyMouseTranslation(BrainOpenGLViewportContent* viewportCon translation[2] += dz; m_volumeSliceViewingTransformation->setTranslation(translation); } - else if (isChartDisplayed()) { - ModelChart* modelChart = getDisplayedChartModel(); + else if (isChartOneDisplayed()) { + ModelChart* modelChart = getDisplayedChartOneModel(); CaretAssert(modelChart); CaretDataFileSelectionModel* matrixSelectionModel = NULL; - if (modelChart->getSelectedChartDataType(m_tabNumber) == ChartDataTypeEnum::CHART_DATA_TYPE_MATRIX_LAYER) { + if (modelChart->getSelectedChartOneDataType(m_tabNumber) == ChartOneDataTypeEnum::CHART_DATA_TYPE_MATRIX_LAYER) { matrixSelectionModel = modelChart->getChartableMatrixParcelFileSelectionModel(m_tabNumber); } - if (modelChart->getSelectedChartDataType(m_tabNumber) == ChartDataTypeEnum::CHART_DATA_TYPE_MATRIX_SERIES) { + if (modelChart->getSelectedChartOneDataType(m_tabNumber) == ChartOneDataTypeEnum::CHART_DATA_TYPE_MATRIX_SERIES) { matrixSelectionModel = modelChart->getChartableMatrixSeriesFileSelectionModel(m_tabNumber); } if (matrixSelectionModel != NULL) { @@ -2596,6 +2873,14 @@ BrowserTabContent::applyMouseTranslation(BrainOpenGLViewportContent* viewportCon } } } + else if (isChartTwoDisplayed()) { + float translation[3]; + m_chartTwoMatrixViewingTranformation->getTranslation(translation); + translation[0] += mouseDX; + translation[1] += mouseDY; + translation[2] = 0; // NO Z-translation + m_chartTwoMatrixViewingTranformation->setTranslation(translation); + } else if (isCerebellumDisplayed()) { const float screenDX = mouseDX; const float screenDY = mouseDY; @@ -2768,7 +3053,7 @@ BrowserTabContent::applyMouseTranslation(BrainOpenGLViewportContent* viewportCon getViewingTransformation()->setTranslation(translation); } } - updateYokedBrowserTabs(); + updateYokedModelBrowserTabs(); } /** @@ -2803,6 +3088,15 @@ BrowserTabContent::getTransformationsForOpenGLDrawing(const ProjectionViewTypeEn return; } + if (isChartTwoDisplayed()) { + m_chartTwoMatrixViewingTranformation->getTranslation(translationOut); + Matrix4x4 matrix; + matrix.identity(); + matrix.getMatrixForOpenGL(rotationMatrixOut); + scalingOut = m_chartTwoMatrixViewingTranformation->getScaling(); + return; + } + /* * Surfaces may need a modification to the rotation matrix * dependent upon the projection view type. @@ -2965,7 +3259,7 @@ BrowserTabContent::setTransformationsFromModelTransform(const ModelTransform& mo const float rightFlatZoom = modelTransform.getRightCortexFlatMapZoomFactor(); setRightCortexFlatMapZoomFactor(rightFlatZoom); - updateYokedBrowserTabs(); + updateYokedModelBrowserTabs(); } @@ -3025,9 +3319,28 @@ BrowserTabContent::restoreFromScene(const SceneAttributes* sceneAttributes, return; } - m_sceneClassAssistant->restoreMembers(sceneAttributes, + m_brainModelYokingGroup = YokingGroupEnum::YOKING_GROUP_A; + m_chartModelYokingGroup = YokingGroupEnum::YOKING_GROUP_OFF; + + m_sceneClassAssistant->restoreMembers(sceneAttributes, sceneClass); + /* + * With charting version two, yoking was split into chart and non-chart yoking + * If old yoking group is found, apply it to the brain model yoking group + */ + const AString oldYokingGroupName = sceneClass->getEnumeratedTypeValueAsString("m_yokingGroup", + "XXXXXX"); + if ( ! oldYokingGroupName.isEmpty()) { + bool validFlag = false; + const YokingGroupEnum::Enum oldYokeGroup = YokingGroupEnum::fromName(oldYokingGroupName, + &validFlag); + if (validFlag) { + m_brainModelYokingGroup = oldYokeGroup; + m_chartModelYokingGroup = oldYokeGroup; + } + } + m_obliqueVolumeRotationMatrix->identity(); float obliqueMatrix[16]; const int32_t numInObliqueArray = sceneClass->getFloatArrayValue("m_obliqueVolumeRotationMatrix", obliqueMatrix, 16); @@ -3172,6 +3485,8 @@ BrowserTabContent::restoreFromScene(const SceneAttributes* sceneAttributes, switch (getSelectedModelType()) { case ModelTypeEnum::MODEL_TYPE_CHART: break; + case ModelTypeEnum::MODEL_TYPE_CHART_TWO: + break; case ModelTypeEnum::MODEL_TYPE_INVALID: break; case ModelTypeEnum::MODEL_TYPE_SURFACE: @@ -3261,6 +3576,7 @@ BrowserTabContent::restoreFromScene(const SceneAttributes* sceneAttributes, } } } + } /** @@ -3332,7 +3648,7 @@ BrowserTabContent::setClippingPlaneEnabled(const bool xEnabled, m_clippingPlaneGroup->setVolumeSelected(volumeEnabled); m_clippingPlaneGroup->setFeaturesSelected(featuresEnabled); - updateYokedBrowserTabs(); + updateYokedModelBrowserTabs(); } /** @@ -3380,7 +3696,7 @@ BrowserTabContent::setClippingPlaneTransformation(const float panning[3], m_clippingPlaneGroup->setDisplayClippingBoxSelected(displayClippingBox); - updateYokedBrowserTabs(); + updateYokedModelBrowserTabs(); } /** @@ -3403,7 +3719,7 @@ void BrowserTabContent::resetClippingPlaneTransformation() { m_clippingPlaneGroup->resetTransformation(); - updateYokedBrowserTabs(); + updateYokedModelBrowserTabs(); } /** @@ -3457,7 +3773,29 @@ void BrowserTabContent::setSliceViewPlane(const VolumeSliceViewPlaneEnum::Enum slicePlane) { m_volumeSliceSettings->setSliceViewPlane(slicePlane); - updateYokedBrowserTabs(); + updateBrainModelYokedBrowserTabs(); +} + +/** + * @return The layout for all slices view (grid, row, column) + */ +VolumeSliceViewAllPlanesLayoutEnum::Enum +BrowserTabContent::getSlicePlanesAllViewLayout() const +{ + return m_volumeSliceSettings->getSlicePlanesAllViewLayout(); +} + +/** + * Set the layout for all slices view (grid, row, column) + * + * @param slicePlanesAllViewLayout + * New value for layout. + */ +void +BrowserTabContent::setSlicePlanesAllViewLayout(const VolumeSliceViewAllPlanesLayoutEnum::Enum slicePlanesAllViewLayout) +{ + m_volumeSliceSettings->setSlicePlanesAllViewLayout(slicePlanesAllViewLayout); + updateBrainModelYokedBrowserTabs(); } /** @@ -3479,7 +3817,7 @@ void BrowserTabContent::setSliceDrawingType(const VolumeSliceDrawingTypeEnum::Enum sliceDrawingType) { m_volumeSliceSettings->setSliceDrawingType(sliceDrawingType); - updateYokedBrowserTabs(); + updateBrainModelYokedBrowserTabs(); } /** @@ -3501,7 +3839,29 @@ void BrowserTabContent::setSliceProjectionType(const VolumeSliceProjectionTypeEnum::Enum sliceProjectionType) { m_volumeSliceSettings->setSliceProjectionType(sliceProjectionType); - updateYokedBrowserTabs(); + updateBrainModelYokedBrowserTabs(); +} + +/** + * @return The masking used when drawing an oblique volume slice + */ +VolumeSliceInterpolationEdgeEffectsMaskingEnum::Enum +BrowserTabContent::getVolumeSliceInterpolationEdgeEffectsMaskingType() const +{ + return m_volumeSliceSettings->getVolumeSliceInterpolationEdgeEffectsMaskingType(); +} + +/** + * Set the masking used when drawing an oblique volume slice. + * + * @param maskingType + * Type of masking. + */ +void +BrowserTabContent::setVolumeSliceInterpolationEdgeEffectsMaskingType(const VolumeSliceInterpolationEdgeEffectsMaskingEnum::Enum maskingType) +{ + m_volumeSliceSettings->setVolumeSliceInterpolationEdgeEffectsMaskingType(maskingType); + updateBrainModelYokedBrowserTabs(); } /** @@ -3522,7 +3882,7 @@ void BrowserTabContent::setMontageNumberOfColumns(const int32_t montageNumberOfColumns) { m_volumeSliceSettings->setMontageNumberOfColumns(montageNumberOfColumns); - updateYokedBrowserTabs(); + updateBrainModelYokedBrowserTabs(); } /** @@ -3543,7 +3903,7 @@ void BrowserTabContent::setMontageNumberOfRows(const int32_t montageNumberOfRows) { m_volumeSliceSettings->setMontageNumberOfRows(montageNumberOfRows); - updateYokedBrowserTabs(); + updateBrainModelYokedBrowserTabs(); } /** @@ -3564,7 +3924,7 @@ void BrowserTabContent::setMontageSliceSpacing(const int32_t montageSliceSpacing) { m_volumeSliceSettings->setMontageSliceSpacing(montageSliceSpacing); - updateYokedBrowserTabs(); + updateBrainModelYokedBrowserTabs(); } /** @@ -3574,7 +3934,7 @@ void BrowserTabContent::setSlicesToOrigin() { selectSlicesAtOrigin(); - updateYokedBrowserTabs(); + updateBrainModelYokedBrowserTabs(); } /** @@ -3588,7 +3948,7 @@ BrowserTabContent::reset() m_volumeSliceSettings->reset(); m_obliqueVolumeRotationMatrix->identity(); } - updateYokedBrowserTabs(); + updateBrainModelYokedBrowserTabs(); } /** @@ -3610,7 +3970,7 @@ void BrowserTabContent::selectSlicesAtOrigin() { m_volumeSliceSettings->selectSlicesAtOrigin(); - updateYokedBrowserTabs(); + updateBrainModelYokedBrowserTabs(); } /** @@ -3622,7 +3982,7 @@ void BrowserTabContent::selectSlicesAtCoordinate(const float xyz[3]) { m_volumeSliceSettings->selectSlicesAtCoordinate(xyz); - updateYokedBrowserTabs(); + updateBrainModelYokedBrowserTabs(); } /** @@ -3646,9 +4006,119 @@ void BrowserTabContent::setIdentificationUpdatesVolumeSlices(const bool status) { m_identificationUpdatesVolumeSlices = status; - updateYokedBrowserTabs(); + updateBrainModelYokedBrowserTabs(); +} + +/** + * @return Is volume axis crosshairs displayed + */ +bool +BrowserTabContent::isVolumeAxesCrosshairsDisplayed() const +{ + return m_displayVolumeAxesCrosshairs; +} + +/** + * Set volume axis crosshairs displayed + * + * @param displayed + * New status + */ +void +BrowserTabContent::setVolumeAxesCrosshairsDisplayed(const bool displayed) +{ + m_displayVolumeAxesCrosshairs = displayed; + updateBrainModelYokedBrowserTabs(); +} + +/** + * @return Is volume axis crosshairs labels displayed + */ +bool +BrowserTabContent::isVolumeAxesCrosshairLabelsDisplayed() const +{ + return m_displayVolumeAxesCrosshairLabels; +} + +/** + * Set volume axis crosshairs labels displayed + * + * @param displayed + * New status + */ +void +BrowserTabContent::setVolumeAxesCrosshairLabelsDisplayed(const bool displayed) +{ + m_displayVolumeAxesCrosshairLabels = displayed; + updateBrainModelYokedBrowserTabs(); +} + +/** + * @return Is volume montage axes coordinates displayed + */ +bool +BrowserTabContent::isVolumeMontageAxesCoordinatesDisplayed() const +{ + return m_displayVolumeMontageAxesCoordinates; +} + +/** + * Set volume montage axes coordinates displayed + * + * @param displayed + * New status + */ +void +BrowserTabContent::setVolumeMontageAxesCoordinatesDisplayed(const bool displayed) +{ + m_displayVolumeMontageAxesCoordinates = displayed; + updateBrainModelYokedBrowserTabs(); +} + +/** + * @return Digits right of decimal for montage coordinates + */ +int32_t +BrowserTabContent::getVolumeMontageCoordinatePrecision() const +{ + return m_volumeMontageCoordinatePrecision; +} + +/** + * Set digits right of decimal for montage coordinates + * + * @param volumeMontageCoordinatePrecision + * New precision + */ +void +BrowserTabContent::setVolumeMontageCoordinatePrecision(const int32_t volumeMontageCoordinatePrecision) +{ + m_volumeMontageCoordinatePrecision = volumeMontageCoordinatePrecision; + updateBrainModelYokedBrowserTabs(); +} + +/** + * @return Is lighting enabled ? + */ +bool +BrowserTabContent::isLightingEnabled() const +{ + return m_lightingEnabled; +} + +/** + * Set lighting enabled. + * + * @param lightingEnabled + * New status for lighting. + */ +void +BrowserTabContent::setLightingEnabled(const bool lightingEnabled) +{ + m_lightingEnabled = lightingEnabled; } + /** * Return the axial slice index. * @return @@ -3670,7 +4140,7 @@ BrowserTabContent::setSliceIndexAxial(const VolumeMappableInterface* volumeFile, const int64_t sliceIndexAxial) { m_volumeSliceSettings->setSliceIndexAxial(volumeFile, sliceIndexAxial); - updateYokedBrowserTabs(); + updateBrainModelYokedBrowserTabs(); } /** @@ -3695,7 +4165,7 @@ BrowserTabContent::setSliceIndexCoronal(const VolumeMappableInterface* volumeFil const int64_t sliceIndexCoronal) { m_volumeSliceSettings->setSliceIndexCoronal(volumeFile, sliceIndexCoronal); - updateYokedBrowserTabs(); + updateBrainModelYokedBrowserTabs(); } /** @@ -3720,7 +4190,7 @@ BrowserTabContent::setSliceIndexParasagittal(const VolumeMappableInterface* volu { m_volumeSliceSettings->setSliceIndexParasagittal(volumeFile, sliceIndexParasagittal); - updateYokedBrowserTabs(); + updateBrainModelYokedBrowserTabs(); } /** @@ -3741,7 +4211,7 @@ void BrowserTabContent::setSliceCoordinateAxial(const float z) { m_volumeSliceSettings->setSliceCoordinateAxial(z); - updateYokedBrowserTabs(); + updateBrainModelYokedBrowserTabs(); } /** @@ -3762,7 +4232,7 @@ void BrowserTabContent::setSliceCoordinateCoronal(const float y) { m_volumeSliceSettings->setSliceCoordinateCoronal(y); - updateYokedBrowserTabs(); + updateBrainModelYokedBrowserTabs(); } /** @@ -3783,7 +4253,7 @@ void BrowserTabContent::setSliceCoordinateParasagittal(const float x) { m_volumeSliceSettings->setSliceCoordinateParasagittal(x); - updateYokedBrowserTabs(); + updateBrainModelYokedBrowserTabs(); } /** @@ -3806,7 +4276,7 @@ void BrowserTabContent::setSliceParasagittalEnabled(const bool sliceEnabledParasagittal) { m_volumeSliceSettings->setSliceParasagittalEnabled(sliceEnabledParasagittal); - updateYokedBrowserTabs(); + updateBrainModelYokedBrowserTabs(); } /** @@ -3829,7 +4299,7 @@ void BrowserTabContent::setSliceCoronalEnabled(const bool sliceEnabledCoronal) { m_volumeSliceSettings->setSliceCoronalEnabled(sliceEnabledCoronal); - updateYokedBrowserTabs(); + updateBrainModelYokedBrowserTabs(); } /** @@ -3852,7 +4322,7 @@ void BrowserTabContent::setSliceAxialEnabled(const bool sliceEnabledAxial) { m_volumeSliceSettings->setSliceAxialEnabled(sliceEnabledAxial); - updateYokedBrowserTabs(); + updateBrainModelYokedBrowserTabs(); } @@ -3877,7 +4347,7 @@ void BrowserTabContent::setWholeBrainLeftEnabled(const bool enabled) { m_wholeBrainSurfaceSettings->setLeftEnabled(enabled); - updateYokedBrowserTabs(); + updateBrainModelYokedBrowserTabs(); } /** @@ -3898,7 +4368,7 @@ void BrowserTabContent::setWholeBrainRightEnabled(const bool enabled) { m_wholeBrainSurfaceSettings->setRightEnabled(enabled); - updateYokedBrowserTabs(); + updateBrainModelYokedBrowserTabs(); } /** @@ -3919,7 +4389,7 @@ void BrowserTabContent::setWholeBrainCerebellumEnabled(const bool enabled) { m_wholeBrainSurfaceSettings->setCerebellumEnabled(enabled); - updateYokedBrowserTabs(); + updateBrainModelYokedBrowserTabs(); } /** @@ -3940,7 +4410,7 @@ void BrowserTabContent::setWholeBrainLeftRightSeparation(const float separation) { m_wholeBrainSurfaceSettings->setLeftRightSeparation(separation); - updateYokedBrowserTabs(); + updateBrainModelYokedBrowserTabs(); } /** @@ -3961,30 +4431,88 @@ void BrowserTabContent::setWholeBrainCerebellumSeparation(const float separation) { m_wholeBrainSurfaceSettings->setCerebellumSeparation(separation); - updateYokedBrowserTabs(); + updateBrainModelYokedBrowserTabs(); +} + +/** + * @return Selected yoking group for charts + */ +YokingGroupEnum::Enum +BrowserTabContent::getChartModelYokingGroup() const +{ + return m_chartModelYokingGroup; +} + +/** + * Set the selected yoking group for charts. + * + * @param chartModelYokingType + * New value for yoking group. + */ +void +BrowserTabContent::setChartModelYokingGroup(const YokingGroupEnum::Enum chartModelYokingType) +{ + m_chartModelYokingGroup = chartModelYokingType; + + if (m_chartModelYokingGroup == YokingGroupEnum::YOKING_GROUP_OFF) { + return; + } + + int32_t copyFromTabIndex = -1; + + /* + * Find another browser tab using the same yoking as 'me' and copy + * yoked data from the other browser tab. + */ + for (std::set::iterator iter = s_allBrowserTabContent.begin(); + iter != s_allBrowserTabContent.end(); + iter++) { + BrowserTabContent* btc = *iter; + if (btc != this) { + if (btc->getChartModelYokingGroup() == m_chartModelYokingGroup) { + copyFromTabIndex = btc->getTabNumber(); + /* + * If anything is added, also need to update updateYokedBrowserTabs() + */ + *m_chartTwoMatrixViewingTranformation = *btc->m_chartTwoMatrixViewingTranformation; + *m_chartTwoMatrixDisplayProperties = *btc->m_chartTwoMatrixDisplayProperties; + break; + } + } + } + + if (copyFromTabIndex >= 0) { + /* + * Maybe NULL when restoring scenes + */ + if (m_chartTwoModel != NULL) { + m_chartTwoModel->copyChartTwoCartesianAxes(copyFromTabIndex, + m_tabNumber); + } + } } /** - * @return Selected yoking group. + * @return Selected yoking group for brain models (surface or volumes) */ YokingGroupEnum::Enum -BrowserTabContent::getYokingGroup() const +BrowserTabContent::getBrainModelYokingGroup() const { - return m_yokingGroup; + return m_brainModelYokingGroup; } /** - * Set the selected yoking group. + * Set the selected yoking group for brain models (surface or volumes) * - * @param yokingGroup + * @param brainModelYokingType * New value for yoking group. */ void -BrowserTabContent::setYokingGroup(const YokingGroupEnum::Enum yokingGroup) +BrowserTabContent::setBrainModelYokingGroup(const YokingGroupEnum::Enum brainModelYokingType) { - m_yokingGroup = yokingGroup; + m_brainModelYokingGroup = brainModelYokingType; - if (m_yokingGroup == YokingGroupEnum::YOKING_GROUP_OFF) { + if (m_brainModelYokingGroup == YokingGroupEnum::YOKING_GROUP_OFF) { return; } @@ -3997,7 +4525,7 @@ BrowserTabContent::setYokingGroup(const YokingGroupEnum::Enum yokingGroup) iter++) { BrowserTabContent* btc = *iter; if (btc != this) { - if (btc->getYokingGroup() == m_yokingGroup) { + if (btc->getBrainModelYokingGroup() == m_brainModelYokingGroup) { /* * If anything is added, also need to update updateYokedBrowserTabs() */ @@ -4009,6 +4537,14 @@ BrowserTabContent::setYokingGroup(const YokingGroupEnum::Enum yokingGroup) *m_obliqueVolumeRotationMatrix = *btc->m_obliqueVolumeRotationMatrix; *m_clippingPlaneGroup = *btc->m_clippingPlaneGroup; m_identificationUpdatesVolumeSlices = btc->m_identificationUpdatesVolumeSlices; + m_displayVolumeAxesCrosshairs = btc->m_displayVolumeAxesCrosshairs; + m_displayVolumeAxesCrosshairLabels = btc->m_displayVolumeAxesCrosshairLabels; + m_displayVolumeMontageAxesCoordinates = btc->m_displayVolumeMontageAxesCoordinates; + m_volumeMontageCoordinatePrecision = btc->m_volumeMontageCoordinatePrecision; + /** + * lighting enabled NOT yoked + * m_lightingEnabled = btc->m_lightingEnabled; + */ break; } } @@ -4016,26 +4552,71 @@ BrowserTabContent::setYokingGroup(const YokingGroupEnum::Enum yokingGroup) } /** - * @return Is this browser tab yoked? + * @return Is this browser tab brain model yoked? */ bool -BrowserTabContent::isYoked() const +BrowserTabContent::isBrainModelYoked() const { - const bool yoked = (m_yokingGroup != YokingGroupEnum::YOKING_GROUP_OFF); + const bool yoked = (m_brainModelYokingGroup != YokingGroupEnum::YOKING_GROUP_OFF); return yoked; } /** - * Update other browser tabs with yoked data. + * @return Is this browser tab chart model yoked? + */ +bool +BrowserTabContent::isChartModelYoked() const +{ + const bool yoked = (m_chartModelYokingGroup != YokingGroupEnum::YOKING_GROUP_OFF); + return yoked; +} + +/** + * Update other browser tabs with brain or chart yoked data dependent upon active model */ void -BrowserTabContent::updateYokedBrowserTabs() +BrowserTabContent::updateYokedModelBrowserTabs() +{ + bool chartFlag = false; + + switch (getSelectedModelType()) { + case ModelTypeEnum::MODEL_TYPE_CHART: + chartFlag = true; + break; + case ModelTypeEnum::MODEL_TYPE_CHART_TWO: + chartFlag = true; + break; + case ModelTypeEnum::MODEL_TYPE_INVALID: + break; + case ModelTypeEnum::MODEL_TYPE_SURFACE: + break; + case ModelTypeEnum::MODEL_TYPE_SURFACE_MONTAGE: + break; + case ModelTypeEnum::MODEL_TYPE_VOLUME_SLICES: + break; + case ModelTypeEnum::MODEL_TYPE_WHOLE_BRAIN: + break; + } + + if (chartFlag) { + updateChartModelYokedBrowserTabs(); + } + else { + updateBrainModelYokedBrowserTabs(); + } +} + +/** + * Update other browser tabs with brain model yoked data. + */ +void +BrowserTabContent::updateBrainModelYokedBrowserTabs() { if (isExecutingConstructor) { return; } - if (m_yokingGroup == YokingGroupEnum::YOKING_GROUP_OFF) { + if (m_brainModelYokingGroup == YokingGroupEnum::YOKING_GROUP_OFF) { return; } @@ -4050,7 +4631,7 @@ BrowserTabContent::updateYokedBrowserTabs() /* * If anything is added, also need to update setYokingGroup() */ - if (btc->getYokingGroup() == m_yokingGroup) { + if (btc->getBrainModelYokingGroup() == m_brainModelYokingGroup) { *btc->m_viewingTransformation = *m_viewingTransformation; *btc->m_flatSurfaceViewingTransformation = *m_flatSurfaceViewingTransformation; *btc->m_cerebellumViewingTransformation = *m_cerebellumViewingTransformation; @@ -4059,6 +4640,47 @@ BrowserTabContent::updateYokedBrowserTabs() *btc->m_obliqueVolumeRotationMatrix = *m_obliqueVolumeRotationMatrix; *btc->m_clippingPlaneGroup = *m_clippingPlaneGroup; btc->m_identificationUpdatesVolumeSlices = m_identificationUpdatesVolumeSlices; + btc->m_displayVolumeAxesCrosshairs = m_displayVolumeAxesCrosshairs; + btc->m_displayVolumeAxesCrosshairLabels = m_displayVolumeAxesCrosshairLabels; + btc->m_displayVolumeMontageAxesCoordinates = m_displayVolumeMontageAxesCoordinates; + btc->m_volumeMontageCoordinatePrecision = m_volumeMontageCoordinatePrecision; + /** + * lighting enabled NOT yoked + * btc->m_lightingEnabled = m_lightingEnabled; + */ + } + } + } +} + +/** + * Update other browser tabs with brain model yoked data. + */ +void +BrowserTabContent::updateChartModelYokedBrowserTabs() +{ + if (isExecutingConstructor) { + return; + } + + if (m_chartModelYokingGroup == YokingGroupEnum::YOKING_GROUP_OFF) { + return; + } + + /* + * Copy yoked data from 'me' to all other yoked browser tabs + */ + for (std::set::iterator iter = s_allBrowserTabContent.begin(); + iter != s_allBrowserTabContent.end(); + iter++) { + BrowserTabContent* btc = *iter; + if (btc != this) { + /* + * If anything is added, also need to update setYokingGroup() + */ + if (btc->getChartModelYokingGroup() == m_chartModelYokingGroup) { + *btc->m_chartTwoMatrixViewingTranformation = *m_chartTwoMatrixViewingTranformation; + *btc->m_chartTwoMatrixDisplayProperties = *m_chartTwoMatrixDisplayProperties; } } } diff --git a/src/Brain/BrowserTabContent.h b/src/Brain/BrowserTabContent.h index f40101c37f43fb8be149f270f0aab21868776fb8..32444a98f9c0f0b470c107afb3d80cea87529df9 100644 --- a/src/Brain/BrowserTabContent.h +++ b/src/Brain/BrowserTabContent.h @@ -32,8 +32,10 @@ #include "SceneableInterface.h" #include "StructureEnum.h" #include "VolumeSliceDrawingTypeEnum.h" +#include "VolumeSliceInterpolationEdgeEffectsMaskingEnum.h" #include "VolumeSliceProjectionTypeEnum.h" #include "VolumeSliceViewPlaneEnum.h" +#include "VolumeSliceViewAllPlanesLayoutEnum.h" #include "YokingGroupEnum.h" namespace caret { @@ -42,9 +44,12 @@ namespace caret { class BrainOpenGLViewportContent; class CaretDataFile; class CaretMappableDataFile; + class ChartTwoMatrixDisplayProperties; + class ChartTwoOverlaySet; class ClippingPlaneGroup; class Matrix4x4; class ModelChart; + class ModelChartTwo; class ModelSurface; class ModelSurfaceMontage; class ModelSurfaceSelector; @@ -92,6 +97,10 @@ namespace caret { const OverlaySet* getOverlaySet() const; + ChartTwoOverlaySet* getChartTwoOverlaySet(); + + const ChartTwoOverlaySet* getChartTwoOverlaySet() const; + int32_t getTabNumber() const; ModelTypeEnum::Enum getSelectedModelType() const; @@ -102,9 +111,13 @@ namespace caret { Model* getModelForDisplay(); - ModelChart* getDisplayedChartModel(); + ModelChart* getDisplayedChartOneModel(); + + const ModelChart* getDisplayedChartOneModel() const; - const ModelChart* getDisplayedChartModel() const; + ModelChartTwo* getDisplayedChartTwoModel(); + + const ModelChartTwo* getDisplayedChartTwoModel() const; ModelSurface* getDisplayedSurfaceModel(); @@ -128,7 +141,9 @@ namespace caret { bool isCerebellumDisplayed() const; - bool isChartDisplayed() const; + bool isChartOneDisplayed() const; + + bool isChartTwoDisplayed() const; bool isFlatSurfaceDisplayed() const; @@ -140,7 +155,9 @@ namespace caret { void update(const std::vector models); - bool isChartModelValid() const; + bool isChartOneModelValid() const; + + bool isChartTwoModelValid() const; bool isSurfaceModelValid() const; @@ -270,10 +287,18 @@ namespace caret { void setTransformationsFromModelTransform(const ModelTransform& modelTransform); + ChartTwoMatrixDisplayProperties* getChartTwoMatrixDisplayProperties(); + + const ChartTwoMatrixDisplayProperties* getChartTwoMatrixDisplayProperties() const; + VolumeSliceViewPlaneEnum::Enum getSliceViewPlane() const; void setSliceViewPlane(VolumeSliceViewPlaneEnum::Enum sliceAxisMode); + VolumeSliceViewAllPlanesLayoutEnum::Enum getSlicePlanesAllViewLayout() const; + + void setSlicePlanesAllViewLayout(const VolumeSliceViewAllPlanesLayoutEnum::Enum slicePlanesAllViewLayout); + VolumeSliceDrawingTypeEnum::Enum getSliceDrawingType() const; void setSliceDrawingType(const VolumeSliceDrawingTypeEnum::Enum sliceDrawingType); @@ -282,6 +307,10 @@ namespace caret { void setSliceProjectionType(const VolumeSliceProjectionTypeEnum::Enum sliceProjectionType); + VolumeSliceInterpolationEdgeEffectsMaskingEnum::Enum getVolumeSliceInterpolationEdgeEffectsMaskingType() const; + + void setVolumeSliceInterpolationEdgeEffectsMaskingType(const VolumeSliceInterpolationEdgeEffectsMaskingEnum::Enum maskingType); + int32_t getMontageNumberOfColumns() const; void setMontageNumberOfColumns(const int32_t montageNumberOfColumns); @@ -345,15 +374,41 @@ namespace caret { void setIdentificationUpdatesVolumeSlices(const bool status); + bool isVolumeAxesCrosshairsDisplayed() const; + + void setVolumeAxesCrosshairsDisplayed(const bool displayed); + + bool isVolumeAxesCrosshairLabelsDisplayed() const; + + void setVolumeAxesCrosshairLabelsDisplayed(const bool displayed); + + bool isVolumeMontageAxesCoordinatesDisplayed() const; + + void setVolumeMontageAxesCoordinatesDisplayed(const bool displayed); + + int32_t getVolumeMontageCoordinatePrecision() const; + + void setVolumeMontageCoordinatePrecision(const int32_t volumeMontageCoordinatePrecision); + + bool isLightingEnabled() const; + + void setLightingEnabled(const bool lightingEnabled); + void reset(); - void updateYokedBrowserTabs(); + void updateChartModelYokedBrowserTabs(); + + bool isBrainModelYoked() const; - bool isYoked() const; + bool isChartModelYoked() const; - YokingGroupEnum::Enum getYokingGroup() const; + YokingGroupEnum::Enum getBrainModelYokingGroup() const; - void setYokingGroup(const YokingGroupEnum::Enum yokingType); + void setBrainModelYokingGroup(const YokingGroupEnum::Enum brainModelYokingType); + + YokingGroupEnum::Enum getChartModelYokingGroup() const; + + void setChartModelYokingGroup(const YokingGroupEnum::Enum chartModelYokingType); bool isWholeBrainLeftEnabled() const; @@ -401,15 +456,19 @@ namespace caret { BrowserTabContent& operator=(const BrowserTabContent&); - VolumeSliceViewPlaneEnum::Enum getSliceViewPlaneForVolumeAllSliceView(const int viewport[4], - const int32_t mousePressX, - const int32_t mousePressY, - int sliceViewportOut[4]) const; +// VolumeSliceViewPlaneEnum::Enum getSliceViewPlaneForVolumeAllSliceView(const int viewport[4], +// const int32_t mousePressX, +// const int32_t mousePressY, +// int sliceViewportOut[4]) const; ViewingTransformations* getViewingTransformation(); const ViewingTransformations* getViewingTransformation() const; + void updateBrainModelYokedBrowserTabs(); + + void updateYokedModelBrowserTabs(); + AString getDefaultName() const; /** Number of this tab */ @@ -436,6 +495,9 @@ namespace caret { /** The chart model */ ModelChart* m_chartModel; + /** The chart two model */ + ModelChartTwo* m_chartTwoModel; + /** * Name requested by user interface - reflects contents * such as Surface, Volume Slices, etc @@ -457,8 +519,11 @@ namespace caret { */ Matrix4x4* m_obliqueVolumeRotationMatrix; - /** Yoking group */ - YokingGroupEnum::Enum m_yokingGroup; + /** Brain Model Yoking group */ + YokingGroupEnum::Enum m_brainModelYokingGroup; + + /** Chart Model Yoking group */ + YokingGroupEnum::Enum m_chartModelYokingGroup; /** Volume Surface Outlines */ VolumeSurfaceOutlineSetModel* m_volumeSurfaceOutlineSetModel; @@ -478,6 +543,12 @@ namespace caret { /** Transformation for volume slices viewing */ ViewingTransformationsVolume* m_volumeSliceViewingTransformation; + /** Transformation for matrix chart two viewing */ + ViewingTransformations* m_chartTwoMatrixViewingTranformation; + + /** Display properties for chart two matrix */ + ChartTwoMatrixDisplayProperties* m_chartTwoMatrixDisplayProperties; + /** Volume slice settings for volume slices */ VolumeSliceSettings* m_volumeSliceSettings; @@ -496,6 +567,21 @@ namespace caret { */ bool m_identificationUpdatesVolumeSlices; + /** display crosshairs on volume slices */ + bool m_displayVolumeAxesCrosshairs; + + /** display crosshair labels on volume slices */ + bool m_displayVolumeAxesCrosshairLabels; + + /** display coordinates on montage */ + bool m_displayVolumeMontageAxesCoordinates; + + /** precision for coordinate on montage */ + int32_t m_volumeMontageCoordinatePrecision; + + /** enable lighting (shading) added 29 March 2018 */ + bool m_lightingEnabled; + /* * True if constructing an instance */ diff --git a/src/Brain/BrowserWindowContent.cxx b/src/Brain/BrowserWindowContent.cxx new file mode 100644 index 0000000000000000000000000000000000000000..6fdc799e6d124c4436cd0ed09401e61b7b8a84b7 --- /dev/null +++ b/src/Brain/BrowserWindowContent.cxx @@ -0,0 +1,481 @@ + +/*LICENSE_START*/ +/* + * Copyright (C) 2018 Washington University School of Medicine + * + * 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 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ +/*LICENSE_END*/ + +#define __BROWSER_WINDOW_CONTENT_DECLARE__ +#include "BrowserWindowContent.h" +#undef __BROWSER_WINDOW_CONTENT_DECLARE__ + +#include "CaretAssert.h" +#include "SceneClass.h" +#include "SceneClassAssistant.h" +#include "SceneIntegerArray.h" +#include "TileTabsConfiguration.h" + +using namespace caret; + + + +/** + * \class caret::BrowserWindowContent + * \brief Information about content of a browser window. + * \ingroup Brain + */ + +/** + * Constructor. + */ +BrowserWindowContent::BrowserWindowContent(const int32_t windowIndex) +: CaretObject(), +m_windowIndex(windowIndex) +{ + m_validFlag = false; + reset(); + + m_sceneAssistant = std::unique_ptr(new SceneClassAssistant()); + m_sceneAssistant->add("m_validFlag", &m_validFlag); + m_sceneAssistant->add("m_windowAspectRatioLocked", &m_windowAspectRatioLocked); + m_sceneAssistant->add("m_windowAspectLockedRatio", &m_windowAspectLockedRatio); + m_sceneAssistant->add("m_allTabsInWindowAspectRatioLocked", &m_allTabsInWindowAspectRatioLocked); + m_sceneAssistant->add("m_tileTabsEnabled", &m_tileTabsEnabled); + m_sceneAssistant->add("m_sceneGraphicsWidth", &m_sceneGraphicsWidth); + m_sceneAssistant->add("m_sceneGraphicsHeight", &m_sceneGraphicsHeight); + m_sceneAssistant->add("m_sceneSelectedTabIndex", &m_sceneSelectedTabIndex); +} + +/** + * Destructor. + */ +BrowserWindowContent::~BrowserWindowContent() +{ +} + +/** + * @return True if this browser window content is valid. + */ +bool +BrowserWindowContent::isValid() const +{ + return m_validFlag; +} + +/** + * Set validity for this browser window content. + * + * @param valid + * New validity status. + */ +void +BrowserWindowContent::setValid(const bool valid) +{ + m_validFlag = valid; +} + + +/** + * Reset the number of elements in this instance. + */ +void +BrowserWindowContent::reset() +{ + /* Note: do not change m_validFlag */ + + m_windowAspectRatioLocked = false; + m_windowAspectLockedRatio = 1.0f; + m_allTabsInWindowAspectRatioLocked = false; + m_tileTabsEnabled = false; + m_sceneGraphicsHeight = 0; + m_sceneGraphicsWidth = 0; + m_sceneTileTabsConfiguration.reset(new TileTabsConfiguration()); + m_sceneTileTabsConfiguration->setName(s_sceneTileTabsConfigurationText); + m_sceneSelectedTabIndex = 0; + m_sceneTabIndices.clear(); +} + + +/** + * Get a description of this object's content. + * @return String describing this object's content. + */ +int32_t +BrowserWindowContent::getWindowIndex() const +{ + return m_windowIndex; +} + +/** + * @return Window aspect locked status + */ +bool +BrowserWindowContent::isWindowAspectLocked() const +{ + return m_windowAspectRatioLocked; +} + +/** + * Set the window's aspect ratio locked status + * + * @param lockedStatus + * The new locked status. + */ +void +BrowserWindowContent::setWindowAspectLocked(const bool lockedStatus) +{ + m_windowAspectRatioLocked = lockedStatus; +} + +/** + * @return The window's locked aspect ratio + */ +float +BrowserWindowContent::getWindowAspectLockedRatio() const +{ + return m_windowAspectLockedRatio; +} + +/** + * Set the window's locked aspect ratio + * + * @param aspectRatio + * The aspect ratio. + */ +void +BrowserWindowContent::setWindowAspectLockedRatio(const float aspectRatio) +{ + m_windowAspectLockedRatio = aspectRatio; +} + +/** + * @return Is the aspect ratio locked for ALL TABS in the window + */ +bool +BrowserWindowContent::isAllTabsInWindowAspectRatioLocked() const +{ + return m_allTabsInWindowAspectRatioLocked; +} + +/** + * Set the aspect ratio locked for ALL TABS in the window + * + * @param lockedStatus + * The new locked status. + */ +void +BrowserWindowContent::setAllTabsInWindowAspectRatioLocked(const bool lockedStatus) +{ + m_allTabsInWindowAspectRatioLocked = lockedStatus; +} + +/** + * @return Is tile tabs enabled ? + */ +bool +BrowserWindowContent::isTileTabsEnabled() const +{ + return m_tileTabsEnabled; +} + +/** + * Set tile tabs enabled + * + * @param tileTabsEnabled + * Enabled status for tile tabs. + */ +void +BrowserWindowContent::setTileTabsEnabled(const bool tileTabsEnabled) +{ + m_tileTabsEnabled = tileTabsEnabled; +} + +/** + * @return Width of the graphics region from scene. + */ +int32_t +BrowserWindowContent::getSceneGraphicsWidth() const +{ + return m_sceneGraphicsWidth; +} + +/** + * Set the width of the graphics region for scene. + * + * @param width + * New value for width. + */ +void +BrowserWindowContent::setSceneGraphicsWidth(const int32_t width) +{ + m_sceneGraphicsWidth = width; +} + +/** + * @return Height of the graphics region from scene. + */ +int32_t +BrowserWindowContent::getSceneGraphicsHeight() const +{ + return m_sceneGraphicsHeight; +} + +/** + * Set the height of the graphics region for scene. + * + * @param height + * New value for height. + */ +void +BrowserWindowContent::setSceneGraphicsHeight(const int32_t height) +{ + m_sceneGraphicsHeight = height; +} + +/** + * @return The tile tabs configuration from the scene. + */ +TileTabsConfiguration* +BrowserWindowContent::getSceneTileTabsConfiguration() const +{ + return m_sceneTileTabsConfiguration.get(); +} + +/** + * @return Index of the selected tab in the scene. + */ +int32_t +BrowserWindowContent::getSceneSelectedTabIndex() const +{ + return m_sceneSelectedTabIndex; +} + +/** + * Set index of the selected tab in the scene. + * + * @param selectedTabIndex + * Index of selected tab. + */ +void +BrowserWindowContent::setSceneSelectedTabIndex(const int32_t selectedTabIndex) +{ + m_sceneSelectedTabIndex = selectedTabIndex; +} + +/** + * @return Indices of the tabs in the scene. + * In single tab view, this returns one index that is the same as getSceneSelectedTabIndex(). + * In tile tabs view, this contains all tabs in the window. + */ +std::vector +BrowserWindowContent::getSceneTabIndices() const +{ + return m_sceneTabIndices; +} + +/** + * Set indices of the tabs in the scene. + * In single tab view, this contains one index that the displayed tab. + * In tile tabs view, this contains all tabs in the window. + * + * @param sceneTabIndices + * Indices of the tabs. + */ +void +BrowserWindowContent::setSceneWindowTabIndices(const std::vector& sceneTabIndices) +{ + m_sceneTabIndices = sceneTabIndices; +} + +/** + * Copy the tile tabs configuration for writing to the scene. + * + * @param tileTabsConfiguration + * Tile tabs configuration for scene. + */ +void +BrowserWindowContent::copyTileTabsConfigurationForSavingScene(const TileTabsConfiguration* tileTabsConfiguration) +{ + m_tileTabsConfigurationForSavingScene.reset(new TileTabsConfiguration(*tileTabsConfiguration)); +} + +/** + * Save information specific to this type of model to the scene. + * + * @param sceneAttributes + * Attributes for the scene. Scenes may be of different types + * (full, generic, etc) and the attributes should be checked when + * saving the scene. + * + * @param instanceName + * Name of instance in the scene. + */ +SceneClass* +BrowserWindowContent::saveToScene(const SceneAttributes* sceneAttributes, + const AString& instanceName) +{ + SceneClass* sceneClass = new SceneClass(instanceName, + "BrowserWindowContent", + 1); + m_sceneAssistant->saveMembers(sceneAttributes, + sceneClass); + + if (m_tileTabsEnabled) { + /* + * The browser window will provide selected tile tabs configuration + * by calling this class' copyTileTabsConfigurationForSavingScene() method. + * It is saved as the scene's tile tabs configuration. + * The content of "m_sceneTileTabsConfiguration" is NOT saved to the scene + * but it will be updated when the scene is read. + */ + CaretAssert(m_tileTabsConfigurationForSavingScene.get()); + const AString xmlString = m_tileTabsConfigurationForSavingScene->encodeInXML(); + sceneClass->addString("m_sceneTileTabsConfiguration", + xmlString); + + /* + * Clear tile tabs config now that it has been written and is no longer needed + */ + m_tileTabsConfigurationForSavingScene.reset(); + } + //sceneClass->addString("m_sceneTileTabsConfiguration", m_sceneTileTabsConfiguration->encodeInXML()); + sceneClass->addChild(new SceneIntegerArray("m_sceneTabIndices", + m_sceneTabIndices)); + + // Uncomment if sub-classes must save to scene + //saveSubClassDataToScene(sceneAttributes, + // sceneClass); + + return sceneClass; +} + +/** + * Restore information specific to the type of model from the scene. + * + * @param sceneAttributes + * Attributes for the scene. Scenes may be of different types + * (full, generic, etc) and the attributes should be checked when + * restoring the scene. + * + * @param sceneClass + * sceneClass from which model specific information is obtained. + */ +void +BrowserWindowContent::restoreFromScene(const SceneAttributes* sceneAttributes, + const SceneClass* sceneClass) +{ + reset(); + + if (sceneClass == NULL) { + return; + } + + m_sceneAssistant->restoreMembers(sceneAttributes, + sceneClass); + + const ScenePrimitiveArray* sceneTabIndicesArray = sceneClass->getPrimitiveArray("m_sceneTabIndices"); + if (sceneTabIndicesArray != NULL) { + sceneTabIndicesArray->integerVectorValues(m_sceneTabIndices); + } + + const AString stringTileTabsConfig = sceneClass->getStringValue("m_sceneTileTabsConfiguration"); + if ( ! stringTileTabsConfig.isEmpty()) { + if (m_sceneTileTabsConfiguration->decodeFromXML(stringTileTabsConfig)) { + m_sceneTileTabsConfiguration->setName(s_sceneTileTabsConfigurationText + + " " + + sceneAttributes->getSceneName()); + } + else { + sceneAttributes->addToErrorMessage("Failed to decode tile tabs configuration from BrowserWindowContent: \"" + + stringTileTabsConfig + + "\""); + } + } + + + //Uncomment if sub-classes must restore from scene + //restoreSubClassDataFromScene(sceneAttributes, + // sceneClass); + +} + +/** + * Restore from BrainBrowserWindow scene from before this class was created. + + * @param sceneAttributes + * Attributes for the scene. Scenes may be of different types + * (full, generic, etc) and the attributes should be checked when + * restoring the scene. + * + * @param browserClass + * A BrainBrowserWindow scene. + */ +void +BrowserWindowContent::restoreFromOldBrainBrowserWindowScene(const SceneAttributes* sceneAttributes, + const SceneClass* browserClass) +{ + CaretAssert(browserClass); + + reset(); + + if (browserClass->getName() != "m_brainBrowserWindows") { + sceneAttributes->addToErrorMessage("When restoring BrowserWindowContent::restoreFromOldBrainBrowserWindowScene " + "name of scene class is not m_brainBrowserWindows"); + return; + } + + m_windowAspectRatioLocked = browserClass->getBooleanValue("m_windowAspectRatioLockedAction", + false); + + m_windowAspectLockedRatio = browserClass->getFloatValue("m_aspectRatio", 1.0f); + + /* not in old scene */ + m_allTabsInWindowAspectRatioLocked = false; + + m_tileTabsEnabled = browserClass->getBooleanValue("m_viewTileTabsAction", + false); + + + const SceneClass* graphicsGeometry = browserClass->getClass("openGLWidgetGeometry"); + if (graphicsGeometry != NULL) { + m_sceneGraphicsWidth = graphicsGeometry->getIntegerValue("geometryWidth", -1); + m_sceneGraphicsHeight = graphicsGeometry->getIntegerValue("geometryHeight", -1); + } + + const AString tileTabsConfigString = browserClass->getStringValue("m_sceneTileTabsConfiguration"); + if ( ! tileTabsConfigString.isEmpty()) { + m_sceneTileTabsConfiguration->decodeFromXML(tileTabsConfigString); + m_sceneTileTabsConfiguration->setName(s_sceneTileTabsConfigurationText + + " " + + sceneAttributes->getSceneName()); + } + + const SceneClass* toolbarClass = browserClass->getClass("m_toolbar"); + if (toolbarClass != NULL) { + m_sceneSelectedTabIndex = toolbarClass->getIntegerValue("selectedTabIndex", -1); + + const ScenePrimitiveArray* tabIndexArray = toolbarClass->getPrimitiveArray("tabIndices"); + if (tabIndexArray != NULL) { + const int32_t numTabs = tabIndexArray->getNumberOfArrayElements(); + for (auto iTab = 0; iTab < numTabs; iTab++) { + m_sceneTabIndices.push_back(tabIndexArray->integerValue(iTab)); + } + } + } +} + + diff --git a/src/Brain/BrowserWindowContent.h b/src/Brain/BrowserWindowContent.h new file mode 100644 index 0000000000000000000000000000000000000000..e0224825047f765c201098d40d2065932ef815c4 --- /dev/null +++ b/src/Brain/BrowserWindowContent.h @@ -0,0 +1,157 @@ +#ifndef __BROWSER_WINDOW_CONTENT_H__ +#define __BROWSER_WINDOW_CONTENT_H__ + +/*LICENSE_START*/ +/* + * Copyright (C) 2018 Washington University School of Medicine + * + * 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 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ +/*LICENSE_END*/ + + + +#include + +#include "CaretObject.h" + +#include "SceneableInterface.h" + +namespace caret { + class SceneClassAssistant; + class TileTabsConfiguration; + + class BrowserWindowContent : public CaretObject, public SceneableInterface { + + public: + BrowserWindowContent(const int32_t windowIndex); + + virtual ~BrowserWindowContent(); + + int32_t getWindowIndex() const; + + bool isValid() const; + + void setValid(const bool valid); + + void reset(); + + bool isWindowAspectLocked() const; + + void setWindowAspectLocked(const bool lockedStatus); + + float getWindowAspectLockedRatio() const; + + void setWindowAspectLockedRatio(const float aspectRatio); + + bool isAllTabsInWindowAspectRatioLocked() const; + + void setAllTabsInWindowAspectRatioLocked(const bool lockedStatus); + + bool isTileTabsEnabled() const; + + void setTileTabsEnabled(const bool tileTabsEnabled); + + int32_t getSceneGraphicsWidth() const; + + void setSceneGraphicsWidth(const int32_t width); + + int32_t getSceneGraphicsHeight() const; + + void setSceneGraphicsHeight(const int32_t width); + + TileTabsConfiguration* getSceneTileTabsConfiguration() const; + + void copyTileTabsConfigurationForSavingScene(const TileTabsConfiguration* tileTabsConfiguration); + + int32_t getSceneSelectedTabIndex() const; + + void setSceneSelectedTabIndex(const int32_t selectedTabIndex); + + std::vector getSceneTabIndices() const; + + void setSceneWindowTabIndices(const std::vector& sceneTabIndices); + + // ADD_NEW_METHODS_HERE + + virtual SceneClass* saveToScene(const SceneAttributes* sceneAttributes, + const AString& instanceName); + + virtual void restoreFromScene(const SceneAttributes* sceneAttributes, + const SceneClass* sceneClass); + + void restoreFromOldBrainBrowserWindowScene(const SceneAttributes* sceneAttributes, + const SceneClass* browserClass); + + + + + + +// If there will be sub-classes of this class that need to save +// and restore data from scenes, these pure virtual methods can +// be uncommented to force their implementation by sub-classes. +// protected: +// virtual void saveSubClassDataToScene(const SceneAttributes* sceneAttributes, +// SceneClass* sceneClass) = 0; +// +// virtual void restoreSubClassDataFromScene(const SceneAttributes* sceneAttributes, +// const SceneClass* sceneClass) = 0; + + private: + BrowserWindowContent(const BrowserWindowContent&); + + BrowserWindowContent& operator=(const BrowserWindowContent&); + + std::unique_ptr m_sceneAssistant; + + bool m_validFlag; + + const int32_t m_windowIndex; + + bool m_windowAspectRatioLocked = false; + + float m_windowAspectLockedRatio = 1.0f; + + bool m_allTabsInWindowAspectRatioLocked = false; + + bool m_tileTabsEnabled = false; + + int32_t m_sceneGraphicsWidth = 0; + + int32_t m_sceneGraphicsHeight = 0; + + std::unique_ptr m_sceneTileTabsConfiguration; + + std::unique_ptr m_tileTabsConfigurationForSavingScene; + + int32_t m_sceneSelectedTabIndex = 0; + + std::vector m_sceneTabIndices; + + friend class BrainBrowserWindow; + + static const AString s_sceneTileTabsConfigurationText; + + // ADD_NEW_MEMBERS_HERE + + }; + +#ifdef __BROWSER_WINDOW_CONTENT_DECLARE__ + const AString BrowserWindowContent::s_sceneTileTabsConfigurationText = "From Scene: "; +#endif // __BROWSER_WINDOW_CONTENT_DECLARE__ + +} // namespace +#endif //__BROWSER_WINDOW_CONTENT_H__ diff --git a/src/Brain/CMakeLists.txt b/src/Brain/CMakeLists.txt index c0e1d3a4c01804e60d55b6d281e95525d6987856..0ef06d98fe3e95ce287289ad2fc39c9b5f13f24f 100644 --- a/src/Brain/CMakeLists.txt +++ b/src/Brain/CMakeLists.txt @@ -8,45 +8,18 @@ PROJECT (Brain) # SET(QT_DONT_USE_QTGUI) -# -# Use OpenGL from QT -# -#SET(QT_USE_QTOPENGL TRUE) - -# -# Need network -# -SET(QT_USE_QTNETWORK TRUE) - # # Add QT for includes # -INCLUDE (${QT_USE_FILE}) - -# -# Need OpenGL -# -FIND_PACKAGE(OpenGL REQUIRED) -IF (OPENGL_FOUND) - # - # Need help finding includes on Apple - # - IF (APPLE) - # When searching for the include directory, find the location - # for the OpenGL framework rather than an individual header file. - FIND_PATH(OPENGL_INCLUDE_DIR OpenGL.framework - /System/Library/Frameworks - /Library/Frameworks - ~/Library/Frameworks - ) - ENDIF (APPLE) - # - # OpenGL Include Directory - # - INCLUDE_DIRECTORIES(${OPENGL_INCLUDE_DIR}) -ELSE (OPENGL_FOUND) - MESSAGE(FATAL_ERROR "OpenGL Libraries were not found") -ENDIF (OPENGL_FOUND) +if(Qt5_FOUND) + include_directories(${Qt5Core_INCLUDE_DIRS}) + include_directories(${Qt5Network_INCLUDE_DIRS}) + include_directories(${Qt5Gui_INCLUDE_DIRS}) +endif() +IF (QT4_FOUND) + SET(QT_USE_QTNETWORK TRUE) + INCLUDE(${QT_USE_FILE}) +ENDIF () # # Create the brain library @@ -61,6 +34,8 @@ BrainOpenGL.h BrainOpenGLAnnotationDrawingFixedPipeline.h BrainOpenGLChartDrawingInterface.h BrainOpenGLChartDrawingFixedPipeline.h +BrainOpenGLChartTwoDrawingFixedPipeline.h +BrainOpenGLChartTwoDrawingInterface.h BrainOpenGLFixedPipeline.h BrainOpenGLPrimitiveDrawing.h BrainOpenGLShape.h @@ -68,19 +43,19 @@ BrainOpenGLShapeCone.h BrainOpenGLShapeCube.h BrainOpenGLShapeCylinder.h BrainOpenGLShapeRing.h -BrainOpenGLShapeRingOutline.h BrainOpenGLShapeSphere.h BrainOpenGLTextRenderInterface.h -BrainOpenGLTextureManager.h BrainOpenGLViewportContent.h BrainOpenGLVolumeObliqueSliceDrawing.h BrainOpenGLVolumeSliceDrawing.h +BrainOpenGLWindowContent.h BrainStructure.h BrainStructureNodeAttributes.h BrowserTabContent.h -CaretDataFileSelectionModel.h -CaretMappableDataFileAndMapSelectionModel.h -CaretOpenGLInclude.h +BrowserWindowContent.h +ChartTwoOverlay.h +ChartTwoOverlaySet.h +ChartTwoOverlaySetArray.h ChartingDataManager.h CiftiConnectivityMatrixDataFileManager.h CiftiFiberTrajectoryManager.h @@ -101,12 +76,12 @@ DummyFontTextRenderer.h EventAnnotationColorBarGet.h EventBrainReset.h EventBrainStructureGetAll.h -EventBrowserTabDelete.h EventBrowserTabGet.h EventBrowserTabGetAll.h EventBrowserTabGetAllViewed.h EventBrowserTabNew.h -EventCaretMappableDataFileMapsViewedInOverlays.h +EventBrowserWindowContent.h +EventChartOverlayValidate.h EventDataFileAdd.h EventDataFileDelete.h EventDataFileRead.h @@ -116,6 +91,7 @@ EventIdentificationHighlightLocation.h EventModelAdd.h EventModelDelete.h EventModelGetAll.h +EventModelGetAllDisplayed.h EventModelSurfaceGet.h EventNodeDataFilesGet.h EventNodeIdentificationColorsGetFromCharts.h @@ -139,6 +115,7 @@ IdentifiedItemVoxel.h ImageDepthPositionEnum.h Model.h ModelChart.h +ModelChartTwo.h ModelSurface.h ModelSurfaceMontage.h ModelSurfaceSelector.h @@ -157,6 +134,10 @@ SelectionItemChartDataSeries.h SelectionItemChartFrequencySeries.h SelectionItemChartMatrix.h SelectionItemChartTimeSeries.h +SelectionItemChartTwoHistogram.h +SelectionItemChartTwoLabel.h +SelectionItemChartTwoLineSeries.h +SelectionItemChartTwoMatrix.h SelectionItemCiftiConnectivityMatrixRowColumn.h SelectionItemFocusSurface.h SelectionItemFocusVolume.h @@ -185,7 +166,9 @@ ViewingTransformations.h ViewingTransformationsCerebellum.h ViewingTransformationsVolume.h VolumeSliceDrawingTypeEnum.h +VolumeSliceInterpolationEdgeEffectsMaskingEnum.h VolumeSliceSettings.h +VolumeSliceViewAllPlanesLayoutEnum.h VolumeSurfaceOutlineColorOrTabModel.h VolumeSurfaceOutlineModel.h VolumeSurfaceOutlineSetModel.h @@ -200,6 +183,7 @@ Brain.cxx BrainOpenGL.cxx BrainOpenGLAnnotationDrawingFixedPipeline.cxx BrainOpenGLChartDrawingFixedPipeline.cxx +BrainOpenGLChartTwoDrawingFixedPipeline.cxx BrainOpenGLFixedPipeline.cxx BrainOpenGLPrimitiveDrawing.cxx BrainOpenGLShape.cxx @@ -207,18 +191,19 @@ BrainOpenGLShapeCone.cxx BrainOpenGLShapeCube.cxx BrainOpenGLShapeCylinder.cxx BrainOpenGLShapeRing.cxx -BrainOpenGLShapeRingOutline.cxx BrainOpenGLShapeSphere.cxx BrainOpenGLTextRenderInterface.cxx -BrainOpenGLTextureManager.cxx BrainOpenGLViewportContent.cxx BrainOpenGLVolumeObliqueSliceDrawing.cxx BrainOpenGLVolumeSliceDrawing.cxx +BrainOpenGLWindowContent.cxx BrainStructure.cxx BrainStructureNodeAttributes.cxx BrowserTabContent.cxx -CaretDataFileSelectionModel.cxx -CaretMappableDataFileAndMapSelectionModel.cxx +BrowserWindowContent.cxx +ChartTwoOverlay.cxx +ChartTwoOverlaySet.cxx +ChartTwoOverlaySetArray.cxx ChartingDataManager.cxx CiftiConnectivityMatrixDataFileManager.cxx CiftiFiberTrajectoryManager.cxx @@ -238,12 +223,12 @@ DummyFontTextRenderer.cxx EventAnnotationColorBarGet.cxx EventBrainReset.cxx EventBrainStructureGetAll.cxx -EventBrowserTabDelete.cxx EventBrowserTabGet.cxx EventBrowserTabGetAll.cxx EventBrowserTabGetAllViewed.cxx EventBrowserTabNew.cxx -EventCaretMappableDataFileMapsViewedInOverlays.cxx +EventBrowserWindowContent.cxx +EventChartOverlayValidate.cxx EventDataFileAdd.cxx EventDataFileDelete.cxx EventDataFileRead.cxx @@ -253,6 +238,7 @@ EventIdentificationHighlightLocation.cxx EventModelAdd.cxx EventModelDelete.cxx EventModelGetAll.cxx +EventModelGetAllDisplayed.cxx EventModelSurfaceGet.cxx EventNodeDataFilesGet.cxx EventNodeIdentificationColorsGetFromCharts.cxx @@ -275,6 +261,7 @@ IdentifiedItemVoxel.cxx ImageDepthPositionEnum.cxx Model.cxx ModelChart.cxx +ModelChartTwo.cxx ModelSurface.cxx ModelSurfaceMontage.cxx ModelSurfaceSelector.cxx @@ -293,6 +280,10 @@ SelectionItemChartDataSeries.cxx SelectionItemChartFrequencySeries.cxx SelectionItemChartMatrix.cxx SelectionItemChartTimeSeries.cxx +SelectionItemChartTwoHistogram.cxx +SelectionItemChartTwoLabel.cxx +SelectionItemChartTwoLineSeries.cxx +SelectionItemChartTwoMatrix.cxx SelectionItemCiftiConnectivityMatrixRowColumn.cxx SelectionItemFocusSurface.cxx SelectionItemFocusVolume.cxx @@ -321,7 +312,9 @@ ViewingTransformations.cxx ViewingTransformationsCerebellum.cxx ViewingTransformationsVolume.cxx VolumeSliceDrawingTypeEnum.cxx +VolumeSliceInterpolationEdgeEffectsMaskingEnum.cxx VolumeSliceSettings.cxx +VolumeSliceViewAllPlanesLayoutEnum.cxx VolumeSurfaceOutlineColorOrTabModel.cxx VolumeSurfaceOutlineModel.cxx VolumeSurfaceOutlineSetModel.cxx @@ -329,6 +322,8 @@ WholeBrainSurfaceSettings.cxx WholeBrainVoxelDrawingMode.cxx ) +TARGET_LINK_LIBRARIES(Brain ${CARET_QT5_LINK}) + # # Find Headers # @@ -339,6 +334,7 @@ ${CMAKE_SOURCE_DIR}/Charting ${CMAKE_SOURCE_DIR}/Cifti ${CMAKE_SOURCE_DIR}/FilesBase ${CMAKE_SOURCE_DIR}/Files +${CMAKE_SOURCE_DIR}/Graphics ${CMAKE_SOURCE_DIR}/Palette ${CMAKE_SOURCE_DIR}/Gifti ${CMAKE_SOURCE_DIR}/Nifti diff --git a/src/Brain/ChartTwoOverlay.cxx b/src/Brain/ChartTwoOverlay.cxx new file mode 100644 index 0000000000000000000000000000000000000000..f10b7977cfb0000a0b647ab2abef353cfe6d7042 --- /dev/null +++ b/src/Brain/ChartTwoOverlay.cxx @@ -0,0 +1,1507 @@ + +/*LICENSE_START*/ +/* + * Copyright (C) 2016 Washington University School of Medicine + * + * 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 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ +/*LICENSE_END*/ + +#define __CHART_TWO_OVERLAY_DECLARE__ +#include "ChartTwoOverlay.h" +#undef __CHART_TWO_OVERLAY_DECLARE__ + +#include "AnnotationColorBar.h" +#include "BoundingBox.h" +#include "CaretAssert.h" +#include "CaretLogger.h" +#include "CaretMappableDataFile.h" +#include "ChartableTwoFileDelegate.h" +#include "ChartableTwoFileHistogramChart.h" +#include "ChartableTwoFileLineSeriesChart.h" +#include "ChartableTwoFileMatrixChart.h" +#include "ChartTwoLineSeriesHistory.h" +#include "ChartTwoOverlaySet.h" +#include "EventCaretMappableDataFilesGet.h" +#include "EventChartOverlayValidate.h" +#include "EventManager.h" +#include "Histogram.h" +#include "PlainTextStringBuilder.h" +#include "SceneClass.h" +#include "SceneClassAssistant.h" + +using namespace caret; + + + +/** + * \class caret::ChartTwoOverlay + * \brief Overlay for charts. + * \ingroup Brain + */ + +/** + * Constructor. + * + * @param parentChartTwoOverlaySet + * Parent of this chart overlay set. + * @param chartDataType + * Type of charts allowed in this overlay + * @param overlayIndex + * Index of this overlay. + */ +ChartTwoOverlay::ChartTwoOverlay(ChartTwoOverlaySet* parentChartTwoOverlaySet, + const ChartTwoDataTypeEnum::Enum chartDataType, + const int32_t tabIndex, + const int32_t overlayIndex) +: CaretObject(), +m_parentChartTwoOverlaySet(parentChartTwoOverlaySet), +m_chartDataType(chartDataType), +m_tabIndex(tabIndex), +m_overlayIndex(overlayIndex) +{ + CaretAssert(m_parentChartTwoOverlaySet); + + m_name = "Overlay " + AString::number(overlayIndex + 1); + m_enabled = (m_overlayIndex == 0); + m_mapYokingGroup = MapYokingGroupEnum::MAP_YOKING_GROUP_OFF; + + m_colorBar = std::unique_ptr(new AnnotationColorBar(AnnotationAttributesDefaultTypeEnum::NORMAL)); + m_colorBar->setCoordinateSpace(AnnotationCoordinateSpaceEnum::TAB); + + m_matrixTriangularViewingMode = ChartTwoMatrixTriangularViewingModeEnum::MATRIX_VIEW_FULL; + m_cartesianVerticalAxisLocation = ChartAxisLocationEnum::CHART_AXIS_LOCATION_LEFT; + + m_selectedMapFile = NULL; + m_selectedHistogramMapIndex = -1; + m_allHistogramMapsSelectedFlag = false; + + m_sceneAssistant = std::unique_ptr(new SceneClassAssistant()); + m_sceneAssistant->add("m_enabled", &m_enabled); + m_sceneAssistant->add("m_mapYokingGroup", + &m_mapYokingGroup); + m_sceneAssistant->add("m_colorBar", "AnnotationColorBar", m_colorBar.get()); + m_sceneAssistant->add("m_matrixTriangularViewingMode", + &m_matrixTriangularViewingMode); + m_sceneAssistant->add("m_cartesianVerticalAxisLocation", + &m_cartesianVerticalAxisLocation); + m_sceneAssistant->add("m_selectedHistogramMapIndex", &m_selectedHistogramMapIndex); + m_sceneAssistant->add("m_allHistogramMapsSelectedFlag", &m_allHistogramMapsSelectedFlag); + + EventManager::get()->addEventListener(this, + EventTypeEnum::EVENT_CHART_OVERLAY_VALIDATE); +} + +/** + * Destructor. + */ +ChartTwoOverlay::~ChartTwoOverlay() +{ + EventManager::get()->removeAllEventsFromListener(this); +} + +/** + * @return Weak pointer to instance. + * This weak pointer stored by classes (such as those in the GUI) + * safely test if an instance is still valid and can be safely accessed. + */ +std::weak_ptr +ChartTwoOverlay::getWeakPointerToSelf() +{ + return m_weakPointerToSelf; +} + +/** + * Set the weak pointer for this instance. + * This is called by the parent ChartTwoOverlaySet. + * + * @param weakPointerToSelf + * Weak pointer to this instance. + */ +void +ChartTwoOverlay::setWeakPointerToSelf(std::weak_ptr weakPointerToSelf) +{ + m_weakPointerToSelf = weakPointerToSelf; + CaretAssert( ! m_weakPointerToSelf.expired()); + CaretAssert(m_weakPointerToSelf.lock().get() == this); +} + +/** + * Receive an event. + * + * @param event + * An event for which this instance is listening. + */ +void +ChartTwoOverlay::receiveEvent(Event* event) +{ + if (event->getEventType() == EventTypeEnum::EVENT_CHART_OVERLAY_VALIDATE) { + EventChartOverlayValidate* eov = dynamic_cast(event); + CaretAssert(eov); + eov->testValidChartOverlay(this); + eov->setEventProcessed(); + } +} + +/** + * Get a description of this object's content. + * @return String describing this object's content. + */ +AString +ChartTwoOverlay::toString() const +{ + PlainTextStringBuilder tb; + getDescriptionOfContent(tb); + return tb.getText(); +} + +/** + * Get a text description of the window's content. + * + * @param descriptionOut + * Description of the window's content. + */ +void +ChartTwoOverlay::getDescriptionOfContent(PlainTextStringBuilder& descriptionOut) const +{ + ChartTwoOverlay* me = const_cast(this); + if (me != NULL) { + if (me->isEnabled()) { + CaretMappableDataFile* mapFile = NULL; + SelectedIndexType indexType = SelectedIndexType::INVALID; + int32_t index = 0; + me->getSelectionData(mapFile, + indexType, + index); + if (mapFile != NULL) { + descriptionOut.addLine("Overlay Index: " + AString::number(m_overlayIndex)); + descriptionOut.addLine("File: "+ + mapFile->getFileNameNoPath()); + if (mapFile->hasMapAttributes()) { + switch (indexType) { + case SelectedIndexType::INVALID: + break; + case SelectedIndexType::COLUMN: + descriptionOut.addLine("Column " + + AString::number(index)); + break; + case SelectedIndexType::MAP: + if ((index >= 0) + && (index < mapFile->getNumberOfMaps())) { + descriptionOut.addLine("Map Index: " + + AString::number(index + 1)); + descriptionOut.addLine("Map Name: " + + mapFile->getMapName(index)); + } + break; + case SelectedIndexType::ROW: + descriptionOut.addLine("Row " + + AString::number(index)); + break; + } + } + + const ChartableTwoFileDelegate* chartDelegate = mapFile->getChartingDelegate(); + ChartTwoCompoundDataType cdt; + chartDelegate->getChartTwoCompoundDataTypeForChartTwoDataType(getChartTwoDataType(), + cdt); + descriptionOut.addLine(cdt.toString()); + } + } + } +} + +/** + * @return The chart data type for this chart overlay. + */ +ChartTwoDataTypeEnum::Enum +ChartTwoOverlay::getChartTwoDataType() const +{ + return m_chartDataType; +} + +/** + * Get the chart compound data type + */ +ChartTwoCompoundDataType +ChartTwoOverlay::getChartTwoCompoundDataType() const +{ + return m_chartCompoundDataType; +} + +/** + * Set the compound chart type for charts displayed in this overlay. + * MUST match simplae data type for this chart unless invalid. + * Note that overlay index zero, allows any chart type. + * + * @param chartCompoundDataType + * Type of charts for display in this overlay. + */ +void +ChartTwoOverlay::setChartTwoCompoundDataType(const ChartTwoCompoundDataType& chartCompoundDataType) +{ + if (m_overlayIndex == 0) { + CaretAssertMessage(0, "ChartTwoOverlay::setChartTwoCompoundDataType() should not be called " + " for first overlay"); + return; + } +// do for overlay zero ?? + + if (chartCompoundDataType.getChartTwoDataType() != ChartTwoDataTypeEnum::CHART_DATA_TYPE_INVALID) { + CaretAssert(m_chartDataType == chartCompoundDataType.getChartTwoDataType()); + } + m_chartCompoundDataType = chartCompoundDataType; +} + +/** + * @return The tab index. + */ +int32_t +ChartTwoOverlay::getTabIndex() const +{ + return m_tabIndex; +} + +/** + * @return Enabled status for this surface overlay. + */ +bool +ChartTwoOverlay::isEnabled() const +{ + return m_enabled; +} + +/** + * Set the enabled status for this surface overlay. + * @param enabled + * New status. + */ +void +ChartTwoOverlay::setEnabled(const bool enabled) +{ + m_enabled = enabled; +} + +/** + * @return line-series loading enabled + */ +bool +ChartTwoOverlay::isLineSeriesLoadingEnabled() const +{ + CaretMappableDataFile* mapFile = NULL; + SelectedIndexType indexType = SelectedIndexType::INVALID; + int32_t mapIndex = -1; + getSelectionData(mapFile, + indexType, + mapIndex); + + const ChartableTwoFileLineSeriesChart* lineSeriesChart = mapFile->getChartingDelegate()->getLineSeriesCharting(); + const ChartTwoLineSeriesHistory* lineSeriesHistory = lineSeriesChart->getHistory(); + return lineSeriesHistory->isLoadingEnabled(); +} + +/** + * Set line-series loading enabled + * + * @param lineSeriesLoadingEnabled + * New value for line-series loading enabled + */ +void +ChartTwoOverlay::setLineSeriesLoadingEnabled(const bool lineSeriesLoadingEnabled) +{ + CaretMappableDataFile* mapFile = NULL; + SelectedIndexType indexType = SelectedIndexType::INVALID; + int32_t mapIndex = -1; + getSelectionData(mapFile, + indexType, + mapIndex); + + ChartableTwoFileLineSeriesChart* lineSeriesChart = mapFile->getChartingDelegate()->getLineSeriesCharting(); + ChartTwoLineSeriesHistory* lineSeriesHistory = lineSeriesChart->getHistory(); + lineSeriesHistory->setLoadingEnabled(lineSeriesLoadingEnabled); +} + +/** + * Copy the data from the given overlay to this overlay. + * @param overlay + * Overlay from which data is transferred. + */ +void +ChartTwoOverlay::copyData(const ChartTwoOverlay* overlay) +{ + CaretAssert(overlay); + + /* + * These members are not copied since they + * identify the overlay: + * m_parentChartTwoOverlaySet + * m_name + * m_overlayIndex + * + */ + + m_enabled = overlay->m_enabled; + + m_mapYokingGroup = overlay->m_mapYokingGroup; + + *m_colorBar = *overlay->m_colorBar; + m_matrixTriangularViewingMode = overlay->m_matrixTriangularViewingMode; + m_cartesianVerticalAxisLocation = overlay->m_cartesianVerticalAxisLocation; + + m_selectedMapFile = overlay->m_selectedMapFile; + m_selectedHistogramMapIndex = overlay->m_selectedHistogramMapIndex; + m_allHistogramMapsSelectedFlag = overlay->m_allHistogramMapsSelectedFlag; +} + +/** + * Swap my data with data from the given overlay. + * @param overlay + * Overlay from which data is swapped. + */ +void +ChartTwoOverlay::swapData(ChartTwoOverlay* overlay) +{ + std::unique_ptr swapOverlay = std::unique_ptr(new ChartTwoOverlay(m_parentChartTwoOverlaySet, + overlay->m_chartDataType, + overlay->m_tabIndex, + overlay->m_overlayIndex)); + swapOverlay->copyData(overlay); + + overlay->copyData(this); + copyData(swapOverlay.get()); +} + +/** + * Is history supported ? + */ +bool +ChartTwoOverlay::isHistorySupported() const +{ + bool supportedFlag = false; + + switch (m_chartDataType) { + case ChartTwoDataTypeEnum::CHART_DATA_TYPE_INVALID: + break; + case ChartTwoDataTypeEnum::CHART_DATA_TYPE_HISTOGRAM: + break; + case ChartTwoDataTypeEnum::CHART_DATA_TYPE_LINE_SERIES: + supportedFlag = true; + break; + case ChartTwoDataTypeEnum::CHART_DATA_TYPE_MATRIX: + break; + } + return supportedFlag; +} + +/** + * @return Is map yoking supported ? + * + * NOTE: Within this class, do not use this method. + * Instead, use isMapYokingSupportedPrivate(). + */ +bool +ChartTwoOverlay::isMapYokingSupported() const +{ + CaretMappableDataFile* mapFile = NULL; + SelectedIndexType indexType = SelectedIndexType::INVALID; + int32_t mapIndex = -1; + getSelectionData(mapFile, indexType, mapIndex); + + return isMapYokingSupportedPrivate(mapFile); +} + +/** + * Is map yoking supported for the given map file? + * This is a private method and used within this class. + * as use of the public method could cause stack + * overlow by use of isMapYokingSupported() and + * getSelectionData(). + * + * @param mapFile + * The map file. A NULL value is allowed. + * @return + * True if map yoking is supported, else false. + */ +bool +ChartTwoOverlay::isMapYokingSupportedPrivate(const CaretMappableDataFile* mapFile) const +{ + if (mapFile == NULL) { + return false; + } + + if (mapFile->getNumberOfMaps() < 2) { + return false; + } + + bool supportedFlag = false; + + switch (m_chartDataType) { + case ChartTwoDataTypeEnum::CHART_DATA_TYPE_INVALID: + break; + case ChartTwoDataTypeEnum::CHART_DATA_TYPE_HISTOGRAM: + if (mapFile->isSurfaceMappable() + || mapFile->isVolumeMappable()) { + supportedFlag = true; + } + break; + case ChartTwoDataTypeEnum::CHART_DATA_TYPE_LINE_SERIES: + if (mapFile->getDataFileType() == DataFileTypeEnum::CONNECTIVITY_SCALAR_DATA_SERIES) { + supportedFlag = true; + } + break; + case ChartTwoDataTypeEnum::CHART_DATA_TYPE_MATRIX: + if (mapFile->getDataFileType() == DataFileTypeEnum::CONNECTIVITY_SCALAR_DATA_SERIES) { + supportedFlag = true; + } + break; + } + + return supportedFlag; +} + +/** + * @return Selected map yoking group. + */ +MapYokingGroupEnum::Enum +ChartTwoOverlay::getMapYokingGroup() const +{ + return m_mapYokingGroup; +} + +/** + * Set the map yoking group. + * + * @param mapYokingGroup + * New value for map yoking group. + */ +void +ChartTwoOverlay::setMapYokingGroup(const MapYokingGroupEnum::Enum mapYokingGroup) +{ + m_mapYokingGroup = mapYokingGroup; +} + +/** + * @return The color bar displayed in graphics window. + */ +AnnotationColorBar* +ChartTwoOverlay::getColorBar() +{ + return m_colorBar.get(); +} + +/** + * @return The color bar displayed in graphics window (const method). + */ +const AnnotationColorBar* +ChartTwoOverlay::getColorBar() const +{ + return m_colorBar.get(); +} + +/** + * Get a bounding box for data displayed within this overlay. + * Bounds are provided for histogram and line-series charts only. + * + * @param boundingBox + * Upon exit contains bounds for data within this overlay + * @return + * True if the bounds are valid, else false. + */ +bool +ChartTwoOverlay::getBounds(BoundingBox& boundingBoxOut) const +{ + boundingBoxOut.resetForUpdate(); + + if ( ! isEnabled()) { + return false; + } + + CaretMappableDataFile* mapFile = NULL; + SelectedIndexType selectedIndexType = SelectedIndexType::INVALID; + int32_t selectedIndex = -1; + getSelectionData(mapFile, + selectedIndexType, + selectedIndex); + + if (mapFile == NULL) { + return false; + } + + ChartableTwoFileDelegate* chartDelegate = mapFile->getChartingDelegate(); + + bool validFlag = false; + + switch (m_chartDataType) { + case ChartTwoDataTypeEnum::CHART_DATA_TYPE_HISTOGRAM: + { + ChartableTwoFileHistogramChart* histogramChart = chartDelegate->getHistogramCharting(); + const Histogram* histogram = histogramChart->getHistogramForChartDrawing(selectedIndex, + (isAllMapsSupported() + && isAllMapsSelected())); + //CaretAssert(histogram); + if (histogram != NULL) { + float histogramMinX = 0.0, histogramMaxX = 0.0, histogramMaxY = 0.0; + histogram->getRangeAndMaxDisplayHeight(histogramMinX, histogramMaxX, histogramMaxY); + if (histogramMaxX > histogramMinX) { + boundingBoxOut.set(histogramMinX, histogramMaxX, + 0.0f, histogramMaxY, + 0.0f, 0.0f); + validFlag = true; + } + } + } + break; + case ChartTwoDataTypeEnum::CHART_DATA_TYPE_INVALID: + break; + case ChartTwoDataTypeEnum::CHART_DATA_TYPE_LINE_SERIES: + { + const ChartableTwoFileLineSeriesChart* lineSeriesChart = chartDelegate->getLineSeriesCharting(); + const ChartTwoLineSeriesHistory* lineSeriesHistory = lineSeriesChart->getHistory(); + validFlag = lineSeriesHistory->getBounds(boundingBoxOut); + } + break; + case ChartTwoDataTypeEnum::CHART_DATA_TYPE_MATRIX: + break; + } + + return validFlag; +} + +/** + * @return The selected map file (NULL if not map file available) + */ +CaretMappableDataFile* +ChartTwoOverlay::getSelectedMapFile() const +{ + std::vector mapFiles; + CaretMappableDataFile* selectedMapFile = NULL; + SelectedIndexType selectedIndexType = SelectedIndexType::INVALID; + int32_t selectedIndex = -1; + getSelectionData(mapFiles, + selectedMapFile, + selectedIndexType, + selectedIndex); + + return selectedMapFile; +} + +/** + * Return the selection information. This method is typically + * called to update the user-interface. + * + * @param selectedMapFileOut + * The selected map file. May be NULL. + * @param selectedIndexTypeOut + * Type of index selection + * @param selectedIndexOut + * Selected index in the selected file. + */ +void +ChartTwoOverlay::getSelectionData(CaretMappableDataFile* &selectedMapFileOut, + SelectedIndexType& selectedIndexTypeOut, + int32_t& selectedIndexOut) const +{ + std::vector mapFiles; + getSelectionData(mapFiles, + selectedMapFileOut, + selectedIndexTypeOut, + selectedIndexOut); +} + +/** + * Return the selection information. This method is typically + * called to update the user-interface. + * + * @param mapFilesOut + * Contains all map files that can be selected. + * @param selectedMapFileOut + * The selected map file. May be NULL. + * @param selectedFileMapNamesOut + * Map names from selected file. + * @param selectedIndexTypeOut + * Type of index selection + * @param selectedIndexOut + * Selected index in the selected file. + */ +void +ChartTwoOverlay::getSelectionData(std::vector& mapFilesOut, + CaretMappableDataFile* &selectedMapFileOut, + std::vector& selectedFileMapNamesOut, + SelectedIndexType& selectedIndexTypeOut, + int32_t& selectedIndexOut) const +{ + getSelectionDataPrivate(mapFilesOut, + selectedMapFileOut, + &selectedFileMapNamesOut, + selectedIndexTypeOut, + selectedIndexOut); +} + +/** + * Return the selection information. This method is typically + * called to update the user-interface. + * + * @param mapFilesOut + * Contains all map files that can be selected. + * @param selectedMapFileOut + * The selected map file. May be NULL. + * @param selectedIndexTypeOut + * Type of index selection + * @param selectedIndexOut + * Selected index in the selected file. + */ +void +ChartTwoOverlay::getSelectionData(std::vector& mapFilesOut, + CaretMappableDataFile* &selectedMapFileOut, + SelectedIndexType& selectedIndexTypeOut, + int32_t& selectedIndexOut) const +{ + getSelectionDataPrivate(mapFilesOut, + selectedMapFileOut, + NULL, + selectedIndexTypeOut, + selectedIndexOut); +} + +/** + * Return the selection information. This method is typically + * called to update the user-interface. + * + * @param mapFilesOut + * Contains all map files that can be selected. + * @param selectedMapFileOut + * The selected map file. May be NULL. + * @param selectedFileMapNamesOut + * Optional output, if not NULL, with map names + * @param selectedMapIndexOut + * Index of selected map in the selected file. + */ +void +ChartTwoOverlay::getSelectionDataPrivate(std::vector& mapFilesOut, + CaretMappableDataFile* &selectedMapFileOut, + std::vector* selectedFileMapNamesOut, + SelectedIndexType& selectedIndexTypeOut, + int32_t& selectedIndexOut) const +{ + mapFilesOut.clear(); + selectedMapFileOut = NULL; + selectedIndexTypeOut = SelectedIndexType::INVALID; + selectedIndexOut = -1; + if (selectedFileMapNamesOut != NULL) { + selectedFileMapNamesOut->clear(); + } + + /** + * Get the data files. + */ + std::vector allDataFiles; + EventCaretMappableDataFilesGet eventGetMapDataFiles; + EventManager::get()->sendEvent(eventGetMapDataFiles.getPointer()); + eventGetMapDataFiles.getAllFiles(allDataFiles); + + /* + * Use only those data files that meet criteria. + */ + for (auto mapFile : allDataFiles) { + CaretAssert(mapFile); + ChartableTwoFileDelegate* chartingFile = mapFile->getChartingDelegate(); + if (chartingFile->isChartingTwoSupported()) { + bool useIt = false; + + std::vector chartCompoundDataTypes; + chartingFile->getSupportedChartTwoCompoundDataTypes(chartCompoundDataTypes); + + for (auto& compoundType : chartCompoundDataTypes) { + if (m_chartDataType == compoundType.getChartTwoDataType()) { + if (m_overlayIndex == 0) { + /* + * The first overlay displays ALL files that match the + * enumerated chart type + */ + useIt = true; + } + else { + if (m_chartCompoundDataType == compoundType) { + /* + * If not the first overlay, the enumerated type + * and dimensions must also match + */ + useIt = true; + } + } + } + } + + if (useIt) { + mapFilesOut.push_back(mapFile); + } + } + } + + /* + * Does selected data file still no longer exist? + */ + if (std::find(mapFilesOut.begin(), + mapFilesOut.end(), + m_selectedMapFile) == mapFilesOut.end()) { + /* + * Invalidate seleted file and disable yoking since + * the selected file will change. + */ + m_selectedMapFile = NULL; + m_mapYokingGroup = MapYokingGroupEnum::MAP_YOKING_GROUP_OFF; + } + + /* + * If no file selected, select the first valid file + */ + if (m_selectedMapFile == NULL) { + if ( ! mapFilesOut.empty()) { + for (std::vector::iterator iter = mapFilesOut.begin(); + iter != mapFilesOut.end(); + iter++) { + CaretMappableDataFile* mapTypeFile = *iter; + m_selectedMapFile = mapTypeFile; + switch (m_chartDataType) { + case ChartTwoDataTypeEnum::CHART_DATA_TYPE_INVALID: + break; + case ChartTwoDataTypeEnum::CHART_DATA_TYPE_HISTOGRAM: + m_selectedHistogramMapIndex = 0; + break; + case ChartTwoDataTypeEnum::CHART_DATA_TYPE_LINE_SERIES: + break; + case ChartTwoDataTypeEnum::CHART_DATA_TYPE_MATRIX: + break; + } + break; + } + } + } + + if (m_selectedMapFile != NULL) { + int32_t numMaps = 0; + + ChartableTwoFileMatrixChart* matrixChart = NULL; + + switch (m_chartDataType) { + case ChartTwoDataTypeEnum::CHART_DATA_TYPE_INVALID: + break; + case ChartTwoDataTypeEnum::CHART_DATA_TYPE_HISTOGRAM: + { + numMaps = m_selectedMapFile->getNumberOfMaps(); + if (selectedFileMapNamesOut != NULL) { + for (int32_t i = 0; i < numMaps; i++) { + selectedFileMapNamesOut->push_back(m_selectedMapFile->getMapName(i)); + } + } + if (m_selectedHistogramMapIndex >= numMaps) { + m_selectedHistogramMapIndex = numMaps - 1; + } + if (m_selectedHistogramMapIndex < 0) { + m_selectedHistogramMapIndex = 0; + } + selectedIndexTypeOut = SelectedIndexType::MAP; + selectedIndexOut = m_selectedHistogramMapIndex; + } + break; + case ChartTwoDataTypeEnum::CHART_DATA_TYPE_LINE_SERIES: + { + /* + * Row/Column Scalar files use the matrix for line chart tracking + */ + ChartableTwoFileDelegate* chartDelegate = m_selectedMapFile->getChartingDelegate(); + ChartableTwoFileLineSeriesChart* lineChart = chartDelegate->getLineSeriesCharting(); + CaretAssert(lineChart); + + switch (lineChart->getLineSeriesContentType()) { + case ChartTwoLineSeriesContentTypeEnum::LINE_SERIES_CONTENT_UNSUPPORTED: + break; + case ChartTwoLineSeriesContentTypeEnum::LINE_SERIES_CONTENT_BRAINORDINATE_DATA: + break; + case ChartTwoLineSeriesContentTypeEnum::LINE_SERIES_CONTENT_ROW_SCALAR_DATA: + matrixChart = chartDelegate->getMatrixCharting(); + break; + } + } + break; + case ChartTwoDataTypeEnum::CHART_DATA_TYPE_MATRIX: + { + ChartableTwoFileDelegate* chartDelegate = m_selectedMapFile->getChartingDelegate(); + matrixChart = chartDelegate->getMatrixCharting(); +// const ChartableTwoFileMatrixChart* matrixChart = chartDelegate->getMatrixCharting(); +// CaretAssert(matrixChart); +// int32_t numRows = 0; +// int32_t numCols = 0; +// matrixChart->getMatrixDimensions(numRows, numCols); +// +// ChartTwoMatrixLoadingDimensionEnum::Enum rowColumnDimension; +// std::vector columnIndices; +// std::vector rowIndices; +// matrixChart->getSelectedRowColumnIndices(m_parentChartTwoOverlaySet->m_tabIndex, +// rowColumnDimension, +// rowIndices, +// columnIndices); +// +// switch (rowColumnDimension) { +// case ChartTwoMatrixLoadingDimensionEnum::CHART_MATRIX_LOADING_BY_COLUMN: +// numMaps = numCols; +// if ( ! columnIndices.empty()) { +// selectedIndexTypeOut = SelectedIndexType::COLUMN; +// selectedIndexOut = columnIndices[0]; +// } +// if (matrixChart->hasColumnSelection()) { +// if (selectedFileMapNamesOut != NULL) { +// for (int32_t i = 0; i < numMaps; i++) { +// selectedFileMapNamesOut->push_back(matrixChart->getColumnName(i)); +// } +// } +// } +// break; +// case ChartTwoMatrixLoadingDimensionEnum::CHART_MATRIX_LOADING_BY_ROW: +// numMaps = numRows; +// if ( ! rowIndices.empty()) { +// selectedIndexTypeOut = SelectedIndexType::ROW; +// selectedIndexOut = rowIndices[0]; +// } +// if (matrixChart->hasRowSelection()) { +// if (selectedFileMapNamesOut != NULL) { +// for (int32_t i = 0; i < numMaps; i++) { +// selectedFileMapNamesOut->push_back(matrixChart->getRowName(i)); +// } +// } +// } +// break; +// } + } + break; + } + + if (matrixChart != NULL) { + CaretAssert(matrixChart); + int32_t numRows = 0; + int32_t numCols = 0; + matrixChart->getMatrixDimensions(numRows, numCols); + + ChartTwoMatrixLoadingDimensionEnum::Enum rowColumnDimension; + std::vector columnIndices; + std::vector rowIndices; + matrixChart->getSelectedRowColumnIndices(m_parentChartTwoOverlaySet->m_tabIndex, + rowColumnDimension, + rowIndices, + columnIndices); + + switch (rowColumnDimension) { + case ChartTwoMatrixLoadingDimensionEnum::CHART_MATRIX_LOADING_BY_COLUMN: + numMaps = numCols; + if ( ! columnIndices.empty()) { + selectedIndexTypeOut = SelectedIndexType::COLUMN; + selectedIndexOut = columnIndices[0]; + } + if (matrixChart->hasColumnSelection()) { + if (selectedFileMapNamesOut != NULL) { + for (int32_t i = 0; i < numMaps; i++) { + selectedFileMapNamesOut->push_back(matrixChart->getColumnName(i)); + } + } + } + break; + case ChartTwoMatrixLoadingDimensionEnum::CHART_MATRIX_LOADING_BY_ROW: + numMaps = numRows; + if ( ! rowIndices.empty()) { + selectedIndexTypeOut = SelectedIndexType::ROW; + selectedIndexOut = rowIndices[0]; + } + if (matrixChart->hasRowSelection()) { + if (selectedFileMapNamesOut != NULL) { + for (int32_t i = 0; i < numMaps; i++) { + selectedFileMapNamesOut->push_back(matrixChart->getRowName(i)); + } + } + } + break; + } + } + + } + + selectedMapFileOut = m_selectedMapFile; + + /* + * Update the compound data type if this is the FIRST OVERLAY + */ + if (m_overlayIndex == 0) { + if (m_selectedMapFile != NULL) { + const ChartableTwoFileDelegate* chartFile = m_selectedMapFile->getChartingDelegate(); + CaretAssert(chartFile); + chartFile->getChartTwoCompoundDataTypeForChartTwoDataType(m_chartDataType, + m_chartCompoundDataType); + } + CaretAssert(m_parentChartTwoOverlaySet); + m_parentChartTwoOverlaySet->firstOverlaySelectionChanged(); + } + + if (m_mapYokingGroup != MapYokingGroupEnum::MAP_YOKING_GROUP_OFF) { + if ( ! isMapYokingSupportedPrivate(m_selectedMapFile)) { + m_mapYokingGroup = MapYokingGroupEnum::MAP_YOKING_GROUP_OFF; + } + } +} + +/** + * Set the selected map file and map. + * @param selectedMapFile + * File that is selected. + * @param selectedMapIndex + * Index of map for selection. If invalid, try the current map + * index if it is valid. Otherwise, use the first map index. + */ +void +ChartTwoOverlay::setSelectionData(CaretMappableDataFile* selectedMapFile, + const int32_t selectedMapIndex) +{ + m_selectedMapFile = selectedMapFile; + if (m_selectedMapFile != NULL) { + ChartableTwoFileLineSeriesChart* lineSeriesChart = NULL; + ChartableTwoFileMatrixChart* matrixChart = NULL; + + switch (m_chartDataType) { + case ChartTwoDataTypeEnum::CHART_DATA_TYPE_INVALID: + break; + case ChartTwoDataTypeEnum::CHART_DATA_TYPE_HISTOGRAM: + if (selectedMapIndex >= 0) { + m_selectedHistogramMapIndex = selectedMapIndex; + } + break; + case ChartTwoDataTypeEnum::CHART_DATA_TYPE_LINE_SERIES: + { + /* + * Row/Column Scalar files use the matrix for line chart tracking + */ + ChartableTwoFileDelegate* chartDelegate = m_selectedMapFile->getChartingDelegate(); + ChartableTwoFileLineSeriesChart* lineChart = chartDelegate->getLineSeriesCharting(); + CaretAssert(lineChart); + + switch (lineChart->getLineSeriesContentType()) { + case ChartTwoLineSeriesContentTypeEnum::LINE_SERIES_CONTENT_UNSUPPORTED: + break; + case ChartTwoLineSeriesContentTypeEnum::LINE_SERIES_CONTENT_BRAINORDINATE_DATA: + break; + case ChartTwoLineSeriesContentTypeEnum::LINE_SERIES_CONTENT_ROW_SCALAR_DATA: + lineSeriesChart = lineChart; + matrixChart = chartDelegate->getMatrixCharting(); + break; + } + } + break; + case ChartTwoDataTypeEnum::CHART_DATA_TYPE_MATRIX: + { + ChartableTwoFileDelegate* chartDelegate = m_selectedMapFile->getChartingDelegate(); + CaretAssert(chartDelegate); + matrixChart = chartDelegate->getMatrixCharting(); +// ChartableTwoFileMatrixChart* matrixChart = chartDelegate->getMatrixCharting(); +// CaretAssert(matrixChart); +// matrixChart->setSelectedRowColumnIndex(m_parentChartTwoOverlaySet->m_tabIndex, +// selectedMapIndex); + } + break; + } + + if (matrixChart != NULL) { + matrixChart->setSelectedRowColumnIndex(m_parentChartTwoOverlaySet->m_tabIndex, + selectedMapIndex); + if (lineSeriesChart) { + lineSeriesChart->loadDataForRowOrColumn(m_tabIndex, + selectedMapIndex); + } + } + + if (m_mapYokingGroup != MapYokingGroupEnum::MAP_YOKING_GROUP_OFF) { + if (m_selectedMapFile == NULL) { + m_mapYokingGroup = MapYokingGroupEnum::MAP_YOKING_GROUP_OFF; + } + } + } + + + /* + * By calling getSelectionData(), it will validate the + * selected file and map index and update if needed + * (such as a valid map index). + */ + CaretMappableDataFile* mapFile = NULL; + ChartTwoOverlay::SelectedIndexType selectedIndexType = ChartTwoOverlay::SelectedIndexType::INVALID; + int32_t selectedIndex = -1; + getSelectionData(mapFile, + selectedIndexType, + selectedIndex); +} + +/** + * @return Is the all maps supported? + */ +bool +ChartTwoOverlay::isAllMapsSupported() const +{ + bool supportedFlag = false; + + switch (m_chartDataType) { + case ChartTwoDataTypeEnum::CHART_DATA_TYPE_INVALID: + break; + case ChartTwoDataTypeEnum::CHART_DATA_TYPE_HISTOGRAM: + { + CaretMappableDataFile* cmdf = NULL; + SelectedIndexType indexType = SelectedIndexType::INVALID; + int32_t mapIndex = 0; + getSelectionData(cmdf, + indexType, + mapIndex); + + if (cmdf != NULL) { + if (cmdf->getNumberOfMaps() > 1) { + supportedFlag = true; + } + } + } + break; + case ChartTwoDataTypeEnum::CHART_DATA_TYPE_LINE_SERIES: + break; + case ChartTwoDataTypeEnum::CHART_DATA_TYPE_MATRIX: + break; + } + + return supportedFlag; +} + +/** + * @return All maps selected. + */ +bool +ChartTwoOverlay::isAllMapsSelected() const +{ + return m_allHistogramMapsSelectedFlag; +} + +/** + * Set all maps selected. + * + * @param status + * New status. + */ +void +ChartTwoOverlay::setAllMapsSelected(const bool status) +{ + m_allHistogramMapsSelectedFlag = status; +} + +/** + * @return The matrix triangular viewing mode. + */ +ChartTwoMatrixTriangularViewingModeEnum::Enum +ChartTwoOverlay::getMatrixTriangularViewingMode() const +{ + if ( ! isMatrixTriangularViewingModeSupported()) { + if (m_matrixTriangularViewingMode != ChartTwoMatrixTriangularViewingModeEnum::MATRIX_VIEW_FULL) { + m_matrixTriangularViewingMode = ChartTwoMatrixTriangularViewingModeEnum::MATRIX_VIEW_FULL; + } + } + + return m_matrixTriangularViewingMode; +} + +/** + * Set the matrix triangular viewing mode. + * + * @param mode + * New triangular viewing mode. + */ +void +ChartTwoOverlay::setMatrixTriangularViewingMode(const ChartTwoMatrixTriangularViewingModeEnum::Enum mode) +{ + m_matrixTriangularViewingMode = mode; +} + +/** + * @return Is the matrix triangular view mode supported? + */ +bool +ChartTwoOverlay::isMatrixTriangularViewingModeSupported() const +{ + switch (m_chartDataType) { + case ChartTwoDataTypeEnum::CHART_DATA_TYPE_INVALID: + break; + case ChartTwoDataTypeEnum::CHART_DATA_TYPE_HISTOGRAM: + break; + case ChartTwoDataTypeEnum::CHART_DATA_TYPE_LINE_SERIES: + break; + case ChartTwoDataTypeEnum::CHART_DATA_TYPE_MATRIX: + { + ChartableTwoFileDelegate* chartDelegate = m_selectedMapFile->getChartingDelegate(); + const ChartableTwoFileMatrixChart* matrixChart = chartDelegate->getMatrixCharting(); + return matrixChart->isMatrixTriangularViewingModeSupported(); + } + } + + return false; +} + +/** + * @return Location of vertical cartesian axis + */ +ChartAxisLocationEnum::Enum +ChartTwoOverlay::getCartesianVerticalAxisLocation() const +{ + validateCartesianVerticalAxisLocation(); + return m_cartesianVerticalAxisLocation; +} + +/** + * Set Location of vertical cartesian axis + * + * @param cartesianVerticalAxisLocation + * New value for Location of vertical cartesian axis + */ +void +ChartTwoOverlay::setCartesianVerticalAxisLocation(const ChartAxisLocationEnum::Enum cartesianVerticalAxisLocation) +{ + m_cartesianVerticalAxisLocation = cartesianVerticalAxisLocation; + validateCartesianVerticalAxisLocation(); +} + +/** + * Validate cartesian vertical axis to valid locations (left and right) + */ +void +ChartTwoOverlay::validateCartesianVerticalAxisLocation() const +{ + + switch (m_cartesianVerticalAxisLocation) { + case ChartAxisLocationEnum::CHART_AXIS_LOCATION_BOTTOM: + m_cartesianVerticalAxisLocation = ChartAxisLocationEnum::CHART_AXIS_LOCATION_LEFT; + break; + case ChartAxisLocationEnum::CHART_AXIS_LOCATION_LEFT: + break; + case ChartAxisLocationEnum::CHART_AXIS_LOCATION_RIGHT: + break; + case ChartAxisLocationEnum::CHART_AXIS_LOCATION_TOP: + m_cartesianVerticalAxisLocation = ChartAxisLocationEnum::CHART_AXIS_LOCATION_LEFT; + break; + } +} + + +/** + * @return Is the cartesian vertical axis location supported? + */ +bool +ChartTwoOverlay::isCartesianVerticalAxisLocationSupported() const +{ + switch (m_chartDataType) { + case ChartTwoDataTypeEnum::CHART_DATA_TYPE_INVALID: + break; + case ChartTwoDataTypeEnum::CHART_DATA_TYPE_HISTOGRAM: + return true; + break; + case ChartTwoDataTypeEnum::CHART_DATA_TYPE_LINE_SERIES: + return true; + break; + case ChartTwoDataTypeEnum::CHART_DATA_TYPE_MATRIX: + break; + } + + return false; +} + + +/** + * Save information specific to this type of model to the scene. + * + * @param sceneAttributes + * Attributes for the scene. Scenes may be of different types + * (full, generic, etc) and the attributes should be checked when + * saving the scene. + * + * @param instanceName + * Name of instance in the scene. + */ +SceneClass* +ChartTwoOverlay::saveToScene(const SceneAttributes* sceneAttributes, + const AString& instanceName) +{ + SceneClass* sceneClass = new SceneClass(instanceName, + "ChartTwoOverlay", + 1); + m_sceneAssistant->saveMembers(sceneAttributes, + sceneClass); + + std::vector mapFiles; + CaretMappableDataFile* selectedMapFile = NULL; + //AString selectedMapUniqueID; + SelectedIndexType selectedIndexType = SelectedIndexType::INVALID; + int32_t selectedMapIndex; + getSelectionData(mapFiles, + selectedMapFile, + selectedIndexType, + selectedMapIndex); + + AString sceneSelectedMapFileNameWithPath; + AString sceneSelectedMapFileNameNoPath; + AString sceneSelectedMapName; + int32_t sceneSelectedMapIndex = selectedMapIndex; + + /* + * NOTE:Some of the connectivity matrix files may not have a valid + * selection index (-1) until the user identifies a brainordinate + */ + if (selectedMapFile != NULL) { + sceneSelectedMapFileNameWithPath = selectedMapFile->getFileName(); + sceneSelectedMapFileNameNoPath = selectedMapFile->getFileNameNoPath(); + if ((selectedMapIndex >= 0) + && (selectedMapIndex < selectedMapFile->getNumberOfMaps())) { + sceneSelectedMapName = selectedMapFile->getMapName(selectedMapIndex); + } + } + + sceneClass->addPathName("sceneSelectedMapFileNameWithPath", + sceneSelectedMapFileNameWithPath); + sceneClass->addString("sceneSelectedMapFileNameNoPath", + sceneSelectedMapFileNameNoPath); + sceneClass->addString("sceneSelectedMapName", + sceneSelectedMapName); + sceneClass->addInteger("sceneSelectedMapIndex", + sceneSelectedMapIndex); + + // Uncomment if sub-classes must save to scene + //saveSubClassDataToScene(sceneAttributes, + // sceneClass); + + return sceneClass; +} + +/** + * Restore information specific to the type of model from the scene. + * + * @param sceneAttributes + * Attributes for the scene. Scenes may be of different types + * (full, generic, etc) and the attributes should be checked when + * restoring the scene. + * + * @param sceneClass + * sceneClass from which model specific information is obtained. + */ +void +ChartTwoOverlay::restoreFromScene(const SceneAttributes* sceneAttributes, + const SceneClass* sceneClass) +{ + if (sceneClass == NULL) { + return; + } + + m_sceneAssistant->restoreMembers(sceneAttributes, + sceneClass); + const MapYokingGroupEnum::Enum mapYokingGroupFromScene = m_mapYokingGroup; + + /* + * Making a call to getSelectionData() to get the availble + * map files + */ + std::vector mapFiles; + CaretMappableDataFile* unusedSelectedMapFile = NULL; + SelectedIndexType unusedSelectedIndexType = SelectedIndexType::INVALID; + int32_t unusedSelectedMapIndex; + getSelectionData(mapFiles, + unusedSelectedMapFile, + unusedSelectedIndexType, + unusedSelectedMapIndex); + + const AString selectedMapFileNameWithPath = sceneClass->getPathNameValue("sceneSelectedMapFileNameWithPath"); + + const AString selectedMapFileName = sceneClass->getStringValue("sceneSelectedMapFileNameNoPath", + ""); + const AString selectedMapName = sceneClass->getStringValue("sceneSelectedMapName", + ""); + const int32_t selectedMapIndex = sceneClass->getIntegerValue("sceneSelectedMapIndex", + -1); + + bool found = false; + + /* + * Is used when the file is found but a map is not matched + */ + CaretMappableDataFile* matchedMapFile = NULL; + + /* + * First try to find file by filename INCLUDING path and map by unique ID + */ + + /* + * Find map by unique ID, map index, and map file + */ + CaretMappableDataFile* foundMapNameFile = NULL; + int32_t foundMapNameIndex = -1; + CaretMappableDataFile* foundMapIndexFile = NULL; + int32_t foundMapIndex = -1; + + /* + * Try to match files twice. First time by name with path, then + * by name without path. + */ + for (int iTries = 0; iTries < 2; iTries++) { + for (std::vector::iterator iter = mapFiles.begin(); + iter != mapFiles.end(); + iter++) { + CaretMappableDataFile* mapFile = *iter; + + bool testIt = false; + switch (iTries) { + case 0: { + const AString fileName = mapFile->getFileName(); + if (fileName == selectedMapFileNameWithPath) { + testIt = true; + } + } + break; + + case 1: { + const AString fileName = mapFile->getFileNameNoPath(); + if (fileName == selectedMapFileName) { + testIt = true; + } + } + break; + } + + + if (testIt) { + CaretMappableDataFile* mapFile = *iter; + matchedMapFile = mapFile; + + if (foundMapNameIndex < 0) { + if ( ! selectedMapName.isEmpty()) { + const int mapNameIndex = mapFile->getMapIndexFromName(selectedMapName); + if (mapNameIndex >= 0) { + foundMapNameFile = mapFile; + foundMapNameIndex = mapNameIndex; + } + } + + } + + if (foundMapIndex < 0) { + if (selectedMapIndex >= 0) { + if (selectedMapIndex < mapFile->getNumberOfMaps()) { + foundMapIndexFile = mapFile; + foundMapIndex = selectedMapIndex; + } + } + } + } + } + } + + if (! found) { + if (foundMapIndex >= 0) { + if (foundMapIndexFile != NULL) { + setSelectionData(foundMapIndexFile, + foundMapIndex); + found = true; + } + } + } + + if (! found) { + if (foundMapNameIndex >= 0) { + if (foundMapNameFile != NULL) { + setSelectionData(foundMapNameFile, + foundMapNameIndex); + found = true; + } + } + } + + if ( ! found) { + /* + * If not found by unique ID, try to find map by name + */ + if ( ! selectedMapName.isEmpty()) { + for (std::vector::iterator iter = mapFiles.begin(); + iter != mapFiles.end(); + iter++) { + CaretMappableDataFile* mapFile = *iter; + const AString fileName = mapFile->getFileNameNoPath(); + if (fileName == selectedMapFileName) { + CaretMappableDataFile* mapFile = *iter; + matchedMapFile = mapFile; + + const int32_t mapIndex = mapFile->getMapIndexFromName(selectedMapName); + if (mapIndex >= 0) { + setSelectionData(mapFile, + mapIndex); + found = true; + break; + } + } + } + } + } + + /* + * NOTE:Some of the connectivity matrix files may not have a valid + * selection index (-1) until the user identifies a brainordinate + */ + if ( ! found) { + if (matchedMapFile != NULL) { + if (matchedMapFile->getNumberOfMaps() > 0) { + int32_t defaultMapIndex = -1; + switch (m_chartDataType) { + case ChartTwoDataTypeEnum::CHART_DATA_TYPE_HISTOGRAM: + defaultMapIndex = 0; + break; + case ChartTwoDataTypeEnum::CHART_DATA_TYPE_INVALID: + break; + case ChartTwoDataTypeEnum::CHART_DATA_TYPE_LINE_SERIES: + defaultMapIndex = 0; + break; + case ChartTwoDataTypeEnum::CHART_DATA_TYPE_MATRIX: + break; + } + setSelectionData(matchedMapFile, + defaultMapIndex); + } + } + } + + if (mapYokingGroupFromScene != MapYokingGroupEnum::MAP_YOKING_GROUP_OFF) { + /* + * Need to update selections and then apply yoking as + * yoking is cleared when the the previous file + * was not found. + */ + CaretMappableDataFile* mapFile = NULL; + SelectedIndexType indexType = SelectedIndexType::INVALID; + int32_t mapIndex = -1; + getSelectionData(mapFile, indexType, mapIndex); + setMapYokingGroup(mapYokingGroupFromScene); + } + + + + + + //Uncomment if sub-classes must restore from scene + //restoreSubClassDataFromScene(sceneAttributes, + // sceneClass); + +} + diff --git a/src/Brain/ChartTwoOverlay.h b/src/Brain/ChartTwoOverlay.h new file mode 100644 index 0000000000000000000000000000000000000000..8c429f844abe1b4022923a1a6aada4dfe91232b6 --- /dev/null +++ b/src/Brain/ChartTwoOverlay.h @@ -0,0 +1,229 @@ +#ifndef __CHART_TWO_OVERLAY_H__ +#define __CHART_TWO_OVERLAY_H__ + +/*LICENSE_START*/ +/* + * Copyright (C) 2016 Washington University School of Medicine + * + * 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 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ +/*LICENSE_END*/ + +#include + +#include "CaretObject.h" +#include "ChartAxisLocationEnum.h" +#include "ChartTwoCompoundDataType.h" +#include "ChartTwoMatrixTriangularViewingModeEnum.h" +#include "EventListenerInterface.h" +#include "MapYokingGroupEnum.h" +#include "SceneableInterface.h" + + +namespace caret { + class AnnotationColorBar; + class BoundingBox; + class CaretMappableDataFile; + class ChartTwoOverlaySet; + class PlainTextStringBuilder; + class SceneClassAssistant; + + class ChartTwoOverlay : public CaretObject, public EventListenerInterface, public SceneableInterface { + + public: + enum class SelectedIndexType { + INVALID, + MAP, + ROW, + COLUMN + }; + + ChartTwoOverlay(ChartTwoOverlaySet* parentChartTwoOverlaySet, + const ChartTwoDataTypeEnum::Enum chartDataType, + const int32_t tabIndex, + const int32_t overlayIndex); + + virtual ~ChartTwoOverlay(); + + std::weak_ptr getWeakPointerToSelf(); + + ChartTwoDataTypeEnum::Enum getChartTwoDataType() const; + + ChartTwoCompoundDataType getChartTwoCompoundDataType() const; + + void setChartTwoCompoundDataType(const ChartTwoCompoundDataType& chartCompoundDataType); + + int32_t getTabIndex() const; + + virtual AString toString() const; + + virtual void getDescriptionOfContent(PlainTextStringBuilder& descriptionOut) const; + + bool isEnabled() const; + + void setEnabled(const bool enabled); + + bool isLineSeriesLoadingEnabled() const; + + void setLineSeriesLoadingEnabled(const bool lineSeriesLoadingEnabled); + + void copyData(const ChartTwoOverlay* overlay); + + void swapData(ChartTwoOverlay* overlay); + + bool isHistorySupported() const; + + bool isMapYokingSupported() const; + + MapYokingGroupEnum::Enum getMapYokingGroup() const; + + void setMapYokingGroup(const MapYokingGroupEnum::Enum mapYokingGroup); + + AnnotationColorBar* getColorBar(); + + const AnnotationColorBar* getColorBar() const; + + ChartTwoMatrixTriangularViewingModeEnum::Enum getMatrixTriangularViewingMode() const; + + void setMatrixTriangularViewingMode(const ChartTwoMatrixTriangularViewingModeEnum::Enum mode); + + bool isMatrixTriangularViewingModeSupported() const; + + ChartAxisLocationEnum::Enum getCartesianVerticalAxisLocation() const; + + void setCartesianVerticalAxisLocation(const ChartAxisLocationEnum::Enum cartesianVerticalAxisLocation); + + bool isCartesianVerticalAxisLocationSupported() const; + + CaretMappableDataFile* getSelectedMapFile() const; + + void getSelectionData(std::vector& mapFilesOut, + CaretMappableDataFile* &selectedMapFileOut, + std::vector& selectedFileMapNamesOut, + SelectedIndexType& selectedIndexTypeOut, + int32_t& selectedIndexOut) const; + + void getSelectionData(std::vector& mapFilesOut, + CaretMappableDataFile* &selectedMapFileOut, + SelectedIndexType& selectedIndexTypeOut, + int32_t& selectedIndexOut) const; + + void getSelectionData(CaretMappableDataFile* &selectedMapFileOut, + SelectedIndexType& selectedIndexTypeOut, + int32_t& selectedIndexOut) const; + + void setSelectionData(CaretMappableDataFile* selectedMapFile, + const int32_t selectedMapIndex); + + bool isAllMapsSupported() const; + + bool isAllMapsSelected() const; + + void setAllMapsSelected(const bool status); + + bool getBounds(BoundingBox& boundingBoxOut) const; + + virtual void receiveEvent(Event* event); + + // ADD_NEW_METHODS_HERE + + virtual SceneClass* saveToScene(const SceneAttributes* sceneAttributes, + const AString& instanceName); + + virtual void restoreFromScene(const SceneAttributes* sceneAttributes, + const SceneClass* sceneClass); + +// If there will be sub-classes of this class that need to save +// and restore data from scenes, these pure virtual methods can +// be uncommented to force their implementation by sub-classes. +// protected: +// virtual void saveSubClassDataToScene(const SceneAttributes* sceneAttributes, +// SceneClass* sceneClass) = 0; +// +// virtual void restoreSubClassDataFromScene(const SceneAttributes* sceneAttributes, +// const SceneClass* sceneClass) = 0; + + private: + ChartTwoOverlay(const ChartTwoOverlay&); + + ChartTwoOverlay& operator=(const ChartTwoOverlay&); + + void setWeakPointerToSelf(std::weak_ptr weakPointerToSelf); + + void getSelectionDataPrivate(std::vector& mapFilesOut, + CaretMappableDataFile* &selectedMapFileOut, + std::vector* selectedFileMapNamesOut, + SelectedIndexType& selectedIndexTypeOut, + int32_t& selectedIndexOut) const; + + bool isMapYokingSupportedPrivate(const CaretMappableDataFile* mapFile) const; + + void validateCartesianVerticalAxisLocation() const; + + /** Parent chart overlay set (only used by first overlay in the set */ + ChartTwoOverlaySet* m_parentChartTwoOverlaySet; + + /** Enumerated Type of charts allowed in this overlay */ + const ChartTwoDataTypeEnum::Enum m_chartDataType; + + const int32_t m_tabIndex; + + /** Index of this overlay (DO NOT COPY)*/ + const int32_t m_overlayIndex; + + std::unique_ptr m_sceneAssistant; + + /** Current 'compound chart type' of charts allowed in this overlay */ + mutable ChartTwoCompoundDataType m_chartCompoundDataType; + + /** Name of overlay (DO NOT COPY)*/ + AString m_name; + + /** enabled status */ + mutable bool m_enabled = true; + + /** map yoking group */ + mutable MapYokingGroupEnum::Enum m_mapYokingGroup; + + /** The color bar displayed in the graphics window */ + std::unique_ptr m_colorBar; + + /** selected mappable file */ + mutable CaretMappableDataFile* m_selectedMapFile = NULL; + + /** histogram selected map index */ + mutable int32_t m_selectedHistogramMapIndex = -1; + + bool m_allHistogramMapsSelectedFlag = false; + + mutable ChartTwoMatrixTriangularViewingModeEnum::Enum m_matrixTriangularViewingMode; + + /** Location of vertical cartesian axis*/ + mutable ChartAxisLocationEnum::Enum m_cartesianVerticalAxisLocation; + + /** A weak pointer to 'self' so that can be stored to safely test instance is valid and can be accessed */ + std::weak_ptr m_weakPointerToSelf; + + // ADD_NEW_MEMBERS_HERE + + friend class ChartTwoOverlaySet; + }; + +#ifdef __CHART_TWO_OVERLAY_DECLARE__ + // +#endif // __CHART_TWO_OVERLAY_DECLARE__ + +} // namespace +#endif //__CHART_TWO_OVERLAY_H__ diff --git a/src/Brain/ChartTwoOverlaySet.cxx b/src/Brain/ChartTwoOverlaySet.cxx new file mode 100644 index 0000000000000000000000000000000000000000..6a44acb528aa20e611cfcf873d58afd7ee50487b --- /dev/null +++ b/src/Brain/ChartTwoOverlaySet.cxx @@ -0,0 +1,1194 @@ + +/*LICENSE_START*/ +/* + * Copyright (C) 2016 Washington University School of Medicine + * + * 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 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ +/*LICENSE_END*/ + +#define __CHART_TWO_OVERLAY_SET_DECLARE__ +#include "ChartTwoOverlaySet.h" +#undef __CHART_TWO_OVERLAY_SET_DECLARE__ + +#include "AnnotationPercentSizeText.h" +#include "BoundingBox.h" +#include "BrowserTabContent.h" +#include "CaretAssert.h" +#include "CaretMappableDataFile.h" +#include "ChartTwoCartesianAxis.h" +#include "ChartTwoOverlay.h" +#include "ChartTwoTitle.h" +#include "ChartableTwoFileDelegate.h" +#include "ChartableTwoFileHistogramChart.h" +#include "ChartableTwoFileLineSeriesChart.h" +#include "EventAnnotationChartLabelGet.h" +#include "EventBrowserTabGet.h" +#include "EventChartTwoAttributesChanged.h" +#include "EventChartTwoAxisGetDataRange.h" +#include "EventManager.h" +#include "EventMapYokingSelectMap.h" +#include "EventMapYokingValidation.h" +#include "PlainTextStringBuilder.h" +#include "SceneClass.h" +#include "SceneClassArray.h" +#include "SceneClassAssistant.h" +#include "SceneObjectMapIntegerKey.h" + + +using namespace caret; + + + +/** + * \class caret::ChartTwoOverlaySet + * \brief A set of chart overlays. + * \ingroup Brain + */ + +/** + * Constructor. + * + * @param chartDataType + * Type of charts allowed in this overlay + * @param name + * Name of the overlay set. + * @param tabIndex + * Index of tab in which this overlay set is used. + */ +ChartTwoOverlaySet::ChartTwoOverlaySet(const ChartTwoDataTypeEnum::Enum chartDataType, + const AString& name, + const int32_t tabIndex) +: CaretObject(), +m_chartDataType(chartDataType), +m_name(name), +m_tabIndex(tabIndex) +{ + m_numberOfDisplayedOverlays = BrainConstants::MINIMUM_NUMBER_OF_OVERLAYS; + + m_chartAxisLeft = std::unique_ptr(new ChartTwoCartesianAxis(this, ChartAxisLocationEnum::CHART_AXIS_LOCATION_LEFT)); + m_chartAxisRight = std::unique_ptr(new ChartTwoCartesianAxis(this, ChartAxisLocationEnum::CHART_AXIS_LOCATION_RIGHT)); + m_chartAxisBottom = std::unique_ptr(new ChartTwoCartesianAxis(this, ChartAxisLocationEnum::CHART_AXIS_LOCATION_BOTTOM)); + + m_chartAxisLeft->setEnabledByChart(false); + m_chartAxisRight->setEnabledByChart(false); + m_chartAxisBottom->setEnabledByChart(false); + switch (m_chartDataType) { + case ChartTwoDataTypeEnum::CHART_DATA_TYPE_INVALID: + break; + case ChartTwoDataTypeEnum::CHART_DATA_TYPE_HISTOGRAM: + { + m_chartAxisLeft->setEnabledByChart(true); + m_chartAxisLeft->setUnits(CaretUnitsTypeEnum::NONE); + + m_chartAxisRight->setEnabledByChart(false); + m_chartAxisRight->setUnits(CaretUnitsTypeEnum::NONE); + + m_chartAxisBottom->setEnabledByChart(true); + m_chartAxisBottom->setUnits(CaretUnitsTypeEnum::NONE); + } + break; + case ChartTwoDataTypeEnum::CHART_DATA_TYPE_LINE_SERIES: + { + m_chartAxisLeft->setEnabledByChart(true); + m_chartAxisLeft->setUnits(CaretUnitsTypeEnum::NONE); + + m_chartAxisRight->setEnabledByChart(false); + m_chartAxisRight->setUnits(CaretUnitsTypeEnum::NONE); + + /* + * X-axis for line series shows full extent of data + */ + m_chartAxisBottom->setScaleRangeMode(ChartTwoAxisScaleRangeModeEnum::DATA); + m_chartAxisBottom->setEnabledByChart(true); + m_chartAxisBottom->setUnits(CaretUnitsTypeEnum::NONE); + } + break; + case ChartTwoDataTypeEnum::CHART_DATA_TYPE_MATRIX: + break; + } + + m_title = std::unique_ptr(new ChartTwoTitle()); + + m_sceneAssistant = new SceneClassAssistant(); + m_sceneAssistant->add("m_chartAxisLeft", + "ChartTwoCartesianAxis", + m_chartAxisLeft.get()); + m_sceneAssistant->add("m_chartAxisRight", + "ChartTwoCartesianAxis", + m_chartAxisRight.get()); + m_sceneAssistant->add("m_chartAxisBottom", + "ChartTwoCartesianAxis", + m_chartAxisBottom.get()); + + m_sceneAssistant->add("m_title", + "ChartTwoTitle", + m_title.get()); + m_sceneAssistant->add("m_numberOfDisplayedOverlays", + &m_numberOfDisplayedOverlays); + m_sceneAssistant->add("m_axisLineThickness", + &m_axisLineThickness); + + for (int i = 0; i < BrainConstants::MAXIMUM_NUMBER_OF_OVERLAYS; i++) { + m_overlays.push_back(std::make_shared(this, + m_chartDataType, + m_tabIndex, + i)); + CaretAssertVectorIndex(m_overlays, i); + m_overlays[i]->setWeakPointerToSelf(m_overlays[i]); + } + + EventManager::get()->addEventListener(this, EventTypeEnum::EVENT_ANNOTATION_CHART_LABEL_GET); + EventManager::get()->addEventListener(this, EventTypeEnum::EVENT_CHART_TWO_ATTRIBUTES_CHANGED); + EventManager::get()->addEventListener(this, EventTypeEnum::EVENT_CHART_TWO_AXIS_GET_DATA_RANGE); + EventManager::get()->addEventListener(this, EventTypeEnum::EVENT_MAP_YOKING_VALIDATION); + EventManager::get()->addEventListener(this, EventTypeEnum::EVENT_MAP_YOKING_SELECT_MAP); +} + +/** + * Destructor. + */ +ChartTwoOverlaySet::~ChartTwoOverlaySet() +{ + EventManager::get()->removeAllEventsFromListener(this); + m_overlays.clear(); + delete m_sceneAssistant; +} + +/** + * Copy the given overlay set to this overlay set. + * @param overlaySet + * Overlay set that is copied. + */ +void +ChartTwoOverlaySet::copyOverlaySet(const ChartTwoOverlaySet* overlaySet) +{ + for (int i = 0; i < BrainConstants::MAXIMUM_NUMBER_OF_OVERLAYS; i++) { + CaretAssertArrayIndex(m_overlays, BrainConstants::MAXIMUM_NUMBER_OF_OVERLAYS, i); + m_overlays[i]->copyData(overlaySet->getOverlay(i)); + } + *m_chartAxisLeft = *overlaySet->m_chartAxisLeft; + *m_chartAxisRight = *overlaySet->m_chartAxisRight; + *m_chartAxisBottom = *overlaySet->m_chartAxisBottom; + *m_title = *overlaySet->m_title; + m_axisLineThickness = overlaySet->m_axisLineThickness; + + m_numberOfDisplayedOverlays = overlaySet->m_numberOfDisplayedOverlays; +} + +/** + * Copy the cartesian axes from the given overlay set to this overlay set. + * + * @param overlaySet + * Overlay from which axes are copied. + */ +void +ChartTwoOverlaySet::copyCartesianAxes(const ChartTwoOverlaySet* overlaySet) +{ + CaretAssert(m_chartDataType == overlaySet->m_chartDataType); + + *m_chartAxisLeft = *overlaySet->m_chartAxisLeft; + *m_chartAxisRight = *overlaySet->m_chartAxisRight; + *m_chartAxisBottom = *overlaySet->m_chartAxisBottom; + m_axisLineThickness = overlaySet->m_axisLineThickness; +} + +/** + * @return Returns the top-most overlay regardless of its enabled status. + */ +ChartTwoOverlay* +ChartTwoOverlaySet::getPrimaryOverlay() +{ + return m_overlays[0].get(); +} + +/** + * @return Returns the top-most overlay regardless of its enabled status. + */ +const ChartTwoOverlay* +ChartTwoOverlaySet::getPrimaryOverlay() const +{ + return m_overlays[0].get(); +} + +/** + * Get the overlay at the specified index. If caller needs + * to store the pointer there is a risk that the overlay + * may be destroyed. Consider using getOverlayWeakPointer() + * instead. + * + * @param overlayNumber + * Index of the overlay. + * @return Overlay at the given index. + */ +const ChartTwoOverlay* +ChartTwoOverlaySet::getOverlay(const int32_t overlayNumber) const +{ + CaretAssertArrayIndex(m_overlays, + BrainConstants::MAXIMUM_NUMBER_OF_OVERLAYS, + overlayNumber); + return m_overlays[overlayNumber].get(); +} + +/** + * Get the overlay at the specified index. If caller needs + * to store the pointer there is a risk that the overlay + * may be destroyed. Consider using getOverlayWeakPointer() + * instead. + * + * @param overlayNumber + * Index of the overlay. + * @return Overlay at the given index. + */ +ChartTwoOverlay* +ChartTwoOverlaySet::getOverlay(const int32_t overlayNumber) +{ + CaretAssertArrayIndex(m_overlays, + BrainConstants::MAXIMUM_NUMBER_OF_OVERLAYS, + overlayNumber); + return m_overlays[overlayNumber].get(); +} + +/** + * Get the a "weak pointer" to the chart overlay at the given + * index. Caller can store this pointer and test to see + * if the chart overlay is still valid. + * + * @param overlayNumber + * Index of the overlay. + * @return Weak pointer to chart verlay at the given index. + */ +std::weak_ptr +ChartTwoOverlaySet::getOverlayWeakPointer(const int32_t overlayNumber) +{ + CaretAssertArrayIndex(m_overlays, + BrainConstants::MAXIMUM_NUMBER_OF_OVERLAYS, + overlayNumber); + return m_overlays[overlayNumber]; +} + +/** + * @return The displayed overlays which are the overlays + * shown in the Overlay Toolbox and this includes + * overlays that the user may have set to disabled. + */ +std::vector +ChartTwoOverlaySet::getDisplayedOverlays() const +{ + std::vector displayedOverlays; + + const int numOverlays = getNumberOfDisplayedOverlays(); + for (int32_t i = 0; i < numOverlays; i++) { + CaretAssertVectorIndex(m_overlays, i); + displayedOverlays.push_back(m_overlays[i].get()); + } + + return displayedOverlays; +} + +/** + * @return The displayed overlays but only those + * that are enabled by the user. + */ +std::vector +ChartTwoOverlaySet::getEnabledOverlays() const +{ + std::vector enabledOverlays; + + const int numOverlays = getNumberOfDisplayedOverlays(); + for (int32_t i = 0; i < numOverlays; i++) { + CaretAssertVectorIndex(m_overlays, i); + if (m_overlays[i]->isEnabled()) { + enabledOverlays.push_back(m_overlays[i].get()); + } + } + + return enabledOverlays; +} + +/** + * Get a text description of the window's content. + * + * @param descriptionOut + * Description of the window's content. + */ +void +ChartTwoOverlaySet::getDescriptionOfContent(PlainTextStringBuilder& descriptionOut) const +{ + descriptionOut.addLine("Overlay Set"); + descriptionOut.pushIndentation(); + descriptionOut.addLine(" Title: " + (m_title->isDisplayed() ? m_title->getText() : "Disabled by user")); + descriptionOut.popIndentation(); + + const int numOverlays = getNumberOfDisplayedOverlays(); + for (int32_t i = 0; i < numOverlays; i++) { + if (getOverlay(i)->isEnabled()) { + descriptionOut.pushIndentation(); + + descriptionOut.pushIndentation(); + getOverlay(i)->getDescriptionOfContent(descriptionOut); + descriptionOut.popIndentation(); + + descriptionOut.popIndentation(); + } + } +} + +/** + * Find the top-most displayed and enabled overlay containing the given data file. + * + * @param mapFile + * File for which overlay is requested. + */ +ChartTwoOverlay* +ChartTwoOverlaySet::getDisplayedOverlayContainingDataFile(const CaretMappableDataFile* mapFile) +{ + if (mapFile == NULL) { + return NULL; + } + + ChartTwoOverlay* overlayOut = NULL; + + const int numOverlays = getNumberOfDisplayedOverlays(); + for (int32_t i = 0; i < numOverlays; i++) { + ChartTwoOverlay* overlay = getOverlay(i); + if (overlay->isEnabled()) { + CaretMappableDataFile* overlayMapFile = NULL; + ChartTwoOverlay::SelectedIndexType selectionType = ChartTwoOverlay::SelectedIndexType::INVALID; + int32_t dataFileIndex = -1; + overlay->getSelectionData(overlayMapFile, + selectionType, + dataFileIndex); + if (mapFile == overlayMapFile) { + overlayOut = overlay; + } + } + } + + return overlayOut; +} + +/** + * Add a displayed overlay. If the maximum + * number of surface overlays is reached, + * this method has no effect. + */ +void +ChartTwoOverlaySet::addDisplayedOverlay() +{ + m_numberOfDisplayedOverlays++; + if (m_numberOfDisplayedOverlays > BrainConstants::MAXIMUM_NUMBER_OF_OVERLAYS) { + m_numberOfDisplayedOverlays = BrainConstants::MAXIMUM_NUMBER_OF_OVERLAYS; + } +} + +/** + * @return Returns the number of displayed overlays. + */ +int32_t +ChartTwoOverlaySet::getNumberOfDisplayedOverlays() const +{ + return m_numberOfDisplayedOverlays; +} + +/** + * Insert an overlay below this overlay + * @param overlayIndex + * Index of overlay for which an overlay is added below + */ +void +ChartTwoOverlaySet::insertOverlayAbove(const int32_t overlayIndex) +{ + if (m_numberOfDisplayedOverlays < BrainConstants::MAXIMUM_NUMBER_OF_OVERLAYS) { + m_numberOfDisplayedOverlays++; + + for (int32_t i = (m_numberOfDisplayedOverlays - 2); i >= overlayIndex; i--) { + moveDisplayedOverlayDown(i); + } + } +} + +/** + * Insert an overlay above this overlay + * @param overlayIndex + * Index of overlay for which an overlay is added above + */ +void +ChartTwoOverlaySet::insertOverlayBelow(const int32_t overlayIndex) +{ + if (m_numberOfDisplayedOverlays < BrainConstants::MAXIMUM_NUMBER_OF_OVERLAYS) { + m_numberOfDisplayedOverlays++; + + for (int32_t i = (m_numberOfDisplayedOverlays - 2); i > overlayIndex; i--) { + moveDisplayedOverlayDown(i); + } + } +} + + +/** + * Remove a displayed overlay. This method will have + * no effect if the minimum number of overlays are + * displayed + * + * @param overlayIndex + * Index of overlay for removal from display. + */ +void +ChartTwoOverlaySet::removeDisplayedOverlay(const int32_t overlayIndex) +{ + CaretAssertArrayIndex(m_overlays, + BrainConstants::MAXIMUM_NUMBER_OF_OVERLAYS, + overlayIndex); + m_overlays[overlayIndex]->setMapYokingGroup(MapYokingGroupEnum::MAP_YOKING_GROUP_OFF); + + if (m_numberOfDisplayedOverlays > BrainConstants::MINIMUM_NUMBER_OF_OVERLAYS) { + m_numberOfDisplayedOverlays--; + for (int32_t i = overlayIndex; i < m_numberOfDisplayedOverlays; i++) { + CaretAssertArrayIndex(m_overlays, BrainConstants::MAXIMUM_NUMBER_OF_OVERLAYS, i+1); + m_overlays[i]->copyData(m_overlays[i+1].get()); + } + } +} + +/** + * Move the overlay at the given index up one level + * (swap it with overlayIndex - 1). This method will + * have no effect if the overlay is the top-most overlay. + * + * @param overlayIndex + * Index of overlay that is to be moved up. + */ +void +ChartTwoOverlaySet::moveDisplayedOverlayUp(const int32_t overlayIndex) +{ + if (overlayIndex > 0) { + CaretAssertArrayIndex(m_overlays, BrainConstants::MAXIMUM_NUMBER_OF_OVERLAYS, overlayIndex); + m_overlays[overlayIndex]->swapData(m_overlays[overlayIndex - 1].get()); + } +} + +/** + * Move the overlay at the given index down one level + * (swap it with overlayIndex + 1). This method will + * have no effect if the overlay is the bottom-most overlay. + * + * @param overlayIndex + * Index of overlay that is to be moved down. + */ +void +ChartTwoOverlaySet::moveDisplayedOverlayDown(const int32_t overlayIndex) +{ + const int32_t nextOverlayIndex = overlayIndex + 1; + if (nextOverlayIndex < m_numberOfDisplayedOverlays) { + CaretAssertArrayIndex(m_overlays, BrainConstants::MAXIMUM_NUMBER_OF_OVERLAYS, nextOverlayIndex); + m_overlays[overlayIndex]->swapData(m_overlays[nextOverlayIndex].get()); + } +} + +/** + * Initialize the overlays. + */ +void +ChartTwoOverlaySet::initializeOverlays() +{ + /* + * This method could be used to choose specific file types + * for the default overlays similar to that in OverlaySet.cxx. + */ +} + +/** + * Called by first overlay when the first overlay's selection changes. + * All other overlays are set to use the same chart compound data type + * so that the charts in the tab are compatible + */ +void +ChartTwoOverlaySet::firstOverlaySelectionChanged() +{ + if (m_inFirstOverlayChangedMethodFlag) { + return; + } + + m_inFirstOverlayChangedMethodFlag = true; + + ChartTwoCompoundDataType cdt = m_overlays[0]->getChartTwoCompoundDataType(); + + for (int32_t i = 1; i < BrainConstants::MAXIMUM_NUMBER_OF_OVERLAYS; i++) { + CaretAssertArrayIndex(m_overlays, BrainConstants::MAXIMUM_NUMBER_OF_OVERLAYS, i); + m_overlays[i]->setChartTwoCompoundDataType(cdt); + } + + PlainTextStringBuilder description; + getDescriptionOfContent(description); + + m_inFirstOverlayChangedMethodFlag = false; +} + + +/** + * Reset the yoking status of all overlays to off. + */ +void +ChartTwoOverlaySet::resetOverlayYokingToOff() +{ + for (int i = 0; i < BrainConstants::MAXIMUM_NUMBER_OF_OVERLAYS; i++) { + CaretAssertArrayIndex(m_overlays, BrainConstants::MAXIMUM_NUMBER_OF_OVERLAYS, i); + m_overlays[i]->setMapYokingGroup(MapYokingGroupEnum::MAP_YOKING_GROUP_OFF); + } +} + +/** + * Get a description of this object's content. + * @return String describing this object's content. + */ +AString +ChartTwoOverlaySet::toString() const +{ + return "ChartTwoOverlaySet"; +} + +/** + * Receive an event. + * + * @param event + * An event for which this instance is listening. + */ +void +ChartTwoOverlaySet::receiveEvent(Event* event) +{ + if (event->getEventType() == EventTypeEnum::EVENT_CHART_TWO_ATTRIBUTES_CHANGED) { + EventChartTwoAttributesChanged* attributeEvent = dynamic_cast(event); + CaretAssert(attributeEvent); + + switch (attributeEvent->getMode()) { + case EventChartTwoAttributesChanged::Mode::INVALID: + break; + case EventChartTwoAttributesChanged::Mode::CARTESIAN_AXIS: + { + YokingGroupEnum::Enum yokingGroup = YokingGroupEnum::YOKING_GROUP_OFF; + ChartTwoDataTypeEnum::Enum chartTwoDataType = ChartTwoDataTypeEnum::CHART_DATA_TYPE_INVALID; + ChartTwoCartesianAxis* cartesianAxis = NULL; + attributeEvent->getCartesianAxisChanged(yokingGroup, + chartTwoDataType, + cartesianAxis); + + /* + * Only tabs in the windows are valid + */ + EventBrowserTabGet tabEvent(m_tabIndex); + EventManager::get()->sendEvent(tabEvent.getPointer()); + const BrowserTabContent* btc = tabEvent.getBrowserTab(); + if (btc != NULL) { + const YokingGroupEnum::Enum tabYoking = btc->getChartModelYokingGroup(); + + if ((yokingGroup != YokingGroupEnum::YOKING_GROUP_OFF) + && (yokingGroup == tabYoking) + && (m_chartDataType == chartTwoDataType)) { + switch (cartesianAxis->getAxisLocation()) { + case ChartAxisLocationEnum::CHART_AXIS_LOCATION_BOTTOM: + *m_chartAxisBottom = *cartesianAxis; + break; + case ChartAxisLocationEnum::CHART_AXIS_LOCATION_LEFT: + *m_chartAxisLeft = *cartesianAxis; + break; + case ChartAxisLocationEnum::CHART_AXIS_LOCATION_RIGHT: + *m_chartAxisRight = *cartesianAxis; + break; + case ChartAxisLocationEnum::CHART_AXIS_LOCATION_TOP: + CaretAssert(0); + break; + } + } + } + } + break; + case EventChartTwoAttributesChanged::Mode::LINE_THICKESS: + { + YokingGroupEnum::Enum yokingGroup = YokingGroupEnum::YOKING_GROUP_OFF; + ChartTwoDataTypeEnum::Enum chartTwoDataType = ChartTwoDataTypeEnum::CHART_DATA_TYPE_INVALID; + float lineThickness = 0.0f; + attributeEvent->getLineThicknessChanged(yokingGroup, + chartTwoDataType, + lineThickness); + + /* + * Only tabs in the windows are valid + */ + EventBrowserTabGet tabEvent(m_tabIndex); + EventManager::get()->sendEvent(tabEvent.getPointer()); + const BrowserTabContent* btc = tabEvent.getBrowserTab(); + if (btc != NULL) { + const YokingGroupEnum::Enum tabYoking = btc->getChartModelYokingGroup(); + + if ((yokingGroup != YokingGroupEnum::YOKING_GROUP_OFF) + && (yokingGroup == tabYoking) + && (m_chartDataType == chartTwoDataType)) { + m_axisLineThickness = lineThickness; + } + } + } + break; + case EventChartTwoAttributesChanged::Mode::TITLE: + { + YokingGroupEnum::Enum yokingGroup = YokingGroupEnum::YOKING_GROUP_OFF; + ChartTwoDataTypeEnum::Enum chartTwoDataType = ChartTwoDataTypeEnum::CHART_DATA_TYPE_INVALID; + ChartTwoTitle* chartTitle = NULL; + attributeEvent->getTitleChanged(yokingGroup, + chartTwoDataType, + chartTitle); + + /* + * Only tabs in the windows are valid + */ + EventBrowserTabGet tabEvent(m_tabIndex); + EventManager::get()->sendEvent(tabEvent.getPointer()); + const BrowserTabContent* btc = tabEvent.getBrowserTab(); + if (btc != NULL) { + const YokingGroupEnum::Enum tabYoking = btc->getChartModelYokingGroup(); + + if ((yokingGroup != YokingGroupEnum::YOKING_GROUP_OFF) + && (yokingGroup == tabYoking) + && (m_chartDataType == chartTwoDataType)) { + *m_title = *chartTitle; + } + } + } + break; + } + + attributeEvent->setEventProcessed(); + } + else if (event->getEventType() == EventTypeEnum::EVENT_CHART_TWO_AXIS_GET_DATA_RANGE) { + EventChartTwoAxisGetDataRange* rangeEvent = dynamic_cast(event); + CaretAssert(rangeEvent); + + if (rangeEvent->getChartOverlaySet() == this) { + float minimumValue = 0.0f; + float maximumValue = 0.0f; + if (getDataRangeForAxis(rangeEvent->getChartAxisLocation(), + minimumValue, + maximumValue)) { + rangeEvent->setMinimumAndMaximumValues(minimumValue, + maximumValue); + rangeEvent->setEventProcessed(); + } + } + } + else if (event->getEventType() == EventTypeEnum::EVENT_MAP_YOKING_VALIDATION) { + /* + * The events intended for overlays are received here so that + * only DISPLAYED overlays are updated. + */ + EventMapYokingValidation* mapYokeEvent = dynamic_cast(event); + CaretAssert(mapYokeEvent); + + const MapYokingGroupEnum::Enum requestedYokingGroup = mapYokeEvent->getMapYokingGroup(); + if (requestedYokingGroup != MapYokingGroupEnum::MAP_YOKING_GROUP_OFF) { + + /* + * Find all overlays with the requested yoking + */ + const int32_t overlayCount = getNumberOfDisplayedOverlays(); + for (int32_t j = 0; j < overlayCount; j++) { + ChartTwoOverlay* overlay = getOverlay(j); + if (overlay->isMapYokingSupported()) { + CaretMappableDataFile* mapFile = NULL; + ChartTwoOverlay::SelectedIndexType indexType = ChartTwoOverlay::SelectedIndexType::INVALID; + int32_t mapIndex = -1; + overlay->getSelectionData(mapFile, + indexType, + mapIndex); + if (mapFile != NULL) { + if (overlay->isMapYokingSupported()) { + mapYokeEvent->addMapYokedFile(mapFile, overlay->getMapYokingGroup(), m_tabIndex); + } + } + } + } + } + + mapYokeEvent->setEventProcessed(); + } + else if (event->getEventType() == EventTypeEnum::EVENT_MAP_YOKING_SELECT_MAP) { + /* + * The events intended for overlays are received here so that + * only DISPLAYED overlays are updated. + */ + EventMapYokingSelectMap* selectMapEvent = dynamic_cast(event); + CaretAssert(selectMapEvent); + const MapYokingGroupEnum::Enum eventYokingGroup = selectMapEvent->getMapYokingGroup(); + if (eventYokingGroup != MapYokingGroupEnum::MAP_YOKING_GROUP_OFF) { + const int32_t yokingGroupMapIndex = MapYokingGroupEnum::getSelectedMapIndex(eventYokingGroup); + const bool yokingGroupSelectedStatus = MapYokingGroupEnum::isEnabled(eventYokingGroup); + const CaretMappableDataFile* eventMapFile = selectMapEvent->getCaretMappableDataFile(); + + /* + * Find all overlays with the requested yoking + */ + const int32_t overlayCount = getNumberOfDisplayedOverlays(); + for (int32_t j = 0; j < overlayCount; j++) { + ChartTwoOverlay* overlay = getOverlay(j); + if (overlay->isMapYokingSupported()) { + if (overlay->getMapYokingGroup() == selectMapEvent->getMapYokingGroup()) { + CaretMappableDataFile* mapFile = NULL; + ChartTwoOverlay::SelectedIndexType indexType = ChartTwoOverlay::SelectedIndexType::INVALID; + int32_t mapIndex = -1; + overlay->getSelectionData(mapFile, + indexType, + mapIndex); + + if (mapFile != NULL) { + if (overlay->isMapYokingSupported()) { + + switch (m_chartDataType) { + case ChartTwoDataTypeEnum::CHART_DATA_TYPE_INVALID: + break; + case ChartTwoDataTypeEnum::CHART_DATA_TYPE_HISTOGRAM: + if (yokingGroupMapIndex < mapFile->getNumberOfMaps()) { + overlay->setSelectionData(mapFile, + yokingGroupMapIndex); + } + break; + case ChartTwoDataTypeEnum::CHART_DATA_TYPE_LINE_SERIES: + break; + case ChartTwoDataTypeEnum::CHART_DATA_TYPE_MATRIX: + break; + } + + if (mapFile == eventMapFile) { + overlay->setEnabled(yokingGroupSelectedStatus); + } + } + } + } + } + } + + selectMapEvent->setEventProcessed(); + } + } + else if (event->getEventType() == EventTypeEnum::EVENT_ANNOTATION_CHART_LABEL_GET) { + /* + * The events intended for overlays are received here so that + * only DISPLAYED overlays are updated. + */ + //EventAnnotationChartLabelGet* chartLabelEvent = dynamic_cast(event); + //CaretAssert(chartLabelEvent); + + //chartLabelEvent->addAnnotationChartLabel(m_chartTitle.get()); + } +} + +/** + * Get the minimum and maximum values for the given chart axis + * from the ENABLED overlays in this chart overlay set. + * + * @param chartAxisLocation + * Location of axis. + * @param minimumValueOut + * Output with minimum value for axis. + * @param maximumValueOut + * Output with maximum value for axis. + * @return + * True if output values are valid, else false. + */ +bool +ChartTwoOverlaySet::getDataRangeForAxis(const ChartAxisLocationEnum::Enum chartAxisLocation, + float& minimumValueOut, + float& maximumValueOut) const +{ + minimumValueOut = 0.0f; + maximumValueOut = 0.0f; + if ( ! isAxesSupportedByChartDataType()) { + return false; + } + + minimumValueOut = std::numeric_limits::max(); + maximumValueOut = -std::numeric_limits::max(); + + std::vector enabledOverlays = getEnabledOverlays(); + for (auto overlay : enabledOverlays) { + if (overlay->isEnabled()) { + CaretMappableDataFile* mapFile = NULL; + ChartTwoOverlay::SelectedIndexType selectedIndexType = ChartTwoOverlay::SelectedIndexType::INVALID; + int32_t selectedIndex = -1; + overlay->getSelectionData(mapFile, + selectedIndexType, + selectedIndex); + if (mapFile != NULL) { + BoundingBox boundingBox; + if (overlay->getBounds(boundingBox)) { + switch (chartAxisLocation) { + case ChartAxisLocationEnum::CHART_AXIS_LOCATION_BOTTOM: + minimumValueOut = std::min(minimumValueOut, boundingBox.getMinX()); + maximumValueOut = std::max(maximumValueOut, boundingBox.getMaxX()); + break; + case ChartAxisLocationEnum::CHART_AXIS_LOCATION_LEFT: + if (overlay->getCartesianVerticalAxisLocation() == ChartAxisLocationEnum::CHART_AXIS_LOCATION_LEFT) { + minimumValueOut = std::min(minimumValueOut, boundingBox.getMinY()); + maximumValueOut = std::max(maximumValueOut, boundingBox.getMaxY()); + } + break; + case ChartAxisLocationEnum::CHART_AXIS_LOCATION_RIGHT: + if (overlay->getCartesianVerticalAxisLocation() == ChartAxisLocationEnum::CHART_AXIS_LOCATION_RIGHT) { + minimumValueOut = std::min(minimumValueOut, boundingBox.getMinY()); + maximumValueOut = std::max(maximumValueOut, boundingBox.getMaxY()); + } + break; + case ChartAxisLocationEnum::CHART_AXIS_LOCATION_TOP: + minimumValueOut = std::min(minimumValueOut, boundingBox.getMinX()); + maximumValueOut = std::max(maximumValueOut, boundingBox.getMaxX()); + break; + } + } + } + } + } + + if (minimumValueOut < maximumValueOut) { + return true; + } + + minimumValueOut = 0.0f; + maximumValueOut = 0.0f; + + return false; +} + +/** + * @return Are axes supported by the chart data type for this overlay set? + */ +bool +ChartTwoOverlaySet::isAxesSupportedByChartDataType() const +{ + bool axisSupportedFlag = false; + switch (m_chartDataType) { + case ChartTwoDataTypeEnum::CHART_DATA_TYPE_INVALID: + break; + case ChartTwoDataTypeEnum::CHART_DATA_TYPE_HISTOGRAM: + axisSupportedFlag = true; + break; + case ChartTwoDataTypeEnum::CHART_DATA_TYPE_LINE_SERIES: + axisSupportedFlag = true; + break; + case ChartTwoDataTypeEnum::CHART_DATA_TYPE_MATRIX: + break; + } + + return axisSupportedFlag; +} + + +/** + * Get the displayed chart axes. + * + * @param axesOut + * Output containing the displayed axes. + */ +void +ChartTwoOverlaySet::getDisplayedChartAxes(std::vector& axesOut) const +{ + axesOut.clear(); + + AString lineSeriesDataTypeName; + + + float xMin = 0.0f; + float xMax = 0.0f; + float yMinLeft = 0.0f; + float yMaxLeft = 0.0f; + float yMinRight = 0.0f; + float yMaxRight = 0.0f; + + bool showBottomFlag = getDataRangeForAxis(ChartAxisLocationEnum::CHART_AXIS_LOCATION_BOTTOM, + xMin, xMax); + bool showLeftFlag = getDataRangeForAxis(ChartAxisLocationEnum::CHART_AXIS_LOCATION_LEFT, + yMinLeft, yMaxLeft); + bool showRightFlag = getDataRangeForAxis(ChartAxisLocationEnum::CHART_AXIS_LOCATION_RIGHT, + yMinRight, yMaxRight); + + m_chartAxisBottom->setEnabledByChart(showBottomFlag); + m_chartAxisLeft->setEnabledByChart(showLeftFlag); + m_chartAxisRight->setEnabledByChart(showRightFlag); + + if (m_chartAxisBottom->isEnabledByChart()) { + axesOut.push_back(m_chartAxisBottom.get()); + } + if (m_chartAxisLeft->isEnabledByChart()) { + axesOut.push_back(m_chartAxisLeft.get()); + } + if (m_chartAxisRight->isEnabledByChart()) { + axesOut.push_back(m_chartAxisRight.get()); + } +} + +/** + * Get the text for the axis label. + * + * @param axis + * The cartesian axis. + * @return + * Text for the axis label. + */ +AString +ChartTwoOverlaySet::getAxisLabel(const ChartTwoCartesianAxis* axis) const +{ + if (axis == NULL) { + return ""; + } + + AString label; + + const int32_t overlayTitleIndex = axis->getLabelOverlayIndex(m_numberOfDisplayedOverlays); + const ChartTwoOverlay* chartOverlay = getOverlay(overlayTitleIndex); + CaretMappableDataFile* mapFile = chartOverlay->getSelectedMapFile(); + if (mapFile != NULL) { + switch (m_chartDataType) { + case ChartTwoDataTypeEnum::CHART_DATA_TYPE_INVALID: + break; + case ChartTwoDataTypeEnum::CHART_DATA_TYPE_HISTOGRAM: + switch (axis->getAxisLocation()) { + case ChartAxisLocationEnum::CHART_AXIS_LOCATION_BOTTOM: + label = mapFile->getChartingDelegate()->getHistogramCharting()->getBottomAxisTitle(); + break; + case ChartAxisLocationEnum::CHART_AXIS_LOCATION_LEFT: + label = mapFile->getChartingDelegate()->getHistogramCharting()->getLeftRightAxisTitle(); + break; + case ChartAxisLocationEnum::CHART_AXIS_LOCATION_RIGHT: + label = mapFile->getChartingDelegate()->getHistogramCharting()->getLeftRightAxisTitle(); + break; + case ChartAxisLocationEnum::CHART_AXIS_LOCATION_TOP: + CaretAssert(0); + break; + } + break; + case ChartTwoDataTypeEnum::CHART_DATA_TYPE_LINE_SERIES: + switch (axis->getAxisLocation()) { + case ChartAxisLocationEnum::CHART_AXIS_LOCATION_BOTTOM: + label = mapFile->getChartingDelegate()->getLineSeriesCharting()->getBottomAxisTitle(); + break; + case ChartAxisLocationEnum::CHART_AXIS_LOCATION_LEFT: + label = mapFile->getChartingDelegate()->getLineSeriesCharting()->getLeftRightAxisTitle(); + break; + case ChartAxisLocationEnum::CHART_AXIS_LOCATION_RIGHT: + label = mapFile->getChartingDelegate()->getLineSeriesCharting()->getLeftRightAxisTitle(); + break; + case ChartAxisLocationEnum::CHART_AXIS_LOCATION_TOP: + CaretAssert(0); + break; + } + break; + case ChartTwoDataTypeEnum::CHART_DATA_TYPE_MATRIX: + break; + } + } + + return label; +} + +/** + * Set the text for the axis label. + * + * @param axis + * The cartesian axis. + * @param label + * New text for the axis label. + */ +void +ChartTwoOverlaySet::setAxisLabel(const ChartTwoCartesianAxis* axis, + const AString& label) +{ + const int32_t overlayTitleIndex = axis->getLabelOverlayIndex(m_numberOfDisplayedOverlays); + const ChartTwoOverlay* chartOverlay = getOverlay(overlayTitleIndex); + CaretMappableDataFile* mapFile = chartOverlay->getSelectedMapFile(); + if (mapFile != NULL) { + switch (m_chartDataType) { + case ChartTwoDataTypeEnum::CHART_DATA_TYPE_INVALID: + break; + case ChartTwoDataTypeEnum::CHART_DATA_TYPE_HISTOGRAM: + switch (axis->getAxisLocation()) { + case ChartAxisLocationEnum::CHART_AXIS_LOCATION_BOTTOM: + mapFile->getChartingDelegate()->getHistogramCharting()->setBottomAxisTitle(label); + break; + case ChartAxisLocationEnum::CHART_AXIS_LOCATION_LEFT: + mapFile->getChartingDelegate()->getHistogramCharting()->setLeftRightAxisTitle(label); + break; + case ChartAxisLocationEnum::CHART_AXIS_LOCATION_RIGHT: + mapFile->getChartingDelegate()->getHistogramCharting()->setLeftRightAxisTitle(label); + break; + case ChartAxisLocationEnum::CHART_AXIS_LOCATION_TOP: + CaretAssert(0); + break; + } + break; + case ChartTwoDataTypeEnum::CHART_DATA_TYPE_LINE_SERIES: + switch (axis->getAxisLocation()) { + case ChartAxisLocationEnum::CHART_AXIS_LOCATION_BOTTOM: + mapFile->getChartingDelegate()->getLineSeriesCharting()->setBottomAxisTitle(label); + break; + case ChartAxisLocationEnum::CHART_AXIS_LOCATION_LEFT: + mapFile->getChartingDelegate()->getLineSeriesCharting()->setLeftRightAxisTitle(label); + break; + case ChartAxisLocationEnum::CHART_AXIS_LOCATION_RIGHT: + mapFile->getChartingDelegate()->getLineSeriesCharting()->setLeftRightAxisTitle(label); + break; + case ChartAxisLocationEnum::CHART_AXIS_LOCATION_TOP: + CaretAssert(0); + break; + } + break; + case ChartTwoDataTypeEnum::CHART_DATA_TYPE_MATRIX: + break; + } + } +} + + +/** + * @return The chart title. + */ +ChartTwoTitle* +ChartTwoOverlaySet::getChartTitle() +{ + return m_title.get(); +} + +/** + * @return The chart title. + */ +const ChartTwoTitle* +ChartTwoOverlaySet::getChartTitle() const +{ + return m_title.get(); +} + +/** + * @return Thickness of box around chart and tick marks on axes + */ +float +ChartTwoOverlaySet::getAxisLineThickness() const +{ + return m_axisLineThickness; +} + +/** + * Set Thickness of box around chart and tick marks on axes + * + * @param axisLineThickness + * New value for Thickness of box around chart and tick marks on axes + */ +void +ChartTwoOverlaySet::setAxisLineThickness(const float axisLineThickness) +{ + m_axisLineThickness = axisLineThickness; +} + +/** + * Save information specific to this type of model to the scene. + * + * @param sceneAttributes + * Attributes for the scene. Scenes may be of different types + * (full, generic, etc) and the attributes should be checked when + * saving the scene. + * + * @param instanceName + * Name of instance in the scene. + */ +SceneClass* +ChartTwoOverlaySet::saveToScene(const SceneAttributes* sceneAttributes, + const AString& instanceName) +{ + SceneClass* sceneClass = new SceneClass(instanceName, + "ChartTwoOverlaySet", + 1); + m_sceneAssistant->saveMembers(sceneAttributes, + sceneClass); + + const int32_t numOverlaysToSave = getNumberOfDisplayedOverlays(); + + std::vector overlayClassVector; + for (int i = 0; i < numOverlaysToSave; i++) { + overlayClassVector.push_back(m_overlays[i]->saveToScene(sceneAttributes, "m_overlays")); + } + + SceneClassArray* overlayClassArray = new SceneClassArray("m_overlays", + overlayClassVector); + sceneClass->addChild(overlayClassArray); + + // Uncomment if sub-classes must save to scene + //saveSubClassDataToScene(sceneAttributes, + // sceneClass); + + return sceneClass; +} + +/** + * Restore information specific to the type of model from the scene. + * + * @param sceneAttributes + * Attributes for the scene. Scenes may be of different types + * (full, generic, etc) and the attributes should be checked when + * restoring the scene. + * + * @param sceneClass + * sceneClass from which model specific information is obtained. + */ +void +ChartTwoOverlaySet::restoreFromScene(const SceneAttributes* sceneAttributes, + const SceneClass* sceneClass) +{ + if (sceneClass == NULL) { + return; + } + + m_title->reset(); + + m_sceneAssistant->restoreMembers(sceneAttributes, + sceneClass); + + /* + * Title was originally saved as text and boolean + */ + const SceneClass* oldChartTitleClass = sceneClass->getClass("m_chartTitle"); + if (oldChartTitleClass != NULL) { + if (oldChartTitleClass->getClassName() == "AnnotationPercentSizeText") { + std::unique_ptr title = std::unique_ptr(new AnnotationPercentSizeText(AnnotationAttributesDefaultTypeEnum::NORMAL, + AnnotationTextFontSizeTypeEnum::PERCENTAGE_OF_VIEWPORT_HEIGHT)); + title->setText(""); + title->restoreFromScene(sceneAttributes, oldChartTitleClass); + + m_title->setText(title->getText()); + m_title->setDisplayed(sceneClass->getBooleanValue("m_chartTitleDisplayedFlag", + false)); + } + } + + const SceneClassArray* overlayClassArray = sceneClass->getClassArray("m_overlays"); + if (overlayClassArray != NULL) { + const int32_t numOverlays = std::min(overlayClassArray->getNumberOfArrayElements(), + (int32_t)BrainConstants::MAXIMUM_NUMBER_OF_OVERLAYS); + for (int32_t i = 0; i < numOverlays; i++) { + CaretAssertArrayIndex(m_overlays, BrainConstants::MAXIMUM_NUMBER_OF_OVERLAYS, i); + m_overlays[i]->restoreFromScene(sceneAttributes, + overlayClassArray->getClassAtIndex(i)); + } + } + //Uncomment if sub-classes must restore from scene + //restoreSubClassDataFromScene(sceneAttributes, + // sceneClass); + +} + diff --git a/src/Brain/ChartTwoOverlaySet.h b/src/Brain/ChartTwoOverlaySet.h new file mode 100644 index 0000000000000000000000000000000000000000..c4e8dfec983e62681c72b5b4e6bb4eef93bd63e8 --- /dev/null +++ b/src/Brain/ChartTwoOverlaySet.h @@ -0,0 +1,182 @@ +#ifndef __CHART_TWO_OVERLAY_SET_H__ +#define __CHART_TWO_OVERLAY_SET_H__ + +/*LICENSE_START*/ +/* + * Copyright (C) 2016 Washington University School of Medicine + * + * 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 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ +/*LICENSE_END*/ +#include + +#include "BrainConstants.h" +#include "CaretObject.h" +#include "ChartAxisLocationEnum.h" +#include "ChartTwoDataTypeEnum.h" +#include "EventListenerInterface.h" +#include "SceneableInterface.h" + + +namespace caret { + class AnnotationPercentSizeText; + class CaretMappableDataFile; + class ChartTwoCartesianAxis; + class ChartTwoOverlay; + class ChartTwoTitle; + class PlainTextStringBuilder; + class SceneClassAssistant; + + class ChartTwoOverlaySet : public CaretObject, public EventListenerInterface, public SceneableInterface { + + public: + ChartTwoOverlaySet(const ChartTwoDataTypeEnum::Enum chartDataType, + const AString& name, + const int32_t tabIndex); + + virtual ~ChartTwoOverlaySet(); + + void copyOverlaySet(const ChartTwoOverlaySet* overlaySet); + + void copyCartesianAxes(const ChartTwoOverlaySet* overlaySet); + + ChartTwoOverlay* getPrimaryOverlay(); + + const ChartTwoOverlay* getPrimaryOverlay() const; + + ChartTwoOverlay* getOverlay(const int32_t overlayNumber); + + const ChartTwoOverlay* getOverlay(const int32_t overlayNumber) const; + + std::weak_ptr getOverlayWeakPointer(const int32_t overlayNumber); + + std::vector getDisplayedOverlays() const; + + std::vector getEnabledOverlays() const; + + void addDisplayedOverlay(); + + int32_t getNumberOfDisplayedOverlays() const; + + ChartTwoOverlay* getDisplayedOverlayContainingDataFile(const CaretMappableDataFile* mapFile); + + void getDisplayedChartAxes(std::vector& axesOut) const; + + AString getAxisLabel(const ChartTwoCartesianAxis* axis) const; + + void setAxisLabel(const ChartTwoCartesianAxis* axis, + const AString& label); + + bool isAxesSupportedByChartDataType() const; + + bool getDataRangeForAxis(const ChartAxisLocationEnum::Enum chartAxisLocation, + float& minimumValueOut, + float& maximumValueOut) const; + + ChartTwoTitle* getChartTitle(); + + const ChartTwoTitle* getChartTitle() const; + + void insertOverlayAbove(const int32_t overlayIndex); + + void insertOverlayBelow(const int32_t overlayIndex); + + void removeDisplayedOverlay(const int32_t overlayIndex); + + void moveDisplayedOverlayUp(const int32_t overlayIndex); + + void moveDisplayedOverlayDown(const int32_t overlayIndex); + + void initializeOverlays(); + + void resetOverlayYokingToOff(); + + float getAxisLineThickness() const; + + void setAxisLineThickness(const float axisLineThickness); + + // ADD_NEW_METHODS_HERE + + virtual AString toString() const; + + virtual void getDescriptionOfContent(PlainTextStringBuilder& descriptionOut) const; + + virtual void receiveEvent(Event* event); + + virtual SceneClass* saveToScene(const SceneAttributes* sceneAttributes, + const AString& instanceName); + + virtual void restoreFromScene(const SceneAttributes* sceneAttributes, + const SceneClass* sceneClass); + + + + + + +// If there will be sub-classes of this class that need to save +// and restore data from scenes, these pure virtual methods can +// be uncommented to force their implementation by sub-classes. +// protected: +// virtual void saveSubClassDataToScene(const SceneAttributes* sceneAttributes, +// SceneClass* sceneClass) = 0; +// +// virtual void restoreSubClassDataFromScene(const SceneAttributes* sceneAttributes, +// const SceneClass* sceneClass) = 0; + + private: + ChartTwoOverlaySet(const ChartTwoOverlaySet&); + + ChartTwoOverlaySet& operator=(const ChartTwoOverlaySet&); + + void firstOverlaySelectionChanged(); + + SceneClassAssistant* m_sceneAssistant; + + std::vector> m_overlays; + + const ChartTwoDataTypeEnum::Enum m_chartDataType; + + std::unique_ptr m_chartAxisLeft; + + std::unique_ptr m_chartAxisRight; + + std::unique_ptr m_chartAxisBottom; + + const AString m_name; + + const int32_t m_tabIndex; + + std::unique_ptr m_title; + + int32_t m_numberOfDisplayedOverlays; + + bool m_inFirstOverlayChangedMethodFlag = false; + + /** Thickness of box around chart and tick marks on axes*/ + float m_axisLineThickness = 0.5; + + // ADD_NEW_MEMBERS_HERE + + friend class ChartTwoOverlay; + + }; + +#ifdef __CHART_TWO_OVERLAY_SET_DECLARE__ + // +#endif // __CHART_TWO_OVERLAY_SET_DECLARE__ + +} // namespace +#endif //__CHART_TWO_OVERLAY_SET_H__ diff --git a/src/Brain/ChartTwoOverlaySetArray.cxx b/src/Brain/ChartTwoOverlaySetArray.cxx new file mode 100644 index 0000000000000000000000000000000000000000..5d290159dc876090b001c64dd228cb8bb0c7d5d5 --- /dev/null +++ b/src/Brain/ChartTwoOverlaySetArray.cxx @@ -0,0 +1,302 @@ + +/*LICENSE_START*/ +/* + * Copyright (C) 2014 Washington University School of Medicine + * + * 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 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ +/*LICENSE_END*/ + +#define __CHART_TWO_OVERLAY_SET_ARRAY_DECLARE__ +#include "ChartTwoOverlaySetArray.h" +#undef __CHART_TWO_OVERLAY_SET_ARRAY_DECLARE__ + +#include "BrainConstants.h" +#include "CaretAssert.h" +#include "ChartTwoOverlaySet.h" +#include "EventBrowserTabDelete.h" +#include "EventManager.h" +#include "SceneAttributes.h" +#include "SceneClass.h" +#include "SceneClassArray.h" +using namespace caret; + + + +/** + * \class caret::ChartTwoOverlaySetArray + * \brief Maintains an array of chart overlay sets for use with a model + * \ingroup Brain + */ + +/** + * Constructor. + * + * @param chartDataType + * Type of charts allowed in this overlay + * @param name + * Name of model using this chart overlay set. This name is displayed + * if there is an attempt to yoke models with incompatible chart overlays. + */ +ChartTwoOverlaySetArray::ChartTwoOverlaySetArray(const ChartTwoDataTypeEnum::Enum chartDataType, + const AString& name) +: CaretObject(), +EventListenerInterface(), +m_name(name) +{ + m_chartOverlaySets.resize(BrainConstants::MAXIMUM_NUMBER_OF_BROWSER_TABS); + for (int32_t tabIndex = 0; tabIndex < BrainConstants::MAXIMUM_NUMBER_OF_BROWSER_TABS; tabIndex++) { + m_chartOverlaySets[tabIndex] = new ChartTwoOverlaySet(chartDataType, + name, + tabIndex); + } + + EventManager::get()->addEventListener(this, EventTypeEnum::EVENT_BROWSER_TAB_DELETE); +} + +/** + * Destructor. + */ +ChartTwoOverlaySetArray::~ChartTwoOverlaySetArray() +{ + EventManager::get()->removeAllEventsFromListener(this); + + for (std::vector::iterator iter =m_chartOverlaySets.begin(); + iter !=m_chartOverlaySets.end(); + iter++) { + delete *iter; + } + m_chartOverlaySets.clear(); +} + +/** + * Get a description of this object's content. + * @return String describing this object's content. + */ +AString +ChartTwoOverlaySetArray::toString() const +{ + return "ChartTwoOverlaySetArray"; +} + +/** + * @return The number of chart overlay sets. + */ +int32_t +ChartTwoOverlaySetArray::getNumberOfChartOverlaySets() +{ + return m_chartOverlaySets.size(); +} + +/** + * Get the chart overlay set at the given index. + * + * @param tabIndex + * Index of chart overlay tab. + * @return + * Overlay set at given index. + */ +ChartTwoOverlaySet* +ChartTwoOverlaySetArray::getChartTwoOverlaySet(const int32_t tabIndex) +{ + CaretAssertVectorIndex(m_chartOverlaySets, tabIndex); + + return m_chartOverlaySets[tabIndex]; +} + +/** + * Initialize the chart overlay selections. + */ +void +ChartTwoOverlaySetArray::initializeOverlaySelections() +{ + for (int32_t iTab = 0; iTab < BrainConstants::MAXIMUM_NUMBER_OF_BROWSER_TABS; iTab++) { + CaretAssertVectorIndex(m_chartOverlaySets, iTab); + m_chartOverlaySets[iTab]->initializeOverlays(); + } +} + + +/** + * Receive an event. + * + * @param event + * An event for which this instance is listening. + */ +void +ChartTwoOverlaySetArray::receiveEvent(Event* event) +{ + if (event->getEventType() == EventTypeEnum::EVENT_BROWSER_TAB_DELETE) { + EventBrowserTabDelete* deleteTabEvent = dynamic_cast(event); + CaretAssert(deleteTabEvent); + + /* + * Since tab is being deleted, reset any chart overlay yoking for the tab. + */ + const int32_t tabIndex = deleteTabEvent->getBrowserTabIndex(); + if ((tabIndex > 0) + && (tabIndex < getNumberOfChartOverlaySets())) { + m_chartOverlaySets[tabIndex]->resetOverlayYokingToOff(); + } + + deleteTabEvent->setEventProcessed(); + } +} + +/** + * Copy the chart overlay set from the source tab index to the + * destination tab index. + * + * @param sourceTabIndex + * Source from which tab content is copied. + * @param destinationTabIndex + * Destination to which tab content is copied. + */ +void +ChartTwoOverlaySetArray::copyChartOverlaySet(const int32_t sourceTabIndex, + const int32_t destinationTabIndex) +{ + CaretAssertVectorIndex(m_chartOverlaySets, sourceTabIndex); + CaretAssertVectorIndex(m_chartOverlaySets, destinationTabIndex); + + const ChartTwoOverlaySet* sourceChartOverlaySet =m_chartOverlaySets[sourceTabIndex]; + ChartTwoOverlaySet* destinationChartOverlaySet =m_chartOverlaySets[destinationTabIndex]; + destinationChartOverlaySet->copyOverlaySet(sourceChartOverlaySet); +} + +/** + * Copy the chart overlay set cartesian axes from the source tab index to the + * destination tab index. + * + * @param sourceTabIndex + * Source from which tab content is copied. + * @param destinationTabIndex + * Destination to which tab content is copied. + */ +void +ChartTwoOverlaySetArray::copyChartOverlaySetCartesianAxes(const int32_t sourceTabIndex, + const int32_t destinationTabIndex) +{ + CaretAssertVectorIndex(m_chartOverlaySets, sourceTabIndex); + CaretAssertVectorIndex(m_chartOverlaySets, destinationTabIndex); + + const ChartTwoOverlaySet* sourceChartOverlaySet =m_chartOverlaySets[sourceTabIndex]; + ChartTwoOverlaySet* destinationChartOverlaySet =m_chartOverlaySets[destinationTabIndex]; + + destinationChartOverlaySet->copyCartesianAxes(sourceChartOverlaySet); +} + +/** + * Save information specific to this type of model to the scene. + * + * @param tabIndices + * Tab indices that are valid. + * @param sceneAttributes + * Attributes for the scene. Scenes may be of different types + * (full, generic, etc) and the attributes should be checked when + * saving the scene. + * + * @param instanceName + * Name of instance in the scene. + */ +SceneClass* +ChartTwoOverlaySetArray::saveTabIndicesToScene(const std::vector& tabIndices, + const SceneAttributes* sceneAttributes, + const AString& instanceName) +{ + SceneClass* sceneClass = new SceneClass(instanceName, + "ChartTwoOverlaySetArray", + 1); + + /* + * Save overlay sets for tabs + */ + SceneObjectMapIntegerKey* overlaySetMap = new SceneObjectMapIntegerKey("m_chartOverlaySetMAP", + SceneObjectDataTypeEnum::SCENE_CLASS); + + for (const auto tabIndex : tabIndices) { + overlaySetMap->addClass(tabIndex, m_chartOverlaySets[tabIndex]->saveToScene(sceneAttributes, + "m_chartOverlaySet")); + } + sceneClass->addChild(overlaySetMap); + + + + +// const int32_t numOverlaySetsToSave = getNumberOfChartOverlaySets(); +// +// std::vector overlaySetVector; +// for (int i = 0; i < numOverlaySetsToSave; i++) { +// overlaySetVector.push_back(m_chartOverlaySets[i]->saveToScene(sceneAttributes, +// "m_chartOverlaySet")); +// } +// +// SceneClassArray* overlaySetArray = new SceneClassArray("m_chartOverlaySets", +// overlaySetVector); +// sceneClass->addChild(overlaySetArray); + + // Uncomment if sub-classes must save to scene + //saveSubClassDataToScene(sceneAttributes, + // sceneClass); + + return sceneClass; +} + +/** + * Restore information specific to the type of model from the scene. + * + * @param sceneAttributes + * Attributes for the scene. Scenes may be of different types + * (full, generic, etc) and the attributes should be checked when + * restoring the scene. + * + * @param sceneClass + * sceneClass from which model specific information is obtained. + */ +void +ChartTwoOverlaySetArray::restoreFromScene(const SceneAttributes* sceneAttributes, + const SceneClass* sceneClass) +{ + if (sceneClass == NULL) { + return; + } + + const SceneObjectMapIntegerKey* overlaySetMap = sceneClass->getMapIntegerKey("m_chartOverlaySetMAP"); + if (overlaySetMap != NULL) { + const std::vector tabIndices = overlaySetMap->getKeys(); + for (const auto tabIndex : tabIndices) { + const SceneClass* sceneClass = overlaySetMap->classValue(tabIndex); + CaretAssertVectorIndex(m_chartOverlaySets, tabIndex); + m_chartOverlaySets[tabIndex]->restoreFromScene(sceneAttributes, + sceneClass); + } + } + + +// const SceneClassArray* overlaySetArray = sceneClass->getClassArray("m_chartOverlaySets"); +// if (overlaySetArray != NULL) { +// const int32_t numOverlaySets = std::min(overlaySetArray->getNumberOfArrayElements(), +// (int32_t)BrainConstants::MAXIMUM_NUMBER_OF_BROWSER_TABS); +// for (int32_t i = 0; i < numOverlaySets; i++) { +// m_chartOverlaySets[i]->restoreFromScene(sceneAttributes, +// overlaySetArray->getClassAtIndex(i)); +// } +// } + //Uncomment if sub-classes must restore from scene + //restoreSubClassDataFromScene(sceneAttributes, + // sceneClass); + +} + diff --git a/src/Brain/ChartTwoOverlaySetArray.h b/src/Brain/ChartTwoOverlaySetArray.h new file mode 100644 index 0000000000000000000000000000000000000000..03c54fd4d761a1c35acea0e73590db270a6e7b28 --- /dev/null +++ b/src/Brain/ChartTwoOverlaySetArray.h @@ -0,0 +1,90 @@ +#ifndef __CHART_TWO_OVERLAY_SET_ARRAY_H__ +#define __CHART_TWO_OVERLAY_SET_ARRAY_H__ + +/*LICENSE_START*/ +/* + * Copyright (C) 2014 Washington University School of Medicine + * + * 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 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ +/*LICENSE_END*/ + + +#include "CaretObject.h" +#include "ChartTwoDataTypeEnum.h" +#include "EventListenerInterface.h" + +namespace caret { + class ChartTwoOverlaySet; + class SceneAttributes; + class SceneClass; + + class ChartTwoOverlaySetArray : public CaretObject, public EventListenerInterface { + + public: + ChartTwoOverlaySetArray(const ChartTwoDataTypeEnum::Enum chartDataType, + const AString& name); + + virtual ~ChartTwoOverlaySetArray(); + + int32_t getNumberOfChartOverlaySets(); + + ChartTwoOverlaySet* getChartTwoOverlaySet(const int32_t indx); + + void initializeOverlaySelections(); + + void copyChartOverlaySet(const int32_t sourceTabIndex, + const int32_t destinationTabIndex); + + void copyChartOverlaySetCartesianAxes(const int32_t sourceTabIndex, + const int32_t destinationTabIndex); + + virtual SceneClass* saveTabIndicesToScene(const std::vector& tabIndices, + const SceneAttributes* sceneAttributes, + const AString& instanceName); + + virtual void restoreFromScene(const SceneAttributes* sceneAttributes, + const SceneClass* sceneClass); + private: + ChartTwoOverlaySetArray(const ChartTwoOverlaySetArray&); + + ChartTwoOverlaySetArray& operator=(const ChartTwoOverlaySetArray&); + + public: + + // ADD_NEW_METHODS_HERE + + virtual AString toString() const; + + virtual void receiveEvent(Event* event); + + private: + // ADD_NEW_MEMBERS_HERE + + void initialize(); + + /** Name for this chart overlay set array */ + AString m_name; + + /** The chart overlay sets */ + std::vectorm_chartOverlaySets; + }; + +#ifdef __CHART_TWO_OVERLAY_SET_ARRAY_DECLARE__ + // +#endif // __CHART_TWO_OVERLAY_SET_ARRAY_DECLARE__ + +} // namespace +#endif //__CHART_TWO_OVERLAY_SET_ARRAY_H__ diff --git a/src/Brain/ChartingDataManager.cxx b/src/Brain/ChartingDataManager.cxx index 5deae2eb311790302417860155581cc138616fc5..18abdca96ce6b27b96c89e606e58e4caf9513f6c 100644 --- a/src/Brain/ChartingDataManager.cxx +++ b/src/Brain/ChartingDataManager.cxx @@ -28,6 +28,7 @@ #include "EventManager.h" #include "EventMapYokingSelectMap.h" #include "ModelChart.h" +#include "ModelChartTwo.h" #include "SurfaceFile.h" using namespace caret; @@ -81,6 +82,13 @@ ChartingDataManager::receiveEvent(Event* event) modelChart->loadChartDataForYokedCiftiMappableFiles(yokeMapEvent->getMapYokingGroup(), yokeMapEvent->getMapIndex()); } + ModelChartTwo* modelChartTwo = m_brain->getChartTwoModel(); + if (modelChartTwo != NULL) { + modelChartTwo->loadChartDataForYokedScalarDataSeriesFiles(yokeMapEvent->getMapYokingGroup(), + yokeMapEvent->getMapIndex()); + modelChartTwo->selectRowColumnInYokedScalarDataSeriesFileOverlay(yokeMapEvent->getMapYokingGroup(), + yokeMapEvent->getMapIndex()); + } } } @@ -104,6 +112,13 @@ ChartingDataManager::loadAverageChartForSurfaceNodes(const SurfaceFile* surfaceF surfaceFile->getNumberOfNodes(), nodeIndices); } + + ModelChartTwo* modelChartTwo = m_brain->getChartTwoModel(); + if (modelChartTwo != NULL) { + modelChartTwo->loadAverageChartDataForSurfaceNodes(surfaceFile->getStructure(), + surfaceFile->getNumberOfNodes(), + nodeIndices); + } } /** @@ -126,6 +141,13 @@ ChartingDataManager::loadChartForSurfaceNode(const SurfaceFile* surfaceFile, surfaceFile->getNumberOfNodes(), nodeIndex); } + + ModelChartTwo* modelChartTwo = m_brain->getChartTwoModel(); + if (modelChartTwo != NULL) { + modelChartTwo->loadChartDataForSurfaceNode(surfaceFile->getStructure(), + surfaceFile->getNumberOfNodes(), + nodeIndex); + } } /** @@ -147,6 +169,12 @@ ChartingDataManager::loadChartForCiftiMappableFileRow(CiftiMappableDataFile* cif modelChart->loadChartDataForCiftiMappableFileRow(ciftiMapFile, rowIndex); } + + ModelChartTwo* modelChartTwo = m_brain->getChartTwoModel(); + if (modelChartTwo != NULL) { + modelChartTwo->loadChartDataForCiftiMappableFileRow(ciftiMapFile, + rowIndex); + } } @@ -163,6 +191,11 @@ ChartingDataManager::loadChartForVoxelAtCoordinate(const float xyz[3]) const if (modelChart != NULL) { modelChart->loadChartDataForVoxelAtCoordinate(xyz); } + + ModelChartTwo* modelChartTwo = m_brain->getChartTwoModel(); + if (modelChartTwo != NULL) { + modelChartTwo->loadChartDataForVoxelAtCoordinate(xyz); + } } /** diff --git a/src/Brain/CiftiConnectivityMatrixDataFileManager.cxx b/src/Brain/CiftiConnectivityMatrixDataFileManager.cxx index b22814cfe5e0489f33e47bb28201f738269d592a..661619556803b53f62d107a3076dc30c7c48ae19 100644 --- a/src/Brain/CiftiConnectivityMatrixDataFileManager.cxx +++ b/src/Brain/CiftiConnectivityMatrixDataFileManager.cxx @@ -177,7 +177,6 @@ CiftiConnectivityMatrixDataFileManager::loadRowOrColumnFromParcelFile(Brain* bra parcelFilesToLoadFrom.push_back(parcelFile); } - PaletteFile* paletteFile = brain->getPaletteFile(); const int32_t mapIndex = 0; /* @@ -191,19 +190,17 @@ CiftiConnectivityMatrixDataFileManager::loadRowOrColumnFromParcelFile(Brain* bra switch (pf->getMatrixLoadingDimension()) { case ChartMatrixLoadingDimensionEnum::CHART_MATRIX_LOADING_BY_COLUMN: pf->loadDataForColumnIndex(rowColumnIndexToLoad); - pf->updateScalarColoringForMap(mapIndex, - paletteFile); + pf->updateScalarColoringForMap(mapIndex); rowColumnInformationOut.push_back(pf->getFileNameNoPath() + " column index=" - + AString::number(rowColumnIndexToLoad)); + + AString::number(rowColumnIndexToLoad + CiftiMappableDataFile::getCiftiFileRowColumnIndexBaseForGUI())); break; case ChartMatrixLoadingDimensionEnum::CHART_MATRIX_LOADING_BY_ROW: pf->loadDataForRowIndex(rowColumnIndexToLoad); - pf->updateScalarColoringForMap(mapIndex, - paletteFile); + pf->updateScalarColoringForMap(mapIndex); rowColumnInformationOut.push_back(pf->getFileNameNoPath() + " row index=" - + AString::number(rowColumnIndexToLoad)); + + AString::number(rowColumnIndexToLoad + CiftiMappableDataFile::getCiftiFileRowColumnIndexBaseForGUI())); break; } } @@ -212,30 +209,27 @@ CiftiConnectivityMatrixDataFileManager::loadRowOrColumnFromParcelFile(Brain* bra } bool -CiftiConnectivityMatrixDataFileManager::loadRowOrColumnFromConnectivityMatrixFile(Brain* brain, +CiftiConnectivityMatrixDataFileManager::loadRowOrColumnFromConnectivityMatrixFile( CiftiMappableConnectivityMatrixDataFile* ciftiConnMatrixFile, const int32_t rowIndex, const int32_t columnIndex, std::vector& rowColumnInformationOut) { - PaletteFile* paletteFile = brain->getPaletteFile(); const int32_t mapIndex = 0; if (rowIndex >= 0) { ciftiConnMatrixFile->loadDataForRowIndex(rowIndex); - ciftiConnMatrixFile->updateScalarColoringForMap(mapIndex, - paletteFile); + ciftiConnMatrixFile->updateScalarColoringForMap(mapIndex); rowColumnInformationOut.push_back(ciftiConnMatrixFile->getFileNameNoPath() + " row index=" - + AString::number(rowIndex)); + + AString::number(rowIndex + CiftiMappableDataFile::getCiftiFileRowColumnIndexBaseForGUI())); } else if (columnIndex >= 0) { ciftiConnMatrixFile->loadDataForColumnIndex(columnIndex); - ciftiConnMatrixFile->updateScalarColoringForMap(mapIndex, - paletteFile); + ciftiConnMatrixFile->updateScalarColoringForMap(mapIndex); rowColumnInformationOut.push_back(ciftiConnMatrixFile->getFileNameNoPath() + " column index=" - + AString::number(columnIndex)); + + AString::number(columnIndex + CiftiMappableDataFile::getCiftiFileRowColumnIndexBaseForGUI())); } return true; @@ -266,8 +260,6 @@ CiftiConnectivityMatrixDataFileManager::loadDataForSurfaceNode(Brain* brain, ciftiMatrixFiles); - PaletteFile* paletteFile = brain->getPaletteFile(); - bool haveData = false; for (std::vector::iterator iter = ciftiMatrixFiles.begin(); iter != ciftiMatrixFiles.end(); @@ -283,8 +275,7 @@ CiftiConnectivityMatrixDataFileManager::loadDataForSurfaceNode(Brain* brain, nodeIndex, rowIndex, columnIndex); - cmf->updateScalarColoringForMap(mapIndex, - paletteFile); + cmf->updateScalarColoringForMap(mapIndex); haveData = true; if (rowIndex >= 0) { @@ -292,20 +283,20 @@ CiftiConnectivityMatrixDataFileManager::loadDataForSurfaceNode(Brain* brain, * Get row/column info for node */ rowColumnInformationOut.push_back(cmf->getFileNameNoPath() - + " nodeIndex=" + + " vertex index=" + AString::number(nodeIndex) - + ", row index= " - + AString::number(rowIndex)); + + ", row index=" + + AString::number(rowIndex + CiftiMappableDataFile::getCiftiFileRowColumnIndexBaseForGUI())); } else if (columnIndex >= 0) { /* * Get row/column info for node */ rowColumnInformationOut.push_back(cmf->getFileNameNoPath() - + " nodeIndex=" + + " vertex index=" + AString::number(nodeIndex) - + ", column index= " - + AString::number(columnIndex)); + + ", column index=" + + AString::number(columnIndex + CiftiMappableDataFile::getCiftiFileRowColumnIndexBaseForGUI())); } } } @@ -337,8 +328,6 @@ CiftiConnectivityMatrixDataFileManager::loadAverageDataForSurfaceNodes(Brain* br getDisplayedConnectivityMatrixFiles(brain, ciftiMatrixFiles); - PaletteFile* paletteFile = brain->getPaletteFile(); - bool haveData = false; for (std::vector::iterator iter = ciftiMatrixFiles.begin(); iter != ciftiMatrixFiles.end(); @@ -350,8 +339,7 @@ CiftiConnectivityMatrixDataFileManager::loadAverageDataForSurfaceNodes(Brain* br surfaceFile->getNumberOfNodes(), surfaceFile->getStructure(), nodeIndices); - cmf->updateScalarColoringForMap(mapIndex, - paletteFile); + cmf->updateScalarColoringForMap(mapIndex); haveData = true; } } @@ -379,8 +367,6 @@ CiftiConnectivityMatrixDataFileManager::loadDataForVoxelAtCoordinate(Brain* brai const float xyz[3], std::vector& rowColumnInformationOut) { - PaletteFile* paletteFile = brain->getPaletteFile(); - std::vector ciftiMatrixFiles; getDisplayedConnectivityMatrixFiles(brain, ciftiMatrixFiles); @@ -398,8 +384,7 @@ CiftiConnectivityMatrixDataFileManager::loadDataForVoxelAtCoordinate(Brain* brai xyz, rowIndex, columnIndex); - cmf->updateScalarColoringForMap(mapIndex, - paletteFile); + cmf->updateScalarColoringForMap(mapIndex); haveData = true; if (rowIndex >= 0) { @@ -409,8 +394,8 @@ CiftiConnectivityMatrixDataFileManager::loadDataForVoxelAtCoordinate(Brain* brai rowColumnInformationOut.push_back(cmf->getFileNameNoPath() + " Voxel XYZ=" + AString::fromNumbers(xyz, 3, ",") - + ", row index= " - + AString::number(rowIndex)); + + ", row index=" + + AString::number(rowIndex + CiftiMappableDataFile::getCiftiFileRowColumnIndexBaseForGUI())); } else if (columnIndex >= 0) { /* @@ -419,8 +404,8 @@ CiftiConnectivityMatrixDataFileManager::loadDataForVoxelAtCoordinate(Brain* brai rowColumnInformationOut.push_back(cmf->getFileNameNoPath() + " Voxel XYZ=" + AString::fromNumbers(xyz, 3, ",") - + ", column index= " - + AString::number(columnIndex)); + + ", column index=" + + AString::number(columnIndex + CiftiMappableDataFile::getCiftiFileRowColumnIndexBaseForGUI())); } } } @@ -451,8 +436,6 @@ CiftiConnectivityMatrixDataFileManager::loadAverageDataForVoxelIndices(Brain* br const int64_t volumeDimensionIJK[3], const std::vector& voxelIndices) { - PaletteFile* paletteFile = brain->getPaletteFile(); - std::vector ciftiMatrixFiles; getDisplayedConnectivityMatrixFiles(brain, ciftiMatrixFiles); @@ -470,8 +453,7 @@ CiftiConnectivityMatrixDataFileManager::loadAverageDataForVoxelIndices(Brain* br } haveData = true; - cmf->updateScalarColoringForMap(mapIndex, - paletteFile); + cmf->updateScalarColoringForMap(mapIndex); } } diff --git a/src/Brain/CiftiConnectivityMatrixDataFileManager.h b/src/Brain/CiftiConnectivityMatrixDataFileManager.h index 0f765345bc4f411a1c6bed65eb41483377baee20..ed5f5157432abcf5574b9b8e30a2bc4ede5615cf 100644 --- a/src/Brain/CiftiConnectivityMatrixDataFileManager.h +++ b/src/Brain/CiftiConnectivityMatrixDataFileManager.h @@ -64,8 +64,7 @@ namespace caret { const int32_t columnIndex, std::vector& rowColumnInformationOut); - bool loadRowOrColumnFromConnectivityMatrixFile(Brain* brain, - CiftiMappableConnectivityMatrixDataFile* parcelFile, + bool loadRowOrColumnFromConnectivityMatrixFile(CiftiMappableConnectivityMatrixDataFile* parcelFile, const int32_t rowIndex, const int32_t columnIndex, std::vector& rowColumnInformationOut); diff --git a/src/Brain/DisplayPropertiesAnnotation.cxx b/src/Brain/DisplayPropertiesAnnotation.cxx index e1ae3b99bc452aef8c337fca39c53c5b1a08e08a..71389ade536183a8adaec87c9e1f66efd26bbafa 100644 --- a/src/Brain/DisplayPropertiesAnnotation.cxx +++ b/src/Brain/DisplayPropertiesAnnotation.cxx @@ -87,7 +87,7 @@ DisplayPropertiesAnnotation::updateForNewAnnotation(const Annotation* annotation CaretAssert(annotation); switch (annotation->getCoordinateSpace()) { - case AnnotationCoordinateSpaceEnum::PIXELS: + case AnnotationCoordinateSpaceEnum::CHART: break; case AnnotationCoordinateSpaceEnum::STEREOTAXIC: break; @@ -95,6 +95,8 @@ DisplayPropertiesAnnotation::updateForNewAnnotation(const Annotation* annotation break; case AnnotationCoordinateSpaceEnum::TAB: break; + case AnnotationCoordinateSpaceEnum::VIEWPORT: + break; case AnnotationCoordinateSpaceEnum::WINDOW: setDisplayWindowAnnotationsInSingleTabViews(annotation->getWindowIndex(), true); @@ -133,7 +135,7 @@ DisplayPropertiesAnnotation::resetPrivate() m_displayWindowAnnotationsInSingleTabViews[i] = true; } for (int32_t i = 0; i < BrainConstants::MAXIMUM_NUMBER_OF_BROWSER_TABS; i++) { - m_displayGroup[i] = DisplayGroupEnum::getDefaultValue(); + m_displayGroup[i] = DisplayGroupEnum::DISPLAY_GROUP_TAB; } } @@ -362,10 +364,6 @@ DisplayPropertiesAnnotation::restoreVersionOne(const SceneClass* sceneClass) surfaceStatusInTab[i] = true; tabStatusInTab[i] = true; } - bool windowStatusInTab[BrainConstants::MAXIMUM_NUMBER_OF_BROWSER_WINDOWS]; - for (int32_t i = 0; i < BrainConstants::MAXIMUM_NUMBER_OF_BROWSER_WINDOWS; i++) { - windowStatusInTab[i] = true; - } /* * Look for version one selections in the scene @@ -414,8 +412,8 @@ DisplayPropertiesAnnotation::restoreVersionOne(const SceneClass* sceneClass) annIter++) { Annotation* ann = *annIter; switch (ann->getCoordinateSpace()) { - case AnnotationCoordinateSpaceEnum::PIXELS: - CaretAssert(0); + case AnnotationCoordinateSpaceEnum::CHART: + CaretAssertMessage(0, "This should never happen as CHART SPACE was never available in a version one scene"); break; case AnnotationCoordinateSpaceEnum::STEREOTAXIC: if (stereoAnnDisplay != NULL) { @@ -458,6 +456,12 @@ DisplayPropertiesAnnotation::restoreVersionOne(const SceneClass* sceneClass) } } break; + case AnnotationCoordinateSpaceEnum::VIEWPORT: + /* + * Ignore annotations in viewport space. These are usually text annotations + * in the graphics region such as chart labels. + */ + break; case AnnotationCoordinateSpaceEnum::WINDOW: if (numWindowStatus) { const int32_t windowIndex = ann->getWindowIndex(); diff --git a/src/Brain/DisplayPropertiesFiberOrientation.cxx b/src/Brain/DisplayPropertiesFiberOrientation.cxx index 4404a0115e7e5067513fe4032311cf773b89acfb..f1c29fe8b41cf58d8e43814bedac8865659c91aa 100644 --- a/src/Brain/DisplayPropertiesFiberOrientation.cxx +++ b/src/Brain/DisplayPropertiesFiberOrientation.cxx @@ -145,10 +145,10 @@ DisplayPropertiesFiberOrientation::DisplayPropertiesFiberOrientation() m_fanMultiplierInDisplayGroup, DisplayGroupEnum::NUMBER_OF_GROUPS, m_fanMultiplierInDisplayGroup[0]); - m_sceneAssistant->addArray("m_displaySphereOrientationsInTab", - m_displaySphereOrientationsInTab, + m_sceneAssistant->addArray("m_displaySphereOrientationsInDisplayGroup", + m_displaySphereOrientationsInDisplayGroup, DisplayGroupEnum::NUMBER_OF_GROUPS, - m_displaySphereOrientationsInTab[0]); + m_displaySphereOrientationsInDisplayGroup[0]); m_sceneAssistant->addTabIndexedEnumeratedTypeArray("m_fiberColoringTypeInTab", m_fiberColoringTypeInTab); diff --git a/src/Brain/DisplayPropertiesImages.cxx b/src/Brain/DisplayPropertiesImages.cxx index 30990535a41386b5a6a29c6684540995c0e1639d..0d736d5106fd72ed9453ab827a533af03158b029 100644 --- a/src/Brain/DisplayPropertiesImages.cxx +++ b/src/Brain/DisplayPropertiesImages.cxx @@ -35,6 +35,8 @@ #include "SceneClassAssistant.h" #include "ScenePathName.h" #include "ScenePathNameArray.h" +#include "ScenePrimitive.h" +#include "ScenePrimitiveArray.h" using namespace caret; @@ -590,6 +592,35 @@ DisplayPropertiesImages::restoreFromScene(const SceneAttributes* sceneAttributes } } } + + /* + * Old scenes image display status for tab + */ + const SceneObjectMapIntegerKey* oldTabStatus = sceneClass->getMapIntegerKey("m_displayStatusInTab"); + if (oldTabStatus != NULL) { + const std::map& dataMap = oldTabStatus->getMap(); + for (auto& tabPrim : dataMap) { + const int32_t tabIndex = tabPrim.first; + const ScenePrimitive* primitive = dynamic_cast(tabPrim.second); + CaretAssert(primitive); + m_displayStatus->setValue(DisplayGroupEnum::DISPLAY_GROUP_TAB, tabIndex, primitive->booleanValue()); + } + } + + /* + * Old scenes image display status for display group + */ + const ScenePrimitiveArray* oldGroupStatus = sceneClass->getPrimitiveArray("m_displayStatusInDisplayGroup"); + if (oldGroupStatus != NULL) { + const int32_t numValues = std::min(oldGroupStatus->getNumberOfArrayElements(), + static_cast(DisplayGroupEnum::NUMBER_OF_GROUPS)); + for (int32_t i = 0; i < numValues; i++) { + bool validFlag = false; + const DisplayGroupEnum::Enum dg = DisplayGroupEnum::fromIntegerCode(i, &validFlag); + CaretAssert(validFlag); + m_displayStatus->setValue(dg, 0, oldGroupStatus->booleanValue(i)); + } + } } /** diff --git a/src/Brain/DummyFontTextRenderer.cxx b/src/Brain/DummyFontTextRenderer.cxx index b960a756b7bfcb35ae45d9f6d65997ba43505166..cc14ef7a8767672e9cd0694cc5e217ad25ef76f1 100644 --- a/src/Brain/DummyFontTextRenderer.cxx +++ b/src/Brain/DummyFontTextRenderer.cxx @@ -149,6 +149,8 @@ DummyFontTextRenderer::drawTextAtModelCoords(const double /*modelX*/, * Viewport Y-coordinate. * @param viewportZ * Viewport Z-coordinate. + * @param viewportWidth + * Height of the viewport needed for percentage height text. * @param viewportHeight * Height of the viewport needed for percentage height text. * @param bottomLeftOut @@ -165,7 +167,7 @@ DummyFontTextRenderer::getBoundsForTextAtViewportCoords(const AnnotationText& /* const double /*viewportX*/, const double /*viewportY*/, const double /*viewportZ*/, - const double /*viewportHeight*/, + const double /*viewportWidth*/, const double /*viewportHeight*/, double* /*bottomLeftOut[3]*/, double* /*bottomRightOut[3]*/, double* /*topRightOut[3]*/, @@ -191,6 +193,7 @@ DummyFontTextRenderer::getBoundsForTextAtViewportCoords(const AnnotationText& /* */ void DummyFontTextRenderer::getTextWidthHeightInPixels(const AnnotationText& /*annotationText*/, + const double /*viewportWidth*/, const double /*viewportHeight*/, double& /*widthOut*/, double& /*heightOut*/) diff --git a/src/Brain/DummyFontTextRenderer.h b/src/Brain/DummyFontTextRenderer.h index 0649c326d379d2a35fce54ee9e78c1237b76b525..3dabcd5afc3a15e83e9cdf29c582692d849dc61e 100644 --- a/src/Brain/DummyFontTextRenderer.h +++ b/src/Brain/DummyFontTextRenderer.h @@ -52,6 +52,7 @@ namespace caret { const AnnotationText& annotationText); virtual void getTextWidthHeightInPixels(const AnnotationText& annotationText, + const double viewportWidth, const double viewportHeight, double& widthOut, double& heightOut); @@ -60,6 +61,7 @@ namespace caret { const double viewportX, const double viewportY, const double viewportZ, + const double viewportWidth, const double viewportHeight, double bottomLeftOut[3], double bottomRightOut[3], diff --git a/src/Brain/EventBrowserWindowContent.cxx b/src/Brain/EventBrowserWindowContent.cxx new file mode 100644 index 0000000000000000000000000000000000000000..4ea87f4a85c4b984f97ad21cba4e56d9d6a3a50c --- /dev/null +++ b/src/Brain/EventBrowserWindowContent.cxx @@ -0,0 +1,149 @@ + +/*LICENSE_START*/ +/* + * Copyright (C) 2018 Washington University School of Medicine + * + * 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 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ +/*LICENSE_END*/ + +#define __EVENT_BROWSER_WINDOW_CONTENT_DECLARE__ +#include "EventBrowserWindowContent.h" +#undef __EVENT_BROWSER_WINDOW_CONTENT_DECLARE__ + +#include "CaretAssert.h" +#include "EventTypeEnum.h" + +using namespace caret; + + + +/** + * \class caret::EventBrowserWindowContent + * \brief Evnets involving browser window content + * \ingroup Brain + */ + +/** + * Constructor. + * + * @param mode + * The mode. + * @param browserWindowIndex + * Index of the browser window. + */ +EventBrowserWindowContent::EventBrowserWindowContent(const Mode mode, + const int32_t browserWindowIndex) +: Event(EventTypeEnum::EVENT_BROWSER_WINDOW_CONTENT), +m_mode(mode), +m_browserWindowIndex(browserWindowIndex) +{ + +} + +/** + * Destructor. + */ +EventBrowserWindowContent::~EventBrowserWindowContent() +{ +} + +/** + * @return Event that is sent to create new BrowserWindowContent + * for the given window index. + * + * @param windowIndex + * Index of the new window. + */ +std::unique_ptr +EventBrowserWindowContent::newWindowContent(const int32_t windowIndex) +{ + EventBrowserWindowContent* event = new EventBrowserWindowContent(Mode::NEW, + windowIndex); + std::unique_ptr pointer(event); + return pointer; +} + +/** + * @return Event that is sent to create delete BrowserWindowContent + * for the given window index. + * + * @param windowIndex + * Index of the deleted window. + */ +std::unique_ptr +EventBrowserWindowContent::deleteWindowContent(const int32_t windowIndex) +{ + EventBrowserWindowContent* event = new EventBrowserWindowContent(Mode::DELETER, + windowIndex); + std::unique_ptr pointer(event); + return pointer; +} + +/** + * @return Event that is sent to create get BrowserWindowContent + * for the given window index. + * + * @param windowIndex + * Index of the window. + */ +std::unique_ptr +EventBrowserWindowContent::getWindowContent(const int32_t windowIndex) +{ + EventBrowserWindowContent* event = new EventBrowserWindowContent(Mode::GET, + windowIndex); + std::unique_ptr pointer(event); + return pointer; +} + + +/** + * @return The mode. + */ +EventBrowserWindowContent::Mode +EventBrowserWindowContent::getMode() const +{ + return m_mode; +} + +/** + * @return Pointer to the browser window content. + */ +BrowserWindowContent* +EventBrowserWindowContent::getBrowserWindowContent() +{ + return m_browserWindowContent; +} + +/** + * Set the browser window content. + * + * @param browserWindowContent + * Pointer to the browser window content. + */ +void +EventBrowserWindowContent::setBrowserWindowContent(BrowserWindowContent* browserWindowContent) +{ + m_browserWindowContent = browserWindowContent; +} + +/** + * @return Index of the browser window. + */ +int32_t +EventBrowserWindowContent::getBrowserWindowIndex() const +{ + return m_browserWindowIndex; +} diff --git a/src/Brain/EventBrowserWindowContent.h b/src/Brain/EventBrowserWindowContent.h new file mode 100644 index 0000000000000000000000000000000000000000..b03dd88ce39960b8e8cd9d8666191153454dbccb --- /dev/null +++ b/src/Brain/EventBrowserWindowContent.h @@ -0,0 +1,86 @@ +#ifndef __EVENT_BROWSER_WINDOW_CONTENT_H__ +#define __EVENT_BROWSER_WINDOW_CONTENT_H__ + +/*LICENSE_START*/ +/* + * Copyright (C) 2018 Washington University School of Medicine + * + * 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 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ +/*LICENSE_END*/ + + + +#include + +#include "Event.h" + + + +namespace caret { + + class BrowserWindowContent; + + class EventBrowserWindowContent : public Event { + + public: + enum class Mode { + DELETER, /* DELETE is reserved word in MS Visual Studio C++ */ + GET, + NEW + }; + + static std::unique_ptr newWindowContent(const int32_t windowIndex); + + static std::unique_ptr deleteWindowContent(const int32_t windowIndex); + + static std::unique_ptr getWindowContent(const int32_t windowIndex); + + virtual ~EventBrowserWindowContent(); + + Mode getMode() const; + + BrowserWindowContent* getBrowserWindowContent(); + + void setBrowserWindowContent(BrowserWindowContent* browserWindowContent); + + int32_t getBrowserWindowIndex() const; + + // ADD_NEW_METHODS_HERE + + private: + EventBrowserWindowContent(const Mode mode, + const int32_t browserWindowIndex); + + EventBrowserWindowContent(const EventBrowserWindowContent&); + + EventBrowserWindowContent& operator=(const EventBrowserWindowContent&); + + const Mode m_mode; + + const int32_t m_browserWindowIndex = -1; + + BrowserWindowContent* m_browserWindowContent = NULL; + + // ADD_NEW_MEMBERS_HERE + + }; + +#ifdef __EVENT_BROWSER_WINDOW_CONTENT_DECLARE__ + // +#endif // __EVENT_BROWSER_WINDOW_CONTENT_DECLARE__ + +} // namespace +#endif //__EVENT_BROWSER_WINDOW_CONTENT_H__ diff --git a/src/Brain/EventChartOverlayValidate.cxx b/src/Brain/EventChartOverlayValidate.cxx new file mode 100644 index 0000000000000000000000000000000000000000..61d2b7f9f86ac35abc11692ceb12097c690af900 --- /dev/null +++ b/src/Brain/EventChartOverlayValidate.cxx @@ -0,0 +1,82 @@ + +/*LICENSE_START*/ +/* + * Copyright (C) 2014 Washington University School of Medicine + * + * 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 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ +/*LICENSE_END*/ + +#define __EVENT_CHART_OVERLAY_VALIDATE_DECLARE__ +#include "EventChartOverlayValidate.h" +#undef __EVENT_CHART_OVERLAY_VALIDATE_DECLARE__ + +#include "EventTypeEnum.h" + +using namespace caret; + + + +/** + * \class caret::EventChartOverlayValidate + * \brief Test a chart overlay for validity (it exists). + * \ingroup Brain + */ + +/** + * Constructor. + * + * @param chartOverlay + * Chart overlay for verification. + */ +EventChartOverlayValidate::EventChartOverlayValidate(const ChartTwoOverlay* chartOverlay) +: Event(EventTypeEnum::EVENT_CHART_OVERLAY_VALIDATE), + m_chartOverlay(chartOverlay) +{ + m_valid = false; +} + +/** + * Destructor. + */ +EventChartOverlayValidate::~EventChartOverlayValidate() +{ + +} + +/** + * @return true if the chart overlay was found to be valid. + */ +bool +EventChartOverlayValidate::isValidChartOverlay() const +{ + return m_valid; +} + +/** + * Set the validity if the given overlay is the overlay + * that was passed to the constructor. + * + * @param chartOverlay + * Chart overlay tested for match. + */ +void +EventChartOverlayValidate::testValidChartOverlay(const ChartTwoOverlay* chartOverlay) +{ + if (m_chartOverlay == chartOverlay) { + m_valid = true; + } +} + diff --git a/src/Brain/EventChartOverlayValidate.h b/src/Brain/EventChartOverlayValidate.h new file mode 100644 index 0000000000000000000000000000000000000000..7f4c8dec939e62d389afb0669fc05fb59288ebf9 --- /dev/null +++ b/src/Brain/EventChartOverlayValidate.h @@ -0,0 +1,66 @@ +#ifndef __EVENT_CHART_OVERLAY_VALIDATE_H__ +#define __EVENT_CHART_OVERLAY_VALIDATE_H__ + +/*LICENSE_START*/ +/* + * Copyright (C) 2014 Washington University School of Medicine + * + * 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 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ +/*LICENSE_END*/ + + +#include "Event.h" + +namespace caret { + + class ChartTwoOverlay; + + class EventChartOverlayValidate : public Event { + + public: + EventChartOverlayValidate(const ChartTwoOverlay* chartOverlay); + + virtual ~EventChartOverlayValidate(); + + bool isValidChartOverlay() const; + + void testValidChartOverlay(const ChartTwoOverlay* chartOverlay); + + private: + EventChartOverlayValidate(const EventChartOverlayValidate&); + + EventChartOverlayValidate& operator=(const EventChartOverlayValidate&); + + public: + + // ADD_NEW_METHODS_HERE + + private: + + // ADD_NEW_MEMBERS_HERE + + const ChartTwoOverlay* m_chartOverlay; + + bool m_valid; + + }; + +#ifdef __EVENT_CHART_OVERLAY_VALIDATE_DECLARE__ + // +#endif // __EVENT_CHART_OVERLAY_VALIDATE_DECLARE__ + +} // namespace +#endif //__EVENT_CHART_OVERLAY_VALIDATE_H__ diff --git a/src/Brain/EventGetBrainOpenGLTextRenderer.cxx b/src/Brain/EventGetBrainOpenGLTextRenderer.cxx index 8779f62dbcfecb580cb5aaaaa6a5f1ba054da86d..ce59cd09c4b1a93e802e3c8fe50b97fa326583b6 100644 --- a/src/Brain/EventGetBrainOpenGLTextRenderer.cxx +++ b/src/Brain/EventGetBrainOpenGLTextRenderer.cxx @@ -39,9 +39,8 @@ using namespace caret; /** * Constructor. */ -EventGetBrainOpenGLTextRenderer::EventGetBrainOpenGLTextRenderer(const int32_t windowIndex) -: Event(EventTypeEnum::EVENT_GET_TEXT_RENDERER_FOR_WINDOW), -m_windowIndex(windowIndex) +EventGetBrainOpenGLTextRenderer::EventGetBrainOpenGLTextRenderer() +: Event(EventTypeEnum::EVENT_GET_TEXT_RENDERER_FOR_WINDOW) { m_textRenderer = NULL; } @@ -53,15 +52,6 @@ EventGetBrainOpenGLTextRenderer::~EventGetBrainOpenGLTextRenderer() { } -/** - * @return Index of the window. - */ -int32_t -EventGetBrainOpenGLTextRenderer::getWindowIndex() const -{ - return m_windowIndex; -} - /** * @return The text renderer found for the window. * (NULL if not found). diff --git a/src/Brain/EventGetBrainOpenGLTextRenderer.h b/src/Brain/EventGetBrainOpenGLTextRenderer.h index 19dfd9105648be38fa156f43c607401e89044b53..fc19eae3f4ed525a38daf0f41053bb11622a7c10 100644 --- a/src/Brain/EventGetBrainOpenGLTextRenderer.h +++ b/src/Brain/EventGetBrainOpenGLTextRenderer.h @@ -33,12 +33,10 @@ namespace caret { class EventGetBrainOpenGLTextRenderer : public Event { public: - EventGetBrainOpenGLTextRenderer(const int32_t windowIndex); + EventGetBrainOpenGLTextRenderer(); virtual ~EventGetBrainOpenGLTextRenderer(); - int32_t getWindowIndex() const; - BrainOpenGLTextRenderInterface* getTextRenderer() const; void setTextRenderer(BrainOpenGLTextRenderInterface* textRenderer); @@ -50,8 +48,6 @@ namespace caret { EventGetBrainOpenGLTextRenderer& operator=(const EventGetBrainOpenGLTextRenderer&); - const int32_t m_windowIndex; - BrainOpenGLTextRenderInterface* m_textRenderer; // ADD_NEW_MEMBERS_HERE diff --git a/src/Brain/EventModelGetAllDisplayed.cxx b/src/Brain/EventModelGetAllDisplayed.cxx new file mode 100644 index 0000000000000000000000000000000000000000..cbf57637494b03b60e4a814292b831afd23a4daa --- /dev/null +++ b/src/Brain/EventModelGetAllDisplayed.cxx @@ -0,0 +1,79 @@ + +/*LICENSE_START*/ +/* + * Copyright (C) 2017 Washington University School of Medicine + * + * 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 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ +/*LICENSE_END*/ + +#define __EVENT_MODEL_GET_ALL_DISPLAYED_DECLARE__ +#include "EventModelGetAllDisplayed.h" +#undef __EVENT_MODEL_GET_ALL_DISPLAYED_DECLARE__ + +#include "CaretAssert.h" +#include "EventTypeEnum.h" + +using namespace caret; + + + +/** + * \class caret::EventModelGetAllDisplayed + * \brief Event to get the displayed models. + * \ingroup Brain + */ + +/** + * Constructor. + */ +EventModelGetAllDisplayed::EventModelGetAllDisplayed() +: Event(EventTypeEnum::EVENT_MODEL_GET_ALL_DISPLAYED) +{ + +} + +/** + * Destructor. + */ +EventModelGetAllDisplayed::~EventModelGetAllDisplayed() +{ +} + +/** + * @return A set containing the displayed models. + */ +std::set +EventModelGetAllDisplayed::getModels() const +{ + return m_models; +} + +/** + * Add a model to the displayed models. + * This method may be called more than once with the same model. + * + * @param model + * The model. + */ +void +EventModelGetAllDisplayed::addModel(Model* model) +{ + if (model == NULL) { + return; + } + m_models.insert(model); +} + diff --git a/src/Brain/EventModelGetAllDisplayed.h b/src/Brain/EventModelGetAllDisplayed.h new file mode 100644 index 0000000000000000000000000000000000000000..56267ec176d3b8935604a42bdf66ce183b65ff07 --- /dev/null +++ b/src/Brain/EventModelGetAllDisplayed.h @@ -0,0 +1,62 @@ +#ifndef __EVENT_MODEL_GET_ALL_DISPLAYED_H__ +#define __EVENT_MODEL_GET_ALL_DISPLAYED_H__ + +/*LICENSE_START*/ +/* + * Copyright (C) 2017 Washington University School of Medicine + * + * 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 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ +/*LICENSE_END*/ + +#include + +#include "Event.h" + + +namespace caret { + + class Model; + + class EventModelGetAllDisplayed : public Event { + + public: + EventModelGetAllDisplayed(); + + virtual ~EventModelGetAllDisplayed(); + + std::set getModels() const; + + void addModel(Model* model); + + // ADD_NEW_METHODS_HERE + + private: + EventModelGetAllDisplayed(const EventModelGetAllDisplayed&); + + EventModelGetAllDisplayed& operator=(const EventModelGetAllDisplayed&); + + std::set m_models; + + // ADD_NEW_MEMBERS_HERE + + }; + +#ifdef __EVENT_MODEL_GET_ALL_DISPLAYED_DECLARE__ + // +#endif // __EVENT_MODEL_GET_ALL_DISPLAYED_DECLARE__ + +} // namespace +#endif //__EVENT_MODEL_GET_ALL_DISPLAYED_H__ diff --git a/src/Brain/FtglFontTextRenderer.cxx b/src/Brain/FtglFontTextRenderer.cxx index 13f34b376578df83fc4058ceeb678a6d810d565c..8428ea1dec389f632ae593944ba48e7643c5dca2 100644 --- a/src/Brain/FtglFontTextRenderer.cxx +++ b/src/Brain/FtglFontTextRenderer.cxx @@ -70,15 +70,19 @@ #include #include #include +#include #include #include #include "AnnotationPointSizeText.h" -#include "BrainOpenGLPrimitiveDrawing.h" +#include "BrainOpenGL.h" #include "CaretAssert.h" #include "CaretLogger.h" #include "CaretOpenGLInclude.h" +#include "GraphicsOpenGLError.h" +#include "GraphicsShape.h" +#include "GraphicsUtilitiesOpenGL.h" #include "MathFunctions.h" #include "Matrix4x4.h" @@ -129,6 +133,8 @@ FtglFontTextRenderer::FtglFontTextRenderer() true); #endif // HAVE_FREETYPE m_depthTestingStatus = DEPTH_TEST_NO; + BrainOpenGL::getMinMaxLineWidth(m_lineWidthMinimum, + m_lineWidthMaximum); } /** @@ -178,7 +184,31 @@ FtglFontTextRenderer::getFont(const AnnotationText& annotationText, const bool creatingDefaultFontFlag) { #ifdef HAVE_FREETYPE - const AString fontName = annotationText.getFontRenderingEncodedName(m_viewportHeight); + int32_t viewportWidth = m_viewportWidth; + int32_t viewportHeight = m_viewportHeight; + + switch (annotationText.getCoordinateSpace()) { + case AnnotationCoordinateSpaceEnum::CHART: + break; + case AnnotationCoordinateSpaceEnum::STEREOTAXIC: + break; + case AnnotationCoordinateSpaceEnum::SURFACE: + break; + case AnnotationCoordinateSpaceEnum::TAB: + break; + case AnnotationCoordinateSpaceEnum::VIEWPORT: + { + int32_t vp[4]; + annotationText.getViewportCoordinateSpaceViewport(vp); + viewportWidth = vp[2]; + viewportHeight = vp[3]; + } + break; + case AnnotationCoordinateSpaceEnum::WINDOW: + break; + } + const AString fontName = annotationText.getFontRenderingEncodedName(viewportWidth, + viewportHeight); /* * Has the font already has been created? @@ -187,6 +217,13 @@ FtglFontTextRenderer::getFont(const AnnotationText& annotationText, if (fontIter != m_fontNameToFontMap.end()) { FontData* fontData = fontIter->second; CaretAssert(fontData); + + /* + * Set font "too small" status + */ + const bool tooSmallFlag = (fontData->m_font->FaceSize() <= AnnotationText::getTooSmallTextHeight()); + annotationText.setFontTooSmallWhenLastDrawn(tooSmallFlag); + return fontData->m_font; } @@ -194,7 +231,8 @@ FtglFontTextRenderer::getFont(const AnnotationText& annotationText, * Create and save the font */ FontData* fontData = new FontData(annotationText, - m_viewportHeight); + viewportWidth, + viewportHeight); if (fontData->m_valid) { /* * Request font is valid. @@ -203,6 +241,13 @@ FtglFontTextRenderer::getFont(const AnnotationText& annotationText, fontData)); CaretLogFine("Created font with encoded name " + fontName); + + /* + * Set font "too small" status + */ + const bool tooSmallFlag = (fontData->m_font->FaceSize() <= AnnotationText::getTooSmallTextHeight()); + annotationText.setFontTooSmallWhenLastDrawn(tooSmallFlag); + return fontData->m_font; } else { @@ -235,6 +280,7 @@ FtglFontTextRenderer::getFont(const AnnotationText& annotationText, /* * Failed so use the default font. */ + annotationText.setFontTooSmallWhenLastDrawn(false); return m_defaultFont; #else // HAVE_FREETYPE @@ -243,8 +289,28 @@ FtglFontTextRenderer::getFont(const AnnotationText& annotationText, #endif // HAVE_FREETYPE } - - +/** + * Convert a percentage height to a line width in pixels + * + * @param percentageHeight + * Percentage of viewport height. + * @return + * Line width in pixels clamped to valid range + */ +double +FtglFontTextRenderer::getLineWidthFromPercentageHeight(const double percentageHeight) const +{ + float widthPixels = (percentageHeight / 100.0) * m_viewportHeight; + + if (widthPixels < m_lineWidthMinimum) { + widthPixels = m_lineWidthMinimum; + } + else if (widthPixels > m_lineWidthMaximum) { + widthPixels = m_lineWidthMaximum; + } + + return widthPixels; +} /** * Draw the text piceces at their assigned viewport coordinates. @@ -265,6 +331,9 @@ FtglFontTextRenderer::drawTextAtViewportCoordinatesInternal(const AnnotationText if (! font) { return; } + +// const bool tooSmallFlag = (font->FaceSize() <= s_tooSmallFontSize); +// annotationText.setFontTooSmallWhenLastDrawn(tooSmallFlag); if (annotationText.getText().isEmpty()) { return; @@ -347,7 +416,6 @@ FtglFontTextRenderer::drawTextAtViewportCoordinatesInternal(const AnnotationText } const double underlineOffsetY = (textStringGroup.m_underlineThickness / 2.0); - //const double outlineOffsetY = (textStringGroup.m_outlineThickness / 2.0); double bottomLeft[3], bottomRight[3], topRight[3], topLeft[3], rotationPointXYZ[3]; textStringGroup.getViewportBounds(s_textMarginSize, @@ -405,8 +473,8 @@ FtglFontTextRenderer::drawTextAtViewportCoordinatesInternal(const AnnotationText drawUnderline(ts->m_stringGlyphsMinX, ts->m_stringGlyphsMaxX, underlineY, - z, //0.0, // Z - textStringGroup.m_underlineThickness, + z, + ts->m_underlineThickness, foregroundRgba); glPopMatrix(); @@ -416,16 +484,14 @@ FtglFontTextRenderer::drawTextAtViewportCoordinatesInternal(const AnnotationText glPushMatrix(); glTranslated(ts->m_viewportX - rotationPointXYZ[0], ts->m_viewportY - rotationPointXYZ[1], 0.0); -// const double outlineMinY = ts->m_stringGlyphsMinY + outlineOffsetY; -// const double outlineMaxY = ts->m_stringGlyphsMaxY - outlineOffsetY; uint8_t foregroundRgba[4]; annotationText.getLineColorRGBA(foregroundRgba); drawOutline(ts->m_stringGlyphsMinX, ts->m_stringGlyphsMaxX, ts->m_stringGlyphsMinY, ts->m_stringGlyphsMaxY, - z, //0.0, // Z - textStringGroup.m_outlineThickness, + z, + ts->m_outlineThickness, foregroundRgba); glPopMatrix(); @@ -486,9 +552,10 @@ FtglFontTextRenderer::drawUnderline(const double lineStartX, underlineCoords.insert(underlineCoords.end(), lineY); underlineCoords.insert(underlineCoords.end(), lineZ); - BrainOpenGLPrimitiveDrawing::drawLines(underlineCoords, - foregroundRgba, - underlineThickness); + GraphicsShape::drawLinesByteColor(underlineCoords, + foregroundRgba, + GraphicsPrimitive::LineWidthType::PIXELS, + underlineThickness); glDisable(GL_LINE_SMOOTH); glDisable(GL_BLEND); @@ -529,35 +596,16 @@ FtglFontTextRenderer::drawOutline(const double minX, glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); glHint(GL_LINE_SMOOTH_HINT, GL_NICEST); -// const float halfThickness = outlineThickness / 2.0; - float bottomLeft[3] = { minX, minY, z }; - float bottomRight[3] = { maxX, minY, z }; - float topRight[3] = { maxX, maxY, z }; - float topLeft[3] = { minX, maxY, z }; + float bottomLeft[3] = { (float)minX, (float)minY, (float)z }; + float bottomRight[3] = { (float)maxX, (float)minY, (float)z }; + float topRight[3] = { (float)maxX, (float)maxY, (float)z }; + float topLeft[3] = { (float)minX, (float)maxY, (float)z }; expandBox(bottomLeft, bottomRight, topRight, topLeft, outlineThickness, outlineThickness); - std::vector underlineCoords; - underlineCoords.insert(underlineCoords.end(), bottomLeft, bottomLeft + 3); - underlineCoords.insert(underlineCoords.end(), bottomRight, bottomRight + 3); - underlineCoords.insert(underlineCoords.end(), topRight, topRight + 3); - underlineCoords.insert(underlineCoords.end(), topLeft, topLeft + 3); -// underlineCoords.insert(underlineCoords.end(), minX); -// underlineCoords.insert(underlineCoords.end(), minY); -// underlineCoords.insert(underlineCoords.end(), z); -// underlineCoords.insert(underlineCoords.end(), maxX); -// underlineCoords.insert(underlineCoords.end(), minY); -// underlineCoords.insert(underlineCoords.end(), z); -// underlineCoords.insert(underlineCoords.end(), maxX); -// underlineCoords.insert(underlineCoords.end(), maxY); -// underlineCoords.insert(underlineCoords.end(), z); -// underlineCoords.insert(underlineCoords.end(), minX); -// underlineCoords.insert(underlineCoords.end(), maxY); -// underlineCoords.insert(underlineCoords.end(), z); - - BrainOpenGLPrimitiveDrawing::drawLineLoop(underlineCoords, - foregroundRgba, - outlineThickness); + GraphicsShape::drawBoxOutlineByteColor(bottomLeft, bottomRight, topRight, topLeft, + foregroundRgba, + GraphicsPrimitive::LineWidthType::PIXELS, outlineThickness); glDisable(GL_LINE_SMOOTH); glDisable(GL_BLEND); @@ -704,15 +752,24 @@ FtglFontTextRenderer::drawTextAtViewportCoordsInternal(const DepthTestEnum depth return; } +// const bool tooSmallFlag = (font->FaceSize() < s_tooSmallFontSize); +// annotationText.setFontTooSmallWhenLastDrawn(tooSmallFlag); + m_depthTestingStatus = depthTesting; + if (annotationText.getLineWidthPercentage() <= 0.0f) { + annotationText.convertObsoleteLineWidthPixelsToPercentageWidth(m_viewportHeight); + } + const double lineThicknessForViewportHeight = getLineWidthFromPercentageHeight(annotationText.getLineWidthPercentage()); + TextStringGroup tsg(annotationText, font, viewportX, viewportY, viewportZ, - annotationText.getRotationAngle()); - //tsg.print(); + annotationText.getRotationAngle(), + lineThicknessForViewportHeight); + /* tsg.print(); */ drawTextAtViewportCoordinatesInternal(annotationText, tsg); } @@ -733,6 +790,8 @@ FtglFontTextRenderer::drawTextAtViewportCoordsInternal(const DepthTestEnum depth * Viewport Y-coordinate. * @param viewportZ * Viewport Z-coordinate. + * @param viewportWidth + * Width of the viewport needed for percentage height text. * @param viewportHeight * Height of the viewport needed for percentage height text. * @param bottomLeftOut @@ -749,6 +808,7 @@ FtglFontTextRenderer::getBoundsForTextAtViewportCoords(const AnnotationText& ann const double viewportX, const double viewportY, const double viewportZ, + const double viewportWidth, const double viewportHeight, double bottomLeftOut[3], double bottomRightOut[3], @@ -756,6 +816,7 @@ FtglFontTextRenderer::getBoundsForTextAtViewportCoords(const AnnotationText& ann double topLeftOut[3]) { setViewportHeight(); + m_viewportWidth = viewportWidth; m_viewportHeight = viewportHeight; FTFont* font = getFont(annotationText, false); @@ -763,12 +824,21 @@ FtglFontTextRenderer::getBoundsForTextAtViewportCoords(const AnnotationText& ann return; } + BrainOpenGL::testForOpenGLError("At begininng of FtglFontTextRenderer::getBoundsForTextAtViewportCoords() while " + "drawing text " + annotationText.getText()); + + if (annotationText.getLineWidthPercentage() <= 0.0f) { + annotationText.convertObsoleteLineWidthPixelsToPercentageWidth(m_viewportHeight); + } + double lineThicknessForViewportHeight = getLineWidthFromPercentageHeight(annotationText.getLineWidthPercentage()); + TextStringGroup textStringGroup(annotationText, font, viewportX, viewportY, viewportZ, - annotationText.getRotationAngle()); + annotationText.getRotationAngle(), + lineThicknessForViewportHeight); double rotationPointXYZ[3]; textStringGroup.getViewportBounds(s_textMarginSize, @@ -777,6 +847,46 @@ FtglFontTextRenderer::getBoundsForTextAtViewportCoords(const AnnotationText& ann topRightOut, topLeftOut, rotationPointXYZ); + + std::unique_ptr openglError = GraphicsUtilitiesOpenGL::getOpenGLError(); + if (openglError) { + AString sizeMessage; + sizeMessage.appendWithNewLine("At end of FtglFontTextRenderer::getBoundsForTextAtViewportCoords()."); + sizeMessage.appendWithNewLine(" Error may be caused by font size that is too small."); + sizeMessage.appendWithNewLine(" Error may also occur with particular characters in a font."); + sizeMessage.appendWithNewLine(" Text that caused error: \"" + annotationText.getText() + "\""); + + switch (annotationText.getFontSizeType()) { + case AnnotationTextFontSizeTypeEnum::PERCENTAGE_OF_VIEWPORT_HEIGHT: + { + const float percentSize = annotationText.getFontPercentViewportSize(); + const float fontHeight = (percentSize / 100.0f) * viewportHeight; + sizeMessage.appendWithNewLine(" Percent of viewport height=" + + AString::number(percentSize, 'f', 2) + + "%, viewport height=" + AString::number(viewportHeight) + + " approximage font height=" + AString::number(fontHeight, 'f', 2)); + } + break; + case AnnotationTextFontSizeTypeEnum::PERCENTAGE_OF_VIEWPORT_WIDTH: + { + const float percentSize = annotationText.getFontPercentViewportSize(); + const float fontHeight = (percentSize / 100.0f) * viewportWidth; + sizeMessage.appendWithNewLine(" Percent of viewport width=" + + AString::number(percentSize, 'f', 2) + + "%, viewport width=" + AString::number(viewportWidth) + + " approximage font height=" + AString::number(fontHeight, 'f', 2)); + } + break; + case AnnotationTextFontSizeTypeEnum::POINTS: + sizeMessage.appendWithNewLine(" Font Size=" + + AnnotationTextFontNameEnum::toGuiName(annotationText.getFont())); + break; + } + + sizeMessage.appendWithNewLine(openglError->getVerboseDescription()); + + CaretLogWarning(sizeMessage); + } } @@ -849,6 +959,7 @@ FtglFontTextRenderer::setViewportHeight() glGetIntegerv(GL_VIEWPORT, viewport); + m_viewportWidth = viewport[2]; m_viewportHeight = viewport[3]; } @@ -860,6 +971,8 @@ FtglFontTextRenderer::setViewportHeight() * * @param annotationText * Text for width and height estimation. + * @param viewportWidth + * Width of the viewport needed for percentage height text. * @param viewportHeight * Height of the viewport needed for percentage height text. * @param widthOut @@ -869,15 +982,33 @@ FtglFontTextRenderer::setViewportHeight() */ void FtglFontTextRenderer::getTextWidthHeightInPixels(const AnnotationText& annotationText, + const double viewportWidth, const double viewportHeight, double& widthOut, double& heightOut) { setViewportHeight(); + m_viewportWidth = viewportWidth; m_viewportHeight = viewportHeight; double bottomLeft[3], bottomRight[3], topRight[3], topLeft[3]; - getBoundsForTextAtViewportCoords(annotationText, 0.0, 0.0, 0.0, viewportHeight, bottomLeft, bottomRight, topRight, topLeft); + if (annotationText.getRotationAngle() != 0.0) { + /* + * Any rotation must be removed as the rotation is applied to the viewport postions. + * For example, if the text is rotated -90 degrees, the top-right and top-left will + * be on the left and bottom-right and bottom-left will be on the right + */ + std::unique_ptr annotation = std::unique_ptr(annotationText.clone()); + AnnotationText* textCopy = dynamic_cast(annotation.get()); + CaretAssert(textCopy); + textCopy->setRotationAngle(0.0); + getBoundsForTextAtViewportCoords(*textCopy, 0.0, 0.0, 0.0, + viewportWidth, viewportHeight, bottomLeft, bottomRight, topRight, topLeft); + } + else { + getBoundsForTextAtViewportCoords(annotationText, 0.0, 0.0, 0.0, + viewportWidth, viewportHeight, bottomLeft, bottomRight, topRight, topLeft); + } widthOut = MathFunctions::distance3D(bottomLeft, bottomRight); heightOut = MathFunctions::distance3D(topLeft, bottomLeft); @@ -1011,10 +1142,13 @@ FtglFontTextRenderer::FontData::FontData() * * @param annotationText * Annotation Text that is to be drawn. + * @param viewportWidth + * Width of the viewport in which text is drawn. * @param viewportHeight * Height of the viewport in which text is drawn. */ FtglFontTextRenderer::FontData::FontData(const AnnotationText& annotationText, + const int32_t viewportWidth, const int32_t viewportHeight) { m_valid = false; @@ -1065,7 +1199,7 @@ FtglFontTextRenderer::FontData::FontData(const AnnotationText& annotationText, /* * Font size successful ? */ - const int32_t fontSizePoints = annotationText.getFontSizeForDrawing(viewportHeight); + const int32_t fontSizePoints = annotationText.getFontSizeForDrawing(viewportWidth, viewportHeight); if (m_font->FaceSize(fontSizePoints)) { m_valid = true; @@ -1292,10 +1426,6 @@ m_stringGlyphsMaxY(0.0) * and https://en.wikipedia.org/wiki/Serif */ advanceValue = font->Advance(theWideCharStr.c_str(), 1); -// const std::wstring nextWideCharStr = textString.mid(i + 1, 1).toStdWString(); -// const wchar_t nextWideChar = nextWideCharStr.at(0); -// advanceValue = font->Advance(theWideChar, -// nextWideChar); } TextCharacter* tc = new TextCharacter(theWideChar, @@ -1575,13 +1705,16 @@ FtglFontTextRenderer::TextString::getTextBoundsInViewportCoordinates(double& vie * Z-coordinate in the viewport. * @param rotationAngle * Rotation angle for the text. + * @param lineThicknessForViewportHeight + * Line thickness adjusted for viewport height. */ FtglFontTextRenderer::TextStringGroup::TextStringGroup(const AnnotationText& annotationText, FTFont* font, const double viewportX, const double viewportY, const double viewportZ, - const double rotationAngle) + const double rotationAngle, + const double lineThicknessForViewportHeight) : m_annotationText(annotationText), m_font(font), m_viewportX(viewportX), @@ -1589,7 +1722,6 @@ m_viewportY(viewportY), m_viewportZ(viewportZ), m_rotationAngle(rotationAngle), m_underlineThickness(0.0), -m_outlineThickness(0.0), m_viewportBoundsMinX(0.0), m_viewportBoundsMaxX(0.0), m_viewportBoundsMinY(0.0), @@ -1598,6 +1730,14 @@ m_viewportBoundsMaxY(0.0) #ifdef HAVE_FREETYPE CaretAssert(font); + if (annotationText.getText().isEmpty()) { + m_viewportBoundsMinX = m_viewportX; + m_viewportBoundsMaxX = m_viewportY; + m_viewportBoundsMinY = m_viewportX; + m_viewportBoundsMaxY = m_viewportY; + return; + } + /* * The underline for text is scaled with the size of the font * Underline is drawn anytime thickness is greater than zero @@ -1614,10 +1754,9 @@ m_viewportBoundsMaxY(0.0) * Outline is drawn anytime thickness is greater than zero * and is drawn using the foreground color */ + float outlineThickness = 0.0; if (annotationText.getLineColor() != CaretColorEnum::NONE) { - if (annotationText.getOrientation() == AnnotationTextOrientationEnum::HORIZONTAL) { - m_outlineThickness = m_annotationText.getLineWidth(); - } + outlineThickness = lineThicknessForViewportHeight; } /* @@ -1632,7 +1771,7 @@ m_viewportBoundsMaxY(0.0) TextString* ts = new TextString(textList.at(i), annotationText.getOrientation(), m_underlineThickness, - m_outlineThickness, + outlineThickness, font); m_textStrings.push_back(ts); } @@ -1823,7 +1962,7 @@ FtglFontTextRenderer::TextStringGroup::initializeTextPositions() const double offsetY1 = prevTextString->m_stringGlyphsMinY; const double offsetY2 = -(s_textMarginSize * 2.0); const double offsetY3 = -textString->m_stringGlyphsMaxY; - const double offsetY4 = 0.0; //-m_underlineThickness; + const double offsetY4 = 0.0; const double offsetY = (offsetY1 + offsetY2 + offsetY3 + offsetY4); y += offsetY; diff --git a/src/Brain/FtglFontTextRenderer.h b/src/Brain/FtglFontTextRenderer.h index f12ad2ae0fd370ef0efc7eaa448410fd632f6b94..938670ddb39b4a8f0a67b556dbb89c47e701be17 100644 --- a/src/Brain/FtglFontTextRenderer.h +++ b/src/Brain/FtglFontTextRenderer.h @@ -56,6 +56,7 @@ namespace caret { const AnnotationText& annotationText); virtual void getTextWidthHeightInPixels(const AnnotationText& annotationText, + const double viewportWidth, const double viewportHeight, double& widthOut, double& heightOut); @@ -65,6 +66,7 @@ namespace caret { const double viewportX, const double viewportY, const double viewportZ, + const double viewportWidth, const double viewportHeight, double bottomLeftOut[3], double bottomRightOut[3], @@ -114,11 +116,15 @@ namespace caret { const float extraSpaceX, const float extraSpaceY); + double getLineWidthFromPercentageHeight(const double percentageHeight) const; + + class FontData { public: FontData(); FontData(const AnnotationText& annotationText, + const int32_t viewportWidth, const int32_t viewportHeight); ~FontData(); @@ -221,11 +227,12 @@ namespace caret { class TextStringGroup { public: TextStringGroup(const AnnotationText& annotationText, - FTFont* font, - const double viewportX, - const double viewportY, - const double viewportZ, - const double rotationAngle); + FTFont* font, + const double viewportX, + const double viewportY, + const double viewportZ, + const double rotationAngle, + const double lineThicknessForViewportHeight); ~TextStringGroup(); @@ -254,8 +261,6 @@ namespace caret { double m_underlineThickness; - double m_outlineThickness; - /* * Bounds relative to origin of first character */ @@ -316,9 +321,16 @@ namespace caret { /** Depth testing enabled status */ DepthTestEnum m_depthTestingStatus; + /** Width of the viewport */ + int32_t m_viewportWidth; + /** Height of the viewport */ int32_t m_viewportHeight; + float m_lineWidthMinimum = 1.0f; + + float m_lineWidthMaximum = 5.0f; + static const double s_textMarginSize; }; diff --git a/src/Brain/GapsAndMargins.cxx b/src/Brain/GapsAndMargins.cxx index 6fc16ab148a7337b0b4f7eca13267735710de592..f2d9322fe59e57d63d513d94de5f4ac2e013c56f 100644 --- a/src/Brain/GapsAndMargins.cxx +++ b/src/Brain/GapsAndMargins.cxx @@ -23,7 +23,10 @@ #include "GapsAndMargins.h" #undef __GAPS_AND_MARGINS_DECLARE__ +#include "BrowserTabContent.h" #include "CaretAssert.h" +#include "EventBrowserTabNew.h" +#include "EventManager.h" #include "SceneClass.h" #include "SceneClassAssistant.h" @@ -84,6 +87,12 @@ GapsAndMargins::GapsAndMargins() m_volumeMontageVerticalGaps, BrainConstants::MAXIMUM_NUMBER_OF_BROWSER_WINDOWS, 0.0); + + /* + * Use processed event listener because we need to know the index + * of the tab that was created + */ + EventManager::get()->addProcessedEventListener(this, EventTypeEnum::EVENT_BROWSER_TAB_NEW); } /** @@ -91,9 +100,40 @@ GapsAndMargins::GapsAndMargins() */ GapsAndMargins::~GapsAndMargins() { + EventManager::get()->removeAllEventsFromListener(this); + delete m_sceneAssistant; } +/** + * Receive an event. + * + * @param event + * The event that the receive can respond to. + */ +void +GapsAndMargins::receiveEvent(Event* event) +{ + if (event->getEventType() == EventTypeEnum::EVENT_BROWSER_TAB_NEW) { + EventBrowserTabNew* newTabEvent = dynamic_cast(event); + CaretAssert(newTabEvent); + + const BrowserTabContent* tab = newTabEvent->getBrowserTab(); + CaretAssert(tab); + if (tab != NULL) { + const int32_t tabIndex = tab->getTabNumber(); + CaretAssertArrayIndex(m_tabMarginsLeft, BrainConstants::MAXIMUM_NUMBER_OF_BROWSER_TABS, tabIndex); + CaretAssertArrayIndex(m_tabMarginsRight, BrainConstants::MAXIMUM_NUMBER_OF_BROWSER_TABS, tabIndex); + CaretAssertArrayIndex(m_tabMarginsBottom, BrainConstants::MAXIMUM_NUMBER_OF_BROWSER_TABS, tabIndex); + CaretAssertArrayIndex(m_tabMarginsTop, BrainConstants::MAXIMUM_NUMBER_OF_BROWSER_TABS, tabIndex); + m_tabMarginsLeft[tabIndex] = 0.0; + m_tabMarginsRight[tabIndex] = 0.0; + m_tabMarginsBottom[tabIndex] = 0.0; + m_tabMarginsTop[tabIndex] = 0.0; + } + } +} + /** * Get a description of this object's content. * @return String describing this object's content. diff --git a/src/Brain/GapsAndMargins.h b/src/Brain/GapsAndMargins.h index 03b440ffa7616dc760cf48982485479016583ecc..577b04f8e85369e6c3d1e9ba7db8bb85283f9aca 100644 --- a/src/Brain/GapsAndMargins.h +++ b/src/Brain/GapsAndMargins.h @@ -23,18 +23,21 @@ #include "BrainConstants.h" #include "CaretObject.h" +#include "EventListenerInterface.h" #include "SceneableInterface.h" namespace caret { class SceneClassAssistant; - class GapsAndMargins : public CaretObject, public SceneableInterface { + class GapsAndMargins : public CaretObject, public EventListenerInterface, public SceneableInterface { public: GapsAndMargins(); virtual ~GapsAndMargins(); + virtual void receiveEvent(Event* event) override; + float getMarginLeftForTab(const int32_t tabIndex) const; float getMarginRightForTab(const int32_t tabIndex) const; diff --git a/src/Brain/IdentificationManager.cxx b/src/Brain/IdentificationManager.cxx index a7fd967626f62ed71acc1089aa7141e3490640b9..7c032dad3d8c22c07225ef1a494f0d4dd8f153b3 100644 --- a/src/Brain/IdentificationManager.cxx +++ b/src/Brain/IdentificationManager.cxx @@ -27,6 +27,7 @@ #include "BrowserTabContent.h" #include "CaretAssert.h" #include "CaretLogger.h" +#include "CaretPreferences.h" #include "EventBrowserTabGetAll.h" #include "EventManager.h" #include "IdentifiedItemNode.h" @@ -35,6 +36,7 @@ #include "SceneClassAssistant.h" #include "SceneClass.h" #include "ScenePrimitive.h" +#include "SessionManager.h" using namespace caret; @@ -47,8 +49,11 @@ using namespace caret; /** * Constructor. + * + * @param caretPreferences + * The caret preferencers */ -IdentificationManager::IdentificationManager() +IdentificationManager::IdentificationManager(const CaretPreferences* caretPreferences) : SceneableInterface() { m_sceneAssistant = new SceneClassAssistant(); @@ -58,6 +63,8 @@ IdentificationManager::IdentificationManager() m_identificationContralateralSymbolColor = CaretColorEnum::LIME; m_identifcationSymbolSize = 3.0; m_identifcationMostRecentSymbolSize = 5.0; + m_showSurfaceIdentificationSymbols = caretPreferences->isShowSurfaceIdentificationSymbols(); + m_showVolumeIdentificationSymbols = caretPreferences->isShowVolumeIdentificationSymbols(); m_sceneAssistant->add("m_contralateralIdentificationEnabled", &m_contralateralIdentificationEnabled); @@ -74,6 +81,11 @@ IdentificationManager::IdentificationManager() m_sceneAssistant->add("m_identificationContralateralSymbolColor", &m_identificationContralateralSymbolColor); + m_sceneAssistant->add("m_showSurfaceIdentificationSymbols", + &m_showSurfaceIdentificationSymbols); + m_sceneAssistant->add("m_showVolumeIdentificationSymbols", + &m_showVolumeIdentificationSymbols); + removeAllIdentifiedItems(); } @@ -491,6 +503,47 @@ IdentificationManager::setIdentificationContralateralSymbolColor(const CaretColo m_identificationContralateralSymbolColor = color; } +/** + * @return show surface identification symbols + */ +bool +IdentificationManager::isShowSurfaceIdentificationSymbols() const +{ + return m_showSurfaceIdentificationSymbols; +} + +/** + * Set show surface identification symbols + * + * @param showSurfaceIdentificationSymbols + * New value for show surface identification symbols + */ +void +IdentificationManager::setShowSurfaceIdentificationSymbols(const bool showSurfaceIdentificationSymbols) +{ + m_showSurfaceIdentificationSymbols = showSurfaceIdentificationSymbols; +} + +/** + * @return show volume identification symbols + */ +bool +IdentificationManager::isShowVolumeIdentificationSymbols() const +{ + return m_showVolumeIdentificationSymbols; +} + +/** + * Set show volume identification symbols + * + * @param showVolumeIdentificationSymbols + * New value for show volume identification symbols + */ +void +IdentificationManager::setShowVolumeIdentificationSymbols(const bool showVolumeIdentificationSymbols) +{ + m_showVolumeIdentificationSymbols = showVolumeIdentificationSymbols; +} /** * Create a scene for an instance of a class. diff --git a/src/Brain/IdentificationManager.h b/src/Brain/IdentificationManager.h index 4ef9c95b1b3e4e73b91ee2b4d035c621fc935707..9e6b3ea51df4dcd73b9ef72eb4b11b1eb05feabe 100644 --- a/src/Brain/IdentificationManager.h +++ b/src/Brain/IdentificationManager.h @@ -29,6 +29,7 @@ namespace caret { + class CaretPreferences; class IdentifiedItem; class IdentifiedItemNode; class IdentifiedItemVoxel; @@ -37,7 +38,7 @@ namespace caret { class IdentificationManager : public SceneableInterface { public: - IdentificationManager(); + IdentificationManager(const CaretPreferences* caretPreferences); virtual ~IdentificationManager(); @@ -82,6 +83,14 @@ namespace caret { void setIdentificationContralateralSymbolColor(const CaretColorEnum::Enum color); + bool isShowSurfaceIdentificationSymbols() const; + + void setShowSurfaceIdentificationSymbols(const bool showSurfaceIdentificationSymbols); + + bool isShowVolumeIdentificationSymbols() const; + + void setShowVolumeIdentificationSymbols(const bool showVolumeIdentificationSymbols); + virtual SceneClass* saveToScene(const SceneAttributes* sceneAttributes, const AString& instanceName); @@ -120,7 +129,12 @@ namespace caret { CaretColorEnum::Enum m_identificationSymbolColor; CaretColorEnum::Enum m_identificationContralateralSymbolColor; + + /** show surface identification symbols*/ + bool m_showSurfaceIdentificationSymbols; + /** show volume identification symbols*/ + bool m_showVolumeIdentificationSymbols; }; #ifdef __IDENTIFICATION_MANAGER_DECLARE__ diff --git a/src/Brain/IdentificationStringBuilder.cxx b/src/Brain/IdentificationStringBuilder.cxx index 0a34799d3fd8a24160c08849a4a6ad78e4554a5d..fe3d0916f02389b0e14f5f6aa91a2fdf38c0f52f 100644 --- a/src/Brain/IdentificationStringBuilder.cxx +++ b/src/Brain/IdentificationStringBuilder.cxx @@ -18,7 +18,6 @@ */ /*LICENSE_END*/ - #include "IdentificationStringBuilder.h" using namespace caret; diff --git a/src/Brain/IdentificationTextGenerator.cxx b/src/Brain/IdentificationTextGenerator.cxx index 660eb19e884304b4a25cbd4e5953a0e65ebc2be6..90925718daf1a0b0315e815bb5d5000c86081d43 100644 --- a/src/Brain/IdentificationTextGenerator.cxx +++ b/src/Brain/IdentificationTextGenerator.cxx @@ -32,7 +32,12 @@ #include "ChartDataCartesian.h" #include "ChartDataSource.h" #include "ChartModelDataSeries.h" +#include "ChartTwoDataCartesian.h" #include "ChartableMatrixInterface.h" +#include "ChartableTwoFileDelegate.h" +#include "ChartableTwoFileHistogramChart.h" +#include "ChartableTwoFileLineSeriesChart.h" +#include "ChartableTwoFileMatrixChart.h" #include "CiftiMappableConnectivityMatrixDataFile.h" #include "CiftiMappableDataFile.h" #include "CaretVolumeExtension.h" @@ -42,7 +47,9 @@ #include "FociFile.h" #include "Focus.h" #include "GiftiLabel.h" +#include "Histogram.h" #include "ImageFile.h" +#include "MapFileDataSelector.h" #include "OverlaySet.h" #include "SelectionItemBorderSurface.h" #include "SelectionItemChartDataSeries.h" @@ -50,6 +57,9 @@ #include "SelectionItemChartMatrix.h" #include "SelectionItemCiftiConnectivityMatrixRowColumn.h" #include "SelectionItemChartTimeSeries.h" +#include "SelectionItemChartTwoHistogram.h" +#include "SelectionItemChartTwoLineSeries.h" +#include "SelectionItemChartTwoMatrix.h" #include "SelectionItemFocusSurface.h" #include "SelectionItemFocusVolume.h" #include "SelectionItemImage.h" @@ -143,6 +153,15 @@ IdentificationTextGenerator::createIdentificationText(const SelectionManager* id this->generateCiftiConnectivityMatrixIdentificationText(idText, idManager->getCiftiConnectivityMatrixRowColumnIdentification()); + this->generateChartTwoHistogramIdentificationText(idText, + idManager->getChartTwoHistogramIdentification()); + + this->generateChartTwoLineSeriesIdentificationText(idText, + idManager->getChartTwoLineSeriesIdentification()); + + this->generateChartTwoMatrixIdentificationText(idText, + idManager->getChartTwoMatrixIdentification()); + this->generateImageIdentificationText(idText, idManager->getImageIdentification()); @@ -384,14 +403,6 @@ IdentificationTextGenerator::generateVolumeIdentificationText(IdentificationStri getMapIndicesOfFileUsedInOverlays(ciftiFile, mapIndices); } -// /* -// * Limit dense scalar and data series to maps selected in the overlay. -// */ -// if ((ciftiFile->getDataFileType() == DataFileTypeEnum::CONNECTIVITY_DENSE_SCALAR) -// || (ciftiFile->getDataFileType() == DataFileTypeEnum::CONNECTIVITY_DENSE_TIME_SERIES)) { -// getMapIndicesOfFileUsedInOverlays(ciftiFile, -// mapIndices); -// } AString textValue; int64_t voxelIJK[3]; @@ -456,26 +467,6 @@ IdentificationTextGenerator::generateSurfaceIdentificationText(IdentificationStr const BrainStructure* brainStructure = surface->getBrainStructure(); CaretAssert(brainStructure); -// std::vector allCiftiMappableDataFiles; -// brain->getAllCiftiMappableDataFiles(allCiftiMappableDataFiles); -// for (std::vector::iterator ciftiMapIter = allCiftiMappableDataFiles.begin(); -// ciftiMapIter != allCiftiMappableDataFiles.end(); -// ciftiMapIter++) { -// const CiftiMappableDataFile* cmdf = *ciftiMapIter; -// if (cmdf->isEmpty() == false) { -// const int numMaps = cmdf->getNumberOfMaps(); -// for (int32_t iMap = 0; iMap < numMaps; iMap++) { -// AString textValue; -// if (cmdf->getMapSurfaceNodeValue(iMap, surface->getStructure(), nodeNumber, surface->getNumberOfNodes(), textValue)) { -// AString boldText = (DataFileTypeEnum::toOverlayTypeName(cmdf->getDataFileType()) -// + " " -// + cmdf->getFileNameNoPath()); -// idText.addLine(true, boldText, textValue); -// } -// } -// } -// } - std::vector allCiftiMappableDataFiles; brain->getAllCiftiMappableDataFiles(allCiftiMappableDataFiles); for (std::vector::iterator ciftiMapIter = allCiftiMappableDataFiles.begin(); @@ -574,37 +565,6 @@ IdentificationTextGenerator::generateSurfaceIdentificationText(IdentificationStr boldText, textValue); } - -// const CiftiMappableConnectivityMatrixDataFile* connCifti = dynamic_cast(cmdf); -// if (cmdf->isEmpty() == false) { -// const int numMaps = cmdf->getNumberOfMaps(); -// if (numMaps > 0) { -// if (connCifti != NULL) { -// AString textValue; -// const int32_t mapIndex = 0; -// if (cmdf->getMapSurfaceNodeValue(mapIndex, surface->getStructure(), nodeNumber, surface->getNumberOfNodes(), textValue)) { -// AString boldText = (DataFileTypeEnum::toOverlayTypeName(cmdf->getDataFileType()) -// + " " -// + cmdf->getFileNameNoPath()); -// idText.addLine(true, boldText, textValue); -// } -// } -// else { -// AString boldText = (DataFileTypeEnum::toOverlayTypeName(cmdf->getDataFileType()) -// + " " -// + cmdf->getFileNameNoPath()); -// std::vector nodeData; -// if (cmdf->getSeriesDataForSurfaceNode(surface->getStructure(), -// nodeNumber, -// nodeData)) { -// for (int32_t iMap = 0; iMap < numMaps; iMap++) { -// AString textValue = AString::number(nodeData[iMap]); -// idText.addLine(true, boldText, textValue); -// } -// } -// } -// } -// } } @@ -697,7 +657,6 @@ IdentificationTextGenerator::generateChartDataSeriesIdentificationText(Identific const SelectionItemChartDataSeries* idChartDataSeries) const { if (idChartDataSeries->isValid()) { - //const ChartModelDataSeries* chartModelDataSeries = idChartDataSeries->getChartModelDataSeries(); const ChartDataCartesian* chartDataCartesian = idChartDataSeries->getChartDataCartesian(); const ChartDataSource* chartDataSource = chartDataCartesian->getChartDataSource(); @@ -772,6 +731,168 @@ IdentificationTextGenerator::generateChartMatrixIdentificationText(Identificatio } } +/** + * Generate identification text for a chart two histogram. + * + * @param idText + * String builder for identification text. + * @param idChartTwoHistogram + * Information for selected chart two histogram. + */ +void +IdentificationTextGenerator::generateChartTwoHistogramIdentificationText(IdentificationStringBuilder& idText, + const SelectionItemChartTwoHistogram* idChartTwoHistogram) const +{ + const int32_t mapIndex = idChartTwoHistogram->getMapIndex(); + const int32_t bucketIndex = idChartTwoHistogram->getBucketIndex(); + const bool allMapsFlag = idChartTwoHistogram->isAllMapsSelected(); + + if (idChartTwoHistogram->isValid()) { + ChartableTwoFileHistogramChart* fileHistogramChart = idChartTwoHistogram->getFileHistogramChart(); + CaretAssert(fileHistogramChart); + CaretMappableDataFile* mapFile = fileHistogramChart->getCaretMappableDataFile(); + CaretAssert(mapFile); + + { + ChartableTwoFileHistogramChart* chartingDelegate = mapFile->getChartingDelegate()->getHistogramCharting(); + CaretAssert(chartingDelegate); + const Histogram* histogram = chartingDelegate->getHistogramForChartDrawing(mapIndex, + allMapsFlag); + CaretAssert(histogram); + + float bucketValue = 0.0; + float bucketHeight = 0.0; + if (histogram->getHistogramDisplayBucketDataValueAndHeight(bucketIndex, bucketValue, bucketHeight)) { + AString boldText("Histogram"); + idText.addLine(false, + boldText, + mapFile->getFileNameNoPath()); + + idText.addLine(true, + "Bucket Index", + (AString::number(bucketIndex))); + + idText.addLine(true, + "Data Value at Bucket", + (AString::number(bucketValue))); + + const int64_t bucketHeightInteger = static_cast(bucketHeight); + idText.addLine(true, + "Bucket Count", + (AString::number(bucketHeightInteger))); + } + } + } +} + +/** + * Generate identification text for a chart two line-series. + * + * @param idText + * String builder for identification text. + * @param idChartTwoLineSeries + * Information for selected chart two line-series. + */ +void +IdentificationTextGenerator::generateChartTwoLineSeriesIdentificationText(IdentificationStringBuilder& idText, + const SelectionItemChartTwoLineSeries* idChartTwoLineSeries) const +{ + if (idChartTwoLineSeries->isValid()) { + const ChartableTwoFileLineSeriesChart* fileLineSeriesChart = idChartTwoLineSeries->getFileLineSeriesChart(); + CaretAssert(fileLineSeriesChart); + const CaretMappableDataFile* mapFile = fileLineSeriesChart->getCaretMappableDataFile(); + CaretAssert(mapFile); + const ChartTwoDataCartesian* cartesianData = idChartTwoLineSeries->getChartTwoCartesianData(); + CaretAssert(cartesianData); + const MapFileDataSelector* mapFileDataSelector = cartesianData->getMapFileDataSelector(); + CaretAssert(mapFileDataSelector); + + const int32_t primitiveIndex = idChartTwoLineSeries->getLineSegmentIndex(); + + AString boldText("Line Chart"); + idText.addLine(false, + boldText, + mapFile->getFileNameNoPath()); + + idText.addLine(true, + "Line Segment Index", + (AString::number(primitiveIndex))); + + generateMapFileSelectorText(idText, + mapFileDataSelector); + } +} + +/** + * Generate identification text for a chart two matrix. + * + * @param idText + * String builder for identification text. + * @param idChartTwoMatrix + * Information for selected chart two matrix. + */ +void +IdentificationTextGenerator::generateChartTwoMatrixIdentificationText(IdentificationStringBuilder& idText, + const SelectionItemChartTwoMatrix* idChartTwoMatrix) const +{ + if (idChartTwoMatrix->isValid()) { + const ChartableTwoFileMatrixChart* matrixChart = idChartTwoMatrix->getFileMatrixChart(); + CaretAssert(matrixChart); + + const int32_t rowIndex = idChartTwoMatrix->getRowIndex(); + const int32_t colIndex = idChartTwoMatrix->getColumnIndex(); + + const CaretMappableDataFile* mapFile = matrixChart->getCaretMappableDataFile(); + CaretAssert(mapFile); + + const bool newIdFlag = true; + if (newIdFlag) { + AString boldText("MATRIX "); + idText.addLine(false, + boldText, + mapFile->getFileNameNoPath()); + if ((rowIndex >= 0) + && (matrixChart->hasRowSelection())) { + const AString rowName = matrixChart->getRowName(rowIndex); + if ( ! rowName.isEmpty()) { + idText.addLine(true, + ("Row " + AString::number(rowIndex + 1)), + rowName); + } + } + if ((colIndex >= 0) + && (matrixChart->hasColumnSelection())) { + const AString colName = matrixChart->getColumnName(colIndex); + if ( ! colName.isEmpty()) { + idText.addLine(true, + ("Column " + AString::number(colIndex + 1)), + colName); + } + } + } + else { + AString boldText("MATRIX ROW/COLUMN"); + idText.addLine(false, + boldText, + mapFile->getFileNameNoPath()); + + const CiftiMappableConnectivityMatrixDataFile* matrixFile = dynamic_cast(mapFile); + if (rowIndex >= 0) { + const AString rowName = (matrixFile != NULL) ? (" " + matrixFile->getRowName(rowIndex + 1)) : ""; + idText.addLine(true, + "Row", + (AString::number(rowIndex + 1) + rowName)); + } + if (colIndex >= 0) { + const AString colName = (matrixFile != NULL) ? (" " + matrixFile->getColumnName(colIndex + 1)) : ""; + idText.addLine(true, + "Column", + (AString::number(colIndex + 1) + colName)); + } + } + } +} + /** * Generate identification text for a CIFTI Connectivity Matrix Row/Column * @param idText @@ -793,18 +914,18 @@ IdentificationTextGenerator::generateCiftiConnectivityMatrixIdentificationText(I boldText, connMatrixFile->getFileNameNoPath()); - AString rowName = ""; - AString colName = ""; + AString rowName = " "; + AString colName = " "; bool validData = true; if (validData) { if (rowIndex >= 0) { idText.addLine(true, - ("Row " + AString::number(rowIndex + 1)), + ("Row " + AString::number(rowIndex + CiftiMappableDataFile::getCiftiFileRowColumnIndexBaseForGUI())), rowName); } if (colIndex >= 0) { idText.addLine(true, - ("Column " + AString::number(colIndex + 1)), + ("Column " + AString::number(colIndex + CiftiMappableDataFile::getCiftiFileRowColumnIndexBaseForGUI())), colName); } } @@ -892,6 +1013,107 @@ IdentificationTextGenerator::generateChartDataSourceText(IdentificationStringBui } } +/** + * Generate text for a map file data selector. + * @param idText + * String builder for identification text. + * @param mapFileDataSelector + * The map file data selector. + */ +void +IdentificationTextGenerator::generateMapFileSelectorText(IdentificationStringBuilder& idText, + const MapFileDataSelector* mapFileDataSelector) const +{ + + switch (mapFileDataSelector->getDataSelectionType()) { + case MapFileDataSelector::DataSelectionType::INVALID: + break; + case MapFileDataSelector::DataSelectionType::COLUMN_DATA: + { + CaretMappableDataFile* mapFile = NULL; + AString mapFileName; + int32_t columnIndex = -1; + mapFileDataSelector->getColumnIndex(mapFile, + mapFileName, + columnIndex); + + idText.addLine(true, + "Column File", + mapFileName); + idText.addLine(true, + "Column Index", + AString::number(columnIndex)); + } + break; + case MapFileDataSelector::DataSelectionType::ROW_DATA: + { + CaretMappableDataFile* mapFile = NULL; + AString mapFileName; + int32_t rowIndex = -1; + mapFileDataSelector->getRowIndex(mapFile, + mapFileName, + rowIndex); + + idText.addLine(true, + "Row File", + mapFileName); + idText.addLine(true, + "Row Index", + AString::number(rowIndex)); + } + break; + case MapFileDataSelector::DataSelectionType::SURFACE_VERTEX: + { + StructureEnum::Enum structure = StructureEnum::INVALID; + int32_t numberOfVertices = 0; + int32_t vertexIndex = -1; + mapFileDataSelector->getSurfaceVertex(structure, + numberOfVertices, + vertexIndex); + + if ((structure != StructureEnum::INVALID) + && (vertexIndex >= 0)) { + idText.addLine(true, + "Structure", + StructureEnum::toGuiName(structure)); + idText.addLine(true, + "Vertex Index", + AString::number(vertexIndex)); + } + } + break; + case MapFileDataSelector::DataSelectionType::SURFACE_VERTICES_AVERAGE: + { + StructureEnum::Enum structure = StructureEnum::INVALID; + int32_t numberOfVertices = 0; + std::vector vertexIndices; + mapFileDataSelector->getSurfaceVertexAverage(structure, + numberOfVertices, + vertexIndices); + + const int32_t averageCount = static_cast(vertexIndices.size()); + if ((structure != StructureEnum::INVALID) + && (averageCount > 0)) { + idText.addLine(true, + "Structure", + StructureEnum::toGuiName(structure)); + idText.addLine(true, + "Vertex Average Count", + AString::number(averageCount)); + } + } + break; + case MapFileDataSelector::DataSelectionType::VOLUME_XYZ: + { + float voxelXYZ[3]; + mapFileDataSelector->getVolumeVoxelXYZ(voxelXYZ); + idText.addLine(true, + "Voxel XYZ", + AString::fromNumbers(voxelXYZ, 3, ",")); + } + break; + } +} /** * Generate identification text for a time series chart. @@ -905,7 +1127,6 @@ IdentificationTextGenerator::generateChartTimeSeriesIdentificationText(Identific const SelectionItemChartTimeSeries* idChartTimeSeries) const { if (idChartTimeSeries->isValid()) { - //const ChartModelDataSeries* chartModelDataSeries = idChartDataSeries->getChartModelDataSeries(); const ChartDataCartesian* chartDataCartesian = idChartTimeSeries->getChartDataCartesian(); const ChartDataSource* chartDataSource = chartDataCartesian->getChartDataSource(); diff --git a/src/Brain/IdentificationTextGenerator.h b/src/Brain/IdentificationTextGenerator.h index 14f8a79be9820583a25ba4d9cd882642999e320b..4fa605adde97d3b0d0afd5e093019421e265069d 100644 --- a/src/Brain/IdentificationTextGenerator.h +++ b/src/Brain/IdentificationTextGenerator.h @@ -30,10 +30,14 @@ namespace caret { class BrowserTabContent; class CaretMappableDataFile; class ChartDataSource; + class MapFileDataSelector; class SelectionItemBorderSurface; class SelectionItemChartDataSeries; class SelectionItemChartFrequencySeries; class SelectionItemChartMatrix; + class SelectionItemChartTwoHistogram; + class SelectionItemChartTwoLineSeries; + class SelectionItemChartTwoMatrix; class SelectionItemCiftiConnectivityMatrixRowColumn; class SelectionItemChartTimeSeries; class SelectionItemFocusSurface; @@ -92,6 +96,15 @@ namespace caret { void generateChartMatrixIdentificationText(IdentificationStringBuilder& idText, const SelectionItemChartMatrix* idChartMatrix) const; + void generateChartTwoHistogramIdentificationText(IdentificationStringBuilder& idText, + const SelectionItemChartTwoHistogram* idChartTwoHistogram) const; + + void generateChartTwoLineSeriesIdentificationText(IdentificationStringBuilder& idText, + const SelectionItemChartTwoLineSeries* idChartTwoLineSeries) const; + + void generateChartTwoMatrixIdentificationText(IdentificationStringBuilder& idText, + const SelectionItemChartTwoMatrix* idChartTwoMatrix) const; + void generateCiftiConnectivityMatrixIdentificationText(IdentificationStringBuilder& idText, const SelectionItemCiftiConnectivityMatrixRowColumn* idCiftiConnMatrix) const; @@ -104,6 +117,9 @@ namespace caret { void generateChartDataSourceText(IdentificationStringBuilder& idText, const AString& typeOfChartText, const ChartDataSource* chartDataSource) const; + + void generateMapFileSelectorText(IdentificationStringBuilder& idText, + const MapFileDataSelector* mapFileDataSelector) const; }; #ifdef __IDENTIFICATION_TEXT_GENERATOR_DECLARE__ diff --git a/src/Brain/IdentifiedItem.cxx b/src/Brain/IdentifiedItem.cxx index b5a88bf3b817dc4d33e7abd9ab969a04506a011b..b1e0a59d54285ba72ba4488479f5622acdca84b2 100644 --- a/src/Brain/IdentifiedItem.cxx +++ b/src/Brain/IdentifiedItem.cxx @@ -103,12 +103,9 @@ void IdentifiedItem::initializeMembers() { m_text.clear(); - m_showIdentificationSymbol = true; m_sceneAssistant = new SceneClassAssistant(); m_sceneAssistant->add("m_text", &m_text); - m_sceneAssistant->add("m_showIdentificationSymbol", - &m_showIdentificationSymbol); } @@ -121,7 +118,6 @@ void IdentifiedItem::copyHelperIdentifiedItem(const IdentifiedItem& obj) { m_text = obj.m_text; - m_showIdentificationSymbol = obj.m_showIdentificationSymbol; } /** @@ -174,29 +170,6 @@ IdentifiedItem::toString() const return ("m_text=" + m_text); } -/** - * @return Is the identification symbol displayed for this item? - */ -bool -IdentifiedItem::isShowIdentificationSymbol() const -{ - return m_showIdentificationSymbol; -} - -/** - * Set show identification symbol for this item. - * By default the symbol is on but sub-classes - * can call this method to change the status. - * - * @param showSymbol - * New status for showing identification symbol. - */ -void -IdentifiedItem::setShowIdentificationSymbol(const bool showSymbol) -{ - m_showIdentificationSymbol = showSymbol; -} - /** * Create a scene for an instance of a class. * diff --git a/src/Brain/IdentifiedItem.h b/src/Brain/IdentifiedItem.h index d6369ee3334df99645405ccba25d64e7c50a354d..f52f9c7c9000c314fa4247fa8e04e079508dc19d 100644 --- a/src/Brain/IdentifiedItem.h +++ b/src/Brain/IdentifiedItem.h @@ -53,8 +53,6 @@ namespace caret { AString getText() const; - bool isShowIdentificationSymbol() const; - virtual AString toString() const; virtual SceneClass* saveToScene(const SceneAttributes* sceneAttributes, @@ -69,11 +67,7 @@ namespace caret { virtual void saveMembers(const SceneAttributes* sceneAttributes, SceneClass* sceneClass); - - // This method MUST NOT be virtual as it may be called - // from a sub-class constructor. - void setShowIdentificationSymbol(const bool showSymbol); - + private: void copyHelperIdentifiedItem(const IdentifiedItem& obj); @@ -83,9 +77,9 @@ namespace caret { AString m_text; - bool m_showIdentificationSymbol; - SceneClassAssistant* m_sceneAssistant; + + friend class IdentificationManager; }; #ifdef __IDENTIFIED_ITEM_DECLARE__ diff --git a/src/Brain/IdentifiedItemNode.cxx b/src/Brain/IdentifiedItemNode.cxx index fa5595635a49f8fce238fa379c6417dff979f224..2e7e54141ff50925c9f3e5469aebf4a2ac9a9b4d 100644 --- a/src/Brain/IdentifiedItemNode.cxx +++ b/src/Brain/IdentifiedItemNode.cxx @@ -145,14 +145,6 @@ IdentifiedItemNode::copyHelperIdentifiedItemNode(const IdentifiedItemNode& obj) m_contralateralSymbolRGB[2] = obj.m_contralateralSymbolRGB[2]; m_symbolSize = obj.m_symbolSize; - - /* - * Even though the show identification symbol is a member - * of the superclass, it needs to be copied here since - * initializeMembers() as it gets intialized for all - * constructors but copying needs to take precedence. - */ - setShowIdentificationSymbol(obj.isShowIdentificationSymbol()); } /** @@ -174,9 +166,6 @@ IdentifiedItemNode::initializeMembers() m_sceneAssistant->addArray("m_symbolRGB", m_symbolRGB, 3, 0); m_sceneAssistant->addArray("m_contralateralSymbolRGB", m_contralateralSymbolRGB, 3, 0); m_sceneAssistant->add("m_symbolSize", &m_symbolSize); - - CaretPreferences* prefs = SessionManager::get()->getCaretPreferences(); - setShowIdentificationSymbol(prefs->isShowSurfaceIdentificationSymbols()); } /** diff --git a/src/Brain/IdentifiedItemVoxel.cxx b/src/Brain/IdentifiedItemVoxel.cxx index e4db50fd72ad225e0af4a38cd4a1b1b16bf36a0b..373c71a8d9400dd7aa14f10a562ea0b4f73d811a 100644 --- a/src/Brain/IdentifiedItemVoxel.cxx +++ b/src/Brain/IdentifiedItemVoxel.cxx @@ -24,7 +24,6 @@ #undef __IDENTIFIED_ITEM_VOXEL_DECLARE__ #include "CaretAssert.h" -#include "CaretPreferences.h" #include "SceneClass.h" #include "SceneClassAssistant.h" #include "SessionManager.h" @@ -117,9 +116,6 @@ IdentifiedItemVoxel::initializeMembers() m_sceneAssistant->addArray("m_xyz", m_xyz, 3, 0.0); m_sceneAssistant->addArray("m_symbolRGB", m_symbolRGB, 3, 0); m_sceneAssistant->add("m_symbolSize", &m_symbolSize); - - CaretPreferences* prefs = SessionManager::get()->getCaretPreferences(); - setShowIdentificationSymbol(prefs->isShowVolumeIdentificationSymbols()); } /** @@ -139,14 +135,6 @@ IdentifiedItemVoxel::copyHelperIdentifiedItemVoxel(const IdentifiedItemVoxel& ob m_symbolRGB[2] = obj.m_symbolRGB[2]; m_symbolSize = obj.m_symbolSize; - - /* - * Even though the show identification symbol is a member - * of the superclass, it needs to be copied here since - * initializeMembers() as it gets intialized for all - * constructors but copying needs to take precedence. - */ - setShowIdentificationSymbol(obj.isShowIdentificationSymbol()); } /** diff --git a/src/Brain/Model.cxx b/src/Brain/Model.cxx index edcfc1acde79174ec3c54d2e6c296dc17517cc04..6ec95a26ae0f1d086ad6b136eff5b65e21e4fa01 100644 --- a/src/Brain/Model.cxx +++ b/src/Brain/Model.cxx @@ -116,6 +116,34 @@ Model::initializeSelectedSurfaces() /* nothing */ } +/** + * Get the chart overlay set for this model. + * + * @param tabIndex + * Index for the chart overlay set. + * @return + * Chart overlay set or NULL if not valid for this model. + */ +ChartTwoOverlaySet* +Model::getChartTwoOverlaySet(const int /*tabIndex*/) +{ + return NULL; +} + +/** + * Get the chart overlay set for this model. + * + * @param tabIndex + * Index for the chart overlay set. + * @return + * Chart overlay set or NULL if not valid for this model. + */ +const ChartTwoOverlaySet* +Model::getChartTwoOverlaySet(const int /*tabIndex*/) const +{ + return NULL; +} + /** * Create a scene for an instance of a class. * @@ -226,6 +254,8 @@ Model::restoreFromScene(const SceneAttributes* sceneAttributes, return; } + setRestoredFromScene(true); + if (m_modelType == ModelTypeEnum::MODEL_TYPE_SURFACE) { const AString surfaceName = sceneClass->getStringValue("surfaceName", "NOT-FOUND"); @@ -406,4 +436,25 @@ Model::copyTabContent(const int32_t /*sourceTabIndex*/, } +/** + * @return True if model was restored from scene + */ +bool Model::isRestoredFromScene() const +{ + return m_restoredFromSceneFlag; +} + +/** + * Set model was restored from scene. + * + * @param restoredStatus + * New value for restored status. + */ +void +Model::setRestoredFromScene(const bool restoredStatus) +{ + m_restoredFromSceneFlag = restoredStatus; +} + + diff --git a/src/Brain/Model.h b/src/Brain/Model.h index 95286f43432bbb804fe93458fdf4dc5dee51b77f..f17458bdafc7dc75a9234cc83cccf7c2e73494c9 100644 --- a/src/Brain/Model.h +++ b/src/Brain/Model.h @@ -28,6 +28,7 @@ namespace caret { class Brain; + class ChartTwoOverlaySet; class OverlaySet; class PlainTextStringBuilder; @@ -66,6 +67,10 @@ namespace caret { virtual const OverlaySet* getOverlaySet(const int tabIndex) const = 0; + virtual ChartTwoOverlaySet* getChartTwoOverlaySet(const int tabIndex); + + virtual const ChartTwoOverlaySet* getChartTwoOverlaySet(const int tabIndex) const; + virtual void initializeSelectedSurfaces(); virtual SceneClass* saveToScene(const SceneAttributes* sceneAttributes, @@ -82,6 +87,10 @@ namespace caret { virtual void copyTabContent(const int32_t sourceTabIndex, const int32_t destinationTabIndex); + bool isRestoredFromScene() const; + + void setRestoredFromScene(const bool restoredStatus); + protected: virtual void saveModelSpecificInformationToScene(const SceneAttributes* sceneAttributes, SceneClass* sceneClass) = 0; @@ -110,6 +119,8 @@ namespace caret { ModelTypeEnum::Enum m_modelType; std::vector m_oldSceneTransformations; + + bool m_restoredFromSceneFlag = false; }; } // namespace diff --git a/src/Brain/ModelChart.cxx b/src/Brain/ModelChart.cxx index e155a586709e7b537d21da3763d686e951ea693f..29299f26f779172f43409fcec3d0b323f3cc905d 100644 --- a/src/Brain/ModelChart.cxx +++ b/src/Brain/ModelChart.cxx @@ -30,7 +30,6 @@ #include "ChartableLineSeriesBrainordinateInterface.h" #include "CaretDataFileSelectionModel.h" #include "CaretLogger.h" -#include "CaretMappableDataFileAndMapSelectionModel.h" #include "ChartableLineSeriesRowColumnInterface.h" #include "ChartableMatrixInterface.h" #include "ChartData.h" @@ -39,9 +38,12 @@ #include "ChartModelDataSeries.h" #include "ChartModelFrequencySeries.h" #include "ChartModelTimeSeries.h" +#include "ChartingVersionEnum.h" #include "CiftiMappableDataFile.h" #include "CiftiScalarDataSeriesFile.h" #include "EventBrowserTabGetAll.h" +#include "EventBrowserTabIndicesGetAll.h" +#include "EventCaretMappableDataFilesGet.h" #include "EventManager.h" #include "EventNodeIdentificationColorsGetFromCharts.h" #include "ModelChart.h" @@ -68,15 +70,16 @@ ModelChart::ModelChart(Brain* brain) m_overlaySetArray = new OverlaySetArray(overlaySurfaceStructures, Overlay::INCLUDE_VOLUME_FILES_YES, "Chart View"); - + for (int32_t i = 0; i < BrainConstants::MAXIMUM_NUMBER_OF_BROWSER_TABS; i++) { - m_chartableMatrixFileSelectionModel[i] = CaretDataFileSelectionModel::newInstanceForChartableMatrixParcelInterface(m_brain); - m_chartableMatrixSeriesFileSelectionModel[i] = CaretDataFileSelectionModel::newInstanceForCaretDataFileType(m_brain, + m_chartableMatrixFileSelectionModel[i] = CaretDataFileSelectionModel::newInstanceForChartableMatrixParcelInterface(); + m_chartableMatrixSeriesFileSelectionModel[i] = CaretDataFileSelectionModel::newInstanceForCaretDataFileType( DataFileTypeEnum::CONNECTIVITY_SCALAR_DATA_SERIES); } - initializeCharts(); + initializeCharts(); + m_sceneAssistant = std::unique_ptr(new SceneClassAssistant()); EventManager::get()->addEventListener(this, EventTypeEnum::EVENT_NODE_IDENTIFICATION_COLORS_GET_FROM_CHARTS); } @@ -104,7 +107,7 @@ void ModelChart::initializeCharts() { for (int32_t i = 0; i < BrainConstants::MAXIMUM_NUMBER_OF_BROWSER_TABS; i++) { - m_selectedChartDataType[i] = ChartDataTypeEnum::CHART_DATA_TYPE_INVALID; + m_selectedChartOneDataType[i] = ChartOneDataTypeEnum::CHART_DATA_TYPE_INVALID; m_chartModelDataSeries[i] = new ChartModelDataSeries(); m_chartModelDataSeries[i]->getLeftAxis()->setText("Value"); @@ -200,7 +203,7 @@ ModelChart::loadAverageChartDataForSurfaceNodes(const StructureEnum::Enum struct addChartToChartModels(tabIndices, chartData); } - } + } } /** @@ -326,22 +329,6 @@ ModelChart::loadChartDataForCiftiMappableFileRow(CiftiMappableDataFile* ciftiMap } } } - -// if (ciftiMapFile != NULL) { -// ChartableLineSeriesRowColumnInterface* chartableLineFile = dynamic_cast(ciftiMapFile); -// if (chartableLineFile != NULL) { -// ChartData* chartData = chartableLineFile->loadLineSeriesChartDataForRow(rowIndex); -// if (chartData != NULL) { -// } -// } -// else { -// CaretLogSevere("Loading row charts from file type " -// + DataFileTypeEnum::toGuiName(ciftiMapFile->getDataFileType()) -// + " name " -// + ciftiMapFile->getFileName() -// + " is not supported."); -// } -// } } /** @@ -358,13 +345,13 @@ ModelChart::addChartToChartModels(const std::vector& tabIndices, { CaretAssert(chartData); - const ChartDataTypeEnum::Enum chartDataDataType = chartData->getChartDataType(); + const ChartOneDataTypeEnum::Enum chartDataDataType = chartData->getChartDataType(); switch (chartDataDataType) { - case ChartDataTypeEnum::CHART_DATA_TYPE_INVALID: + case ChartOneDataTypeEnum::CHART_DATA_TYPE_INVALID: CaretAssert(0); break; - case ChartDataTypeEnum::CHART_DATA_TYPE_LINE_DATA_SERIES: + case ChartOneDataTypeEnum::CHART_DATA_TYPE_LINE_DATA_SERIES: { ChartDataCartesian* cdc = dynamic_cast(chartData); CaretAssert(cdc); @@ -379,7 +366,7 @@ ModelChart::addChartToChartModels(const std::vector& tabIndices, m_dataSeriesChartData.push_front(cdcPtr.toWeakRef()); } break; - case ChartDataTypeEnum::CHART_DATA_TYPE_LINE_FREQUENCY_SERIES: + case ChartOneDataTypeEnum::CHART_DATA_TYPE_LINE_FREQUENCY_SERIES: { ChartDataCartesian* cdc = dynamic_cast(chartData); CaretAssert(cdc); @@ -394,7 +381,7 @@ ModelChart::addChartToChartModels(const std::vector& tabIndices, m_frequencySeriesChartData.push_front(cdcPtr.toWeakRef()); } break; - case ChartDataTypeEnum::CHART_DATA_TYPE_LINE_TIME_SERIES: + case ChartOneDataTypeEnum::CHART_DATA_TYPE_LINE_TIME_SERIES: { ChartDataCartesian* cdc = dynamic_cast(chartData); CaretAssert(cdc); @@ -409,10 +396,10 @@ ModelChart::addChartToChartModels(const std::vector& tabIndices, m_timeSeriesChartData.push_front(cdcPtr.toWeakRef()); } break; - case ChartDataTypeEnum::CHART_DATA_TYPE_MATRIX_LAYER: + case ChartOneDataTypeEnum::CHART_DATA_TYPE_MATRIX_LAYER: CaretAssert(0); break; - case ChartDataTypeEnum::CHART_DATA_TYPE_MATRIX_SERIES: + case ChartOneDataTypeEnum::CHART_DATA_TYPE_MATRIX_SERIES: break; } } @@ -427,36 +414,6 @@ ModelChart::addChartToChartModels(const std::vector& tabIndices, void ModelChart::getTabsAndBrainordinateChartFilesForLineChartLoading(std::map >& chartBrainordinateFileEnabledTabsOut) const { -// chartFileEnabledTabsOut.clear(); -// -// EventBrowserTabGetAll allTabsEvent; -// EventManager::get()->sendEvent(allTabsEvent.getPointer()); -// std::vector validTabIndices = allTabsEvent.getBrowserTabIndices(); -// -// std::vector chartFiles; -// m_brain->getAllChartableBrainordinateDataFilesWithChartingEnabled(chartFiles); -// -// for (std::vector::iterator iter = chartFiles.begin(); -// iter != chartFiles.end(); -// iter++) { -// ChartableLineSeriesBrainordinateInterface* cf = *iter; -// std::vector chartFileTabIndices; -// -// for (std::vector::iterator tabIter = validTabIndices.begin(); -// tabIter != validTabIndices.end(); -// tabIter++) { -// const int32_t tabIndex = *tabIter; -// if (cf->isLineSeriesChartingEnabled(tabIndex)) { -// chartFileTabIndices.push_back(tabIndex); -// } -// } -// -// if ( ! chartFileTabIndices.empty()) { -// chartFileEnabledTabsOut.insert(std::make_pair(cf, chartFileTabIndices)); -// } -// } - - chartBrainordinateFileEnabledTabsOut.clear(); std::map > chartFileEnabledTabs; @@ -676,7 +633,7 @@ ModelChart::receiveEvent(Event* event) AString ModelChart::getNameForGUI(const bool /*includeStructureFlag*/) const { - AString name = "Chart"; + AString name = "ChartOld"; return name; } @@ -687,7 +644,7 @@ ModelChart::getNameForGUI(const bool /*includeStructureFlag*/) const AString ModelChart::getNameForBrowserTab() const { - AString name = "Chart"; + AString name = "ChartOld"; return name; } @@ -733,7 +690,7 @@ ModelChart::initializeOverlays() } /** - * Save information specific to this type of model to the scene. + * Save version one charting information specific to this type of model to the scene. * * @param sceneAttributes * Attributes for the scene. Scenes may be of different types @@ -743,9 +700,9 @@ ModelChart::initializeOverlays() * @param sceneClass * SceneClass to which model specific information is added. */ -void -ModelChart::saveModelSpecificInformationToScene(const SceneAttributes* sceneAttributes, - SceneClass* sceneClass) +void +ModelChart::saveVersionOneModelSpecificInformationToScene(const SceneAttributes* sceneAttributes, + SceneClass* sceneClass) { std::vector tabIndices = sceneAttributes->getIndicesOfTabsForSavingToScene(); @@ -755,10 +712,10 @@ ModelChart::saveModelSpecificInformationToScene(const SceneAttributes* sceneAttr tabIndices, validChartDataIDs); - sceneClass->addEnumeratedTypeArrayForTabIndices("m_selectedChartDataType", - m_selectedChartDataType, - tabIndices); - + sceneClass->addEnumeratedTypeArrayForTabIndices("m_selectedChartDataType", + m_selectedChartOneDataType, + tabIndices); + /* * Save matrix chart models to scene. */ @@ -780,7 +737,7 @@ ModelChart::saveModelSpecificInformationToScene(const SceneAttributes* sceneAttr * Save matrix series chart models to scene. */ SceneObjectMapIntegerKey* matrixSeriesSceneMap = new SceneObjectMapIntegerKey("chartableMatrixSeriesFileSelectionModelMap", - SceneObjectDataTypeEnum::SCENE_CLASS); + SceneObjectDataTypeEnum::SCENE_CLASS); std::vector matrixSeriesSelectionVector; for (std::vector::const_iterator tabIter = tabIndices.begin(); @@ -789,14 +746,36 @@ ModelChart::saveModelSpecificInformationToScene(const SceneAttributes* sceneAttr const int32_t tabIndex = *tabIter; matrixSeriesSceneMap->addClass(tabIndex, m_chartableMatrixSeriesFileSelectionModel[tabIndex]->saveToScene(sceneAttributes, - "m_chartableMatrixSeriesFileSelectionModel")); + "m_chartableMatrixSeriesFileSelectionModel")); } sceneClass->addChild(matrixSeriesSceneMap); } +/** + * Save information specific to this type of model to the scene. + * + * @param sceneAttributes + * Attributes for the scene. Scenes may be of different types + * (full, generic, etc) and the attributes should be checked when + * saving the scene. + * + * @param sceneClass + * SceneClass to which model specific information is added. + */ +void +ModelChart::saveModelSpecificInformationToScene(const SceneAttributes* sceneAttributes, + SceneClass* sceneClass) +{ + m_sceneAssistant->saveMembers(sceneAttributes, + sceneClass); + + saveVersionOneModelSpecificInformationToScene(sceneAttributes, + sceneClass); +} + /** * Restore information specific to the type of model from the scene. - * + * * @param sceneAttributes * Attributes for the scene. Scenes may be of different types * (full, generic, etc) and the attributes should be checked when @@ -805,20 +784,42 @@ ModelChart::saveModelSpecificInformationToScene(const SceneAttributes* sceneAttr * @param sceneClass * sceneClass from which model specific information is obtained. */ -void +void ModelChart::restoreModelSpecificInformationFromScene(const SceneAttributes* sceneAttributes, - const SceneClass* sceneClass) + const SceneClass* sceneClass) { reset(); + m_sceneAssistant->restoreMembers(sceneAttributes, + sceneClass); + + restoreVersionOneModelSpecificInformationFromScene(sceneAttributes, + sceneClass); +} + +/** + * Restore information specific to the type of model from VERSION ONE scene. + * + * @param sceneAttributes + * Attributes for the scene. Scenes may be of different types + * (full, generic, etc) and the attributes should be checked when + * restoring the scene. + * + * @param sceneClass + * sceneClass from which model specific information is obtained. + */ +void +ModelChart::restoreVersionOneModelSpecificInformationFromScene(const SceneAttributes* sceneAttributes, + const SceneClass* sceneClass) +{ /* * Restore the chart models */ - restoreChartModelsFromScene(sceneAttributes, - sceneClass); + restoreVersionOneChartModelsFromScene(sceneAttributes, + sceneClass); - sceneClass->getEnumerateTypeArrayForTabIndices("m_selectedChartDataType", - m_selectedChartDataType); + sceneClass->getEnumerateTypeArrayForTabIndices("m_selectedChartDataType", + m_selectedChartOneDataType); /* * Restore matrix chart models from scene. @@ -884,20 +885,20 @@ ModelChart::saveChartModelsToScene(const SceneAttributes* sceneAttributes, const int32_t tabIndex = *tabIter; ChartModel* chartModel = NULL; - switch (getSelectedChartDataType(tabIndex)) { - case ChartDataTypeEnum::CHART_DATA_TYPE_INVALID: + switch (getSelectedChartOneDataType(tabIndex)) { + case ChartOneDataTypeEnum::CHART_DATA_TYPE_INVALID: break; - case ChartDataTypeEnum::CHART_DATA_TYPE_MATRIX_LAYER: + case ChartOneDataTypeEnum::CHART_DATA_TYPE_MATRIX_LAYER: break; - case ChartDataTypeEnum::CHART_DATA_TYPE_MATRIX_SERIES: + case ChartOneDataTypeEnum::CHART_DATA_TYPE_MATRIX_SERIES: break; - case ChartDataTypeEnum::CHART_DATA_TYPE_LINE_FREQUENCY_SERIES: + case ChartOneDataTypeEnum::CHART_DATA_TYPE_LINE_FREQUENCY_SERIES: chartModel = getSelectedFrequencySeriesChartModel(tabIndex); break; - case ChartDataTypeEnum::CHART_DATA_TYPE_LINE_DATA_SERIES: + case ChartOneDataTypeEnum::CHART_DATA_TYPE_LINE_DATA_SERIES: chartModel = getSelectedDataSeriesChartModel(tabIndex); break; - case ChartDataTypeEnum::CHART_DATA_TYPE_LINE_TIME_SERIES: + case ChartOneDataTypeEnum::CHART_DATA_TYPE_LINE_TIME_SERIES: chartModel = getSelectedTimeSeriesChartModel(tabIndex); break; } @@ -913,7 +914,7 @@ ModelChart::saveChartModelsToScene(const SceneAttributes* sceneAttributes, "ChartClassContainer", 1); chartClassContainer->addInteger("tabIndex", tabIndex); - chartClassContainer->addEnumeratedType("chartDataType", + chartClassContainer->addEnumeratedType("chartDataType", chartModel->getChartDataType()); chartClassContainer->addClass(chartModelClass); @@ -947,7 +948,7 @@ ModelChart::saveChartModelsToScene(const SceneAttributes* sceneAttributes, SceneClass* chartDataContainer = new SceneClass("chartDataContainer", "ChartDataContainer", 1); - chartDataContainer->addEnumeratedType("chartDataType", + chartDataContainer->addEnumeratedType("chartDataType", chartData->getChartDataType()); chartDataContainer->addClass(chartDataClass); @@ -963,7 +964,7 @@ ModelChart::saveChartModelsToScene(const SceneAttributes* sceneAttributes, } /** - * Restore the chart models from the scene. + * Restore the chart models from a VERSION ONE scene. * * @param sceneAttributes * Attributes for the scene. Scenes may be of different types @@ -974,7 +975,7 @@ ModelChart::saveChartModelsToScene(const SceneAttributes* sceneAttributes, * sceneClass from which model specific information is obtained. */ void -ModelChart::restoreChartModelsFromScene(const SceneAttributes* sceneAttributes, +ModelChart::restoreVersionOneChartModelsFromScene(const SceneAttributes* sceneAttributes, const SceneClass* sceneClass) { /* @@ -987,34 +988,34 @@ ModelChart::restoreChartModelsFromScene(const SceneAttributes* sceneAttributes, const SceneClass* chartClassContainer = chartModelArray->getClassAtIndex(i); if (chartClassContainer != NULL) { const int32_t tabIndex = chartClassContainer->getIntegerValue("tabIndex", -1); - const ChartDataTypeEnum::Enum chartDataType = chartClassContainer->getEnumeratedTypeValue("chartDataType", - ChartDataTypeEnum::CHART_DATA_TYPE_INVALID); + const ChartOneDataTypeEnum::Enum chartDataType = chartClassContainer->getEnumeratedTypeValue("chartDataType", + ChartOneDataTypeEnum::CHART_DATA_TYPE_INVALID); const SceneClass* chartModelClass = chartClassContainer->getClass("chartModel"); if ((tabIndex >= 0) - && (chartDataType != ChartDataTypeEnum::CHART_DATA_TYPE_INVALID) + && (chartDataType != ChartOneDataTypeEnum::CHART_DATA_TYPE_INVALID) && (chartModelClass != NULL)) { CaretAssertArrayIndex(m_chartModelDataSeries, BrainConstants::MAXIMUM_NUMBER_OF_BROWSER_TABS, tabIndex); switch (chartDataType) { - case ChartDataTypeEnum::CHART_DATA_TYPE_INVALID: + case ChartOneDataTypeEnum::CHART_DATA_TYPE_INVALID: break; - case ChartDataTypeEnum::CHART_DATA_TYPE_MATRIX_LAYER: + case ChartOneDataTypeEnum::CHART_DATA_TYPE_MATRIX_LAYER: CaretAssert(0); break; - case ChartDataTypeEnum::CHART_DATA_TYPE_MATRIX_SERIES: + case ChartOneDataTypeEnum::CHART_DATA_TYPE_MATRIX_SERIES: break; - case ChartDataTypeEnum::CHART_DATA_TYPE_LINE_DATA_SERIES: + case ChartOneDataTypeEnum::CHART_DATA_TYPE_LINE_DATA_SERIES: m_chartModelDataSeries[tabIndex]->restoreFromScene(sceneAttributes, chartModelClass); break; - case ChartDataTypeEnum::CHART_DATA_TYPE_LINE_FREQUENCY_SERIES: + case ChartOneDataTypeEnum::CHART_DATA_TYPE_LINE_FREQUENCY_SERIES: m_chartModelFrequencySeries[tabIndex]->restoreFromScene(sceneAttributes, chartModelClass); break; - case ChartDataTypeEnum::CHART_DATA_TYPE_LINE_TIME_SERIES: + case ChartOneDataTypeEnum::CHART_DATA_TYPE_LINE_TIME_SERIES: m_chartModelTimeSeries[tabIndex]->restoreFromScene(sceneAttributes, chartModelClass); break; @@ -1034,10 +1035,10 @@ ModelChart::restoreChartModelsFromScene(const SceneAttributes* sceneAttributes, for (int32_t i = 0; i < numElements; i++) { const SceneClass* chartDataContainer = chartDataArray->getClassAtIndex(i); if (chartDataContainer != NULL) { - const ChartDataTypeEnum::Enum chartDataType = chartDataContainer->getEnumeratedTypeValue("chartDataType", - ChartDataTypeEnum::CHART_DATA_TYPE_INVALID); + const ChartOneDataTypeEnum::Enum chartDataType = chartDataContainer->getEnumeratedTypeValue("chartDataType", + ChartOneDataTypeEnum::CHART_DATA_TYPE_INVALID); const SceneClass* chartDataClass = chartDataContainer->getClass("chartData"); - if ((chartDataType != ChartDataTypeEnum::CHART_DATA_TYPE_INVALID) + if ((chartDataType != ChartOneDataTypeEnum::CHART_DATA_TYPE_INVALID) && (chartDataClass != NULL)) { ChartData* chartData = ChartData::newChartDataForChartDataType(chartDataType); CaretAssert(chartData); @@ -1114,30 +1115,30 @@ ModelChart::restoreChartModelsFromScene(const SceneAttributes* sceneAttributes, QSharedPointer chartPointer = *rcdIter; switch (chartPointer->getChartDataType()) { - case ChartDataTypeEnum::CHART_DATA_TYPE_INVALID: + case ChartOneDataTypeEnum::CHART_DATA_TYPE_INVALID: CaretAssert(0); break; - case ChartDataTypeEnum::CHART_DATA_TYPE_MATRIX_LAYER: + case ChartOneDataTypeEnum::CHART_DATA_TYPE_MATRIX_LAYER: CaretAssert(0); break; - case ChartDataTypeEnum::CHART_DATA_TYPE_MATRIX_SERIES: + case ChartOneDataTypeEnum::CHART_DATA_TYPE_MATRIX_SERIES: CaretAssert(0); break; - case ChartDataTypeEnum::CHART_DATA_TYPE_LINE_DATA_SERIES: + case ChartOneDataTypeEnum::CHART_DATA_TYPE_LINE_DATA_SERIES: { QSharedPointer cartChartPointer = chartPointer.dynamicCast(); CaretAssert( ! cartChartPointer.isNull()); m_dataSeriesChartData.push_back(cartChartPointer); } break; - case ChartDataTypeEnum::CHART_DATA_TYPE_LINE_FREQUENCY_SERIES: + case ChartOneDataTypeEnum::CHART_DATA_TYPE_LINE_FREQUENCY_SERIES: { QSharedPointer cartChartPointer = chartPointer.dynamicCast(); CaretAssert( ! cartChartPointer.isNull()); m_frequencySeriesChartData.push_back(cartChartPointer); } break; - case ChartDataTypeEnum::CHART_DATA_TYPE_LINE_TIME_SERIES: + case ChartOneDataTypeEnum::CHART_DATA_TYPE_LINE_TIME_SERIES: { QSharedPointer cartChartPointer = chartPointer.dynamicCast(); CaretAssert( ! cartChartPointer.isNull()); @@ -1166,7 +1167,7 @@ ModelChart::loadCartesianChartWhenRestoringScene(const ChartData* chartData) const ChartDataCartesian* cartesianChart = dynamic_cast(chartData); if (cartesianChart != NULL) { - const ChartDataTypeEnum::Enum chartDataType = cartesianChart->getChartDataType(); + const ChartOneDataTypeEnum::Enum chartDataType = cartesianChart->getChartDataType(); std::vector chartableDataFiles; m_brain->getAllChartableLineSeriesDataFiles(chartableDataFiles); @@ -1325,10 +1326,10 @@ ModelChart::getDescriptionOfContent(const int32_t tabIndex, PlainTextStringBuilder& descriptionOut) const { ChartModel* chartModel = NULL; - switch (getSelectedChartDataType(tabIndex)) { - case ChartDataTypeEnum::CHART_DATA_TYPE_INVALID: + switch (getSelectedChartOneDataType(tabIndex)) { + case ChartOneDataTypeEnum::CHART_DATA_TYPE_INVALID: break; - case ChartDataTypeEnum::CHART_DATA_TYPE_MATRIX_LAYER: + case ChartOneDataTypeEnum::CHART_DATA_TYPE_MATRIX_LAYER: { CaretDataFileSelectionModel* sm = m_chartableMatrixFileSelectionModel[tabIndex]; const CaretDataFile* caretFile = sm->getSelectedFile(); @@ -1339,7 +1340,7 @@ ModelChart::getDescriptionOfContent(const int32_t tabIndex, } } break; - case ChartDataTypeEnum::CHART_DATA_TYPE_MATRIX_SERIES: + case ChartOneDataTypeEnum::CHART_DATA_TYPE_MATRIX_SERIES: { CaretDataFileSelectionModel* sm = m_chartableMatrixSeriesFileSelectionModel[tabIndex]; const CaretDataFile* caretFile = sm->getSelectedFile(); @@ -1350,13 +1351,13 @@ ModelChart::getDescriptionOfContent(const int32_t tabIndex, } } break; - case ChartDataTypeEnum::CHART_DATA_TYPE_LINE_DATA_SERIES: + case ChartOneDataTypeEnum::CHART_DATA_TYPE_LINE_DATA_SERIES: chartModel = const_cast(getSelectedDataSeriesChartModel(tabIndex)); break; - case ChartDataTypeEnum::CHART_DATA_TYPE_LINE_FREQUENCY_SERIES: + case ChartOneDataTypeEnum::CHART_DATA_TYPE_LINE_FREQUENCY_SERIES: chartModel = const_cast(getSelectedFrequencySeriesChartModel(tabIndex)); break; - case ChartDataTypeEnum::CHART_DATA_TYPE_LINE_TIME_SERIES: + case ChartOneDataTypeEnum::CHART_DATA_TYPE_LINE_TIME_SERIES: chartModel = const_cast(getSelectedTimeSeriesChartModel(tabIndex)); break; } @@ -1365,7 +1366,7 @@ ModelChart::getDescriptionOfContent(const int32_t tabIndex, if (chartModel != NULL) { descriptionOut.addLine("Chart Type: " - + ChartDataTypeEnum::toGuiName(chartModel->getChartDataType())); + + ChartOneDataTypeEnum::toGuiName(chartModel->getChartDataType())); descriptionOut.pushIndentation(); @@ -1411,7 +1412,7 @@ ModelChart::copyTabContent(const int32_t sourceTabIndex, m_overlaySetArray->copyOverlaySet(sourceTabIndex, destinationTabIndex); - m_selectedChartDataType[destinationTabIndex] = m_selectedChartDataType[sourceTabIndex]; + m_selectedChartOneDataType[destinationTabIndex] = m_selectedChartOneDataType[sourceTabIndex]; *m_chartModelDataSeries[destinationTabIndex] = *m_chartModelDataSeries[sourceTabIndex]; *m_chartModelFrequencySeries[destinationTabIndex] = *m_chartModelFrequencySeries[sourceTabIndex]; *m_chartModelTimeSeries[destinationTabIndex] = *m_chartModelTimeSeries[sourceTabIndex]; @@ -1428,13 +1429,13 @@ ModelChart::copyTabContent(const int32_t sourceTabIndex, * Type of data for chart. */ void -ModelChart::setSelectedChartDataType(const int32_t tabIndex, - const ChartDataTypeEnum::Enum dataType) +ModelChart::setSelectedChartOneDataType(const int32_t tabIndex, + const ChartOneDataTypeEnum::Enum dataType) { - CaretAssertArrayIndex(m_selectedChartDataType, + CaretAssertArrayIndex(m_selectedChartOneDataType, BrainConstants::MAXIMUM_NUMBER_OF_BROWSER_TABS, tabIndex); - m_selectedChartDataType[tabIndex] = dataType; + m_selectedChartOneDataType[tabIndex] = dataType; } /** @@ -1444,7 +1445,7 @@ ModelChart::setSelectedChartDataType(const int32_t tabIndex, * Output containing valid chart data types. */ void -ModelChart::getValidChartDataTypes(std::vector& validChartDataTypesOut) const +ModelChart::getValidChartOneDataTypes(std::vector& validChartDataTypesOut) const { validChartDataTypesOut.clear(); @@ -1462,68 +1463,32 @@ ModelChart::getValidChartDataTypes(std::vector& validCh fileIter++) { ChartableLineSeriesInterface* chartFile = *fileIter; - std::vector chartDataTypes; + std::vector chartDataTypes; chartFile->getSupportedLineSeriesChartDataTypes(chartDataTypes); - for (std::vector::iterator typeIter = chartDataTypes.begin(); + for (std::vector::iterator typeIter = chartDataTypes.begin(); typeIter != chartDataTypes.end(); typeIter++) { - const ChartDataTypeEnum::Enum cdt = *typeIter; + const ChartOneDataTypeEnum::Enum cdt = *typeIter; switch (cdt) { - case ChartDataTypeEnum::CHART_DATA_TYPE_INVALID: + case ChartOneDataTypeEnum::CHART_DATA_TYPE_INVALID: break; - case ChartDataTypeEnum::CHART_DATA_TYPE_MATRIX_LAYER: + case ChartOneDataTypeEnum::CHART_DATA_TYPE_MATRIX_LAYER: break; - case ChartDataTypeEnum::CHART_DATA_TYPE_MATRIX_SERIES: + case ChartOneDataTypeEnum::CHART_DATA_TYPE_MATRIX_SERIES: break; - case ChartDataTypeEnum::CHART_DATA_TYPE_LINE_DATA_SERIES: + case ChartOneDataTypeEnum::CHART_DATA_TYPE_LINE_DATA_SERIES: haveDataSeries = true; break; - case ChartDataTypeEnum::CHART_DATA_TYPE_LINE_FREQUENCY_SERIES: + case ChartOneDataTypeEnum::CHART_DATA_TYPE_LINE_FREQUENCY_SERIES: haveFrequencySeries = true; break; - case ChartDataTypeEnum::CHART_DATA_TYPE_LINE_TIME_SERIES: + case ChartOneDataTypeEnum::CHART_DATA_TYPE_LINE_TIME_SERIES: haveTimeSeries = true; break; } } } - - -// std::vector allBrainordinateChartableFiles; -// m_brain->getAllChartableBrainordinateDataFiles(allBrainordinateChartableFiles); -// -// for (std::vector::iterator fileIter = allBrainordinateChartableFiles.begin(); -// fileIter != allBrainordinateChartableFiles.end(); -// fileIter++) { -// ChartableLineSeriesBrainordinateInterface* chartFile = *fileIter; -// -// std::vector chartDataTypes; -// chartFile->getSupportedLineSeriesChartDataTypes(chartDataTypes); -// -// for (std::vector::iterator typeIter = chartDataTypes.begin(); -// typeIter != chartDataTypes.end(); -// typeIter++) { -// const ChartDataTypeEnum::Enum cdt = *typeIter; -// switch (cdt) { -// case ChartDataTypeEnum::CHART_DATA_TYPE_INVALID: -// break; -// case ChartDataTypeEnum::CHART_DATA_TYPE_MATRIX_LAYER: -// break; -// case ChartDataTypeEnum::CHART_DATA_TYPE_MATRIX_SERIES: -// break; -// case ChartDataTypeEnum::CHART_DATA_TYPE_LINE_DATA_SERIES: -// haveDataSeries = true; -// break; -// case ChartDataTypeEnum::CHART_DATA_TYPE_LINE_FREQUENCY_SERIES: -// haveFrequencySeries = true; -// break; -// case ChartDataTypeEnum::CHART_DATA_TYPE_LINE_TIME_SERIES: -// haveTimeSeries = true; -// break; -// } -// } -// } std::vector allMatrixChartableFiles; m_brain->getAllChartableMatrixDataFiles(allMatrixChartableFiles); @@ -1533,46 +1498,46 @@ ModelChart::getValidChartDataTypes(std::vector& validCh fileIter++) { ChartableMatrixInterface* chartFile = *fileIter; - std::vector chartDataTypes; + std::vector chartDataTypes; chartFile->getSupportedMatrixChartDataTypes(chartDataTypes); - for (std::vector::iterator typeIter = chartDataTypes.begin(); + for (std::vector::iterator typeIter = chartDataTypes.begin(); typeIter != chartDataTypes.end(); typeIter++) { - const ChartDataTypeEnum::Enum cdt = *typeIter; + const ChartOneDataTypeEnum::Enum cdt = *typeIter; switch (cdt) { - case ChartDataTypeEnum::CHART_DATA_TYPE_INVALID: + case ChartOneDataTypeEnum::CHART_DATA_TYPE_INVALID: break; - case ChartDataTypeEnum::CHART_DATA_TYPE_MATRIX_LAYER: + case ChartOneDataTypeEnum::CHART_DATA_TYPE_MATRIX_LAYER: haveMatrixLayers = true; break; - case ChartDataTypeEnum::CHART_DATA_TYPE_MATRIX_SERIES: + case ChartOneDataTypeEnum::CHART_DATA_TYPE_MATRIX_SERIES: haveMatrixSeries = true; break; - case ChartDataTypeEnum::CHART_DATA_TYPE_LINE_DATA_SERIES: + case ChartOneDataTypeEnum::CHART_DATA_TYPE_LINE_DATA_SERIES: break; - case ChartDataTypeEnum::CHART_DATA_TYPE_LINE_FREQUENCY_SERIES: + case ChartOneDataTypeEnum::CHART_DATA_TYPE_LINE_FREQUENCY_SERIES: break; - case ChartDataTypeEnum::CHART_DATA_TYPE_LINE_TIME_SERIES: + case ChartOneDataTypeEnum::CHART_DATA_TYPE_LINE_TIME_SERIES: break; } } } if (haveDataSeries) { - validChartDataTypesOut.push_back(ChartDataTypeEnum::CHART_DATA_TYPE_LINE_DATA_SERIES); + validChartDataTypesOut.push_back(ChartOneDataTypeEnum::CHART_DATA_TYPE_LINE_DATA_SERIES); } if (haveFrequencySeries) { - validChartDataTypesOut.push_back(ChartDataTypeEnum::CHART_DATA_TYPE_LINE_FREQUENCY_SERIES); + validChartDataTypesOut.push_back(ChartOneDataTypeEnum::CHART_DATA_TYPE_LINE_FREQUENCY_SERIES); } if (haveMatrixLayers) { - validChartDataTypesOut.push_back(ChartDataTypeEnum::CHART_DATA_TYPE_MATRIX_LAYER); + validChartDataTypesOut.push_back(ChartOneDataTypeEnum::CHART_DATA_TYPE_MATRIX_LAYER); } if (haveMatrixSeries) { - validChartDataTypesOut.push_back(ChartDataTypeEnum::CHART_DATA_TYPE_MATRIX_SERIES); + validChartDataTypesOut.push_back(ChartOneDataTypeEnum::CHART_DATA_TYPE_MATRIX_SERIES); } if (haveTimeSeries) { - validChartDataTypesOut.push_back(ChartDataTypeEnum::CHART_DATA_TYPE_LINE_TIME_SERIES); + validChartDataTypesOut.push_back(ChartOneDataTypeEnum::CHART_DATA_TYPE_LINE_TIME_SERIES); } } @@ -1585,85 +1550,85 @@ ModelChart::getValidChartDataTypes(std::vector& validCh * @return * Chart type in the given tab. */ -ChartDataTypeEnum::Enum -ModelChart::getSelectedChartDataType(const int32_t tabIndex) const +ChartOneDataTypeEnum::Enum +ModelChart::getSelectedChartOneDataType(const int32_t tabIndex) const { - CaretAssertArrayIndex(m_selectedChartDataType, + CaretAssertArrayIndex(m_selectedChartOneDataType, BrainConstants::MAXIMUM_NUMBER_OF_BROWSER_TABS, tabIndex); - ChartDataTypeEnum::Enum chartDataType = m_selectedChartDataType[tabIndex]; + ChartOneDataTypeEnum::Enum chartDataType = m_selectedChartOneDataType[tabIndex]; /* * Verify that the selected chart data type is valid. */ - std::vector validChartDataTypes; - getValidChartDataTypes(validChartDataTypes); + std::vector validChartDataTypes; + getValidChartOneDataTypes(validChartDataTypes); if (std::find(validChartDataTypes.begin(), validChartDataTypes.end(), chartDataType) == validChartDataTypes.end()) { - chartDataType = ChartDataTypeEnum::CHART_DATA_TYPE_INVALID; + chartDataType = ChartOneDataTypeEnum::CHART_DATA_TYPE_INVALID; } /* * If selected chart data type is invalid, find a valid chart type, * preferably one that contains data. */ - if (chartDataType == ChartDataTypeEnum::CHART_DATA_TYPE_INVALID) { + if (chartDataType == ChartOneDataTypeEnum::CHART_DATA_TYPE_INVALID) { if ( ! validChartDataTypes.empty()) { /* * Will become the the first valid chart data type that contains * data (if there is one) */ - ChartDataTypeEnum::Enum chartDataTypeWithValidData = ChartDataTypeEnum::CHART_DATA_TYPE_INVALID; + ChartOneDataTypeEnum::Enum chartDataTypeWithValidData = ChartOneDataTypeEnum::CHART_DATA_TYPE_INVALID; /* * Loop through all chart types (some or all valid charts * types may not contain data until the user commands loading of data) */ - std::vector allChartDataTypes; - ChartDataTypeEnum::getAllEnums(allChartDataTypes); - for (std::vector::iterator iter = allChartDataTypes.begin(); + std::vector allChartDataTypes; + ChartOneDataTypeEnum::getAllEnums(allChartDataTypes); + for (std::vector::iterator iter = allChartDataTypes.begin(); iter != allChartDataTypes.end(); iter++) { - const ChartDataTypeEnum::Enum cdt = *iter; + const ChartOneDataTypeEnum::Enum cdt = *iter; if (std::find(validChartDataTypes.begin(), validChartDataTypes.end(), cdt) != validChartDataTypes.end()) { - if (chartDataType == ChartDataTypeEnum::CHART_DATA_TYPE_INVALID) { + if (chartDataType == ChartOneDataTypeEnum::CHART_DATA_TYPE_INVALID) { chartDataType = cdt; } switch (cdt) { - case ChartDataTypeEnum::CHART_DATA_TYPE_INVALID: + case ChartOneDataTypeEnum::CHART_DATA_TYPE_INVALID: break; - case ChartDataTypeEnum::CHART_DATA_TYPE_MATRIX_LAYER: - if (chartDataTypeWithValidData == ChartDataTypeEnum::CHART_DATA_TYPE_INVALID) { - chartDataTypeWithValidData = ChartDataTypeEnum::CHART_DATA_TYPE_MATRIX_LAYER; + case ChartOneDataTypeEnum::CHART_DATA_TYPE_MATRIX_LAYER: + if (chartDataTypeWithValidData == ChartOneDataTypeEnum::CHART_DATA_TYPE_INVALID) { + chartDataTypeWithValidData = ChartOneDataTypeEnum::CHART_DATA_TYPE_MATRIX_LAYER; } break; - case ChartDataTypeEnum::CHART_DATA_TYPE_MATRIX_SERIES: - if (chartDataTypeWithValidData == ChartDataTypeEnum::CHART_DATA_TYPE_INVALID) { - chartDataTypeWithValidData = ChartDataTypeEnum::CHART_DATA_TYPE_MATRIX_SERIES; + case ChartOneDataTypeEnum::CHART_DATA_TYPE_MATRIX_SERIES: + if (chartDataTypeWithValidData == ChartOneDataTypeEnum::CHART_DATA_TYPE_INVALID) { + chartDataTypeWithValidData = ChartOneDataTypeEnum::CHART_DATA_TYPE_MATRIX_SERIES; } break; - case ChartDataTypeEnum::CHART_DATA_TYPE_LINE_DATA_SERIES: + case ChartOneDataTypeEnum::CHART_DATA_TYPE_LINE_DATA_SERIES: if (m_chartModelDataSeries[tabIndex]->getNumberOfChartData() > 0) { - if (chartDataTypeWithValidData == ChartDataTypeEnum::CHART_DATA_TYPE_INVALID) { - chartDataTypeWithValidData = ChartDataTypeEnum::CHART_DATA_TYPE_LINE_DATA_SERIES; + if (chartDataTypeWithValidData == ChartOneDataTypeEnum::CHART_DATA_TYPE_INVALID) { + chartDataTypeWithValidData = ChartOneDataTypeEnum::CHART_DATA_TYPE_LINE_DATA_SERIES; } } break; - case ChartDataTypeEnum::CHART_DATA_TYPE_LINE_FREQUENCY_SERIES: + case ChartOneDataTypeEnum::CHART_DATA_TYPE_LINE_FREQUENCY_SERIES: if (m_chartModelFrequencySeries[tabIndex]->getNumberOfChartData() > 0) { - if (chartDataTypeWithValidData == ChartDataTypeEnum::CHART_DATA_TYPE_INVALID) { - chartDataTypeWithValidData = ChartDataTypeEnum::CHART_DATA_TYPE_LINE_FREQUENCY_SERIES; + if (chartDataTypeWithValidData == ChartOneDataTypeEnum::CHART_DATA_TYPE_INVALID) { + chartDataTypeWithValidData = ChartOneDataTypeEnum::CHART_DATA_TYPE_LINE_FREQUENCY_SERIES; } } break; - case ChartDataTypeEnum::CHART_DATA_TYPE_LINE_TIME_SERIES: + case ChartOneDataTypeEnum::CHART_DATA_TYPE_LINE_TIME_SERIES: if (m_chartModelTimeSeries[tabIndex]->getNumberOfChartData() > 0) { - if (chartDataTypeWithValidData == ChartDataTypeEnum::CHART_DATA_TYPE_INVALID) { - chartDataTypeWithValidData = ChartDataTypeEnum::CHART_DATA_TYPE_LINE_TIME_SERIES; + if (chartDataTypeWithValidData == ChartOneDataTypeEnum::CHART_DATA_TYPE_INVALID) { + chartDataTypeWithValidData = ChartOneDataTypeEnum::CHART_DATA_TYPE_LINE_TIME_SERIES; } } break; @@ -1671,10 +1636,10 @@ ModelChart::getSelectedChartDataType(const int32_t tabIndex) const } } - if (chartDataTypeWithValidData != ChartDataTypeEnum::CHART_DATA_TYPE_INVALID) { + if (chartDataTypeWithValidData != ChartOneDataTypeEnum::CHART_DATA_TYPE_INVALID) { chartDataType = chartDataTypeWithValidData; } - else if (chartDataType == ChartDataTypeEnum::CHART_DATA_TYPE_INVALID) { + else if (chartDataType == ChartOneDataTypeEnum::CHART_DATA_TYPE_INVALID) { chartDataType = validChartDataTypes[0]; } } @@ -1683,11 +1648,23 @@ ModelChart::getSelectedChartDataType(const int32_t tabIndex) const /* * Selected type may have changed due to loaded files changing */ - m_selectedChartDataType[tabIndex] = chartDataType; + m_selectedChartOneDataType[tabIndex] = chartDataType; return chartDataType; } + +/** + * Update the chart overlay sets. + * + * @param tabIndex + * Index of the tab. + */ +void +ModelChart::updateChartOverlaySets(const int32_t /*tabIndex*/) +{ +} + /** * Get the data series chart model selected in the given tab. * @@ -1784,12 +1761,6 @@ ModelChart::getSelectedFrequencySeriesChartModelHelper(const int32_t tabIndex) c return m_chartModelFrequencySeries[tabIndex]; } - - - - - - /** * Get the time series chart model selected in the given tab. * @@ -1873,3 +1844,4 @@ ModelChart::getChartableMatrixSeriesFileSelectionModel(const int32_t tabIndex) return m_chartableMatrixSeriesFileSelectionModel[tabIndex]; } + diff --git a/src/Brain/ModelChart.h b/src/Brain/ModelChart.h index a3d9077f7803f34c11cd377ff5f5490ab8a4c79c..b654dd7a7a4f311db35b3c30201e57e7dda3ceef 100644 --- a/src/Brain/ModelChart.h +++ b/src/Brain/ModelChart.h @@ -21,6 +21,8 @@ */ /*LICENSE_END*/ +#include + #include #include @@ -28,7 +30,7 @@ #include #include -#include "ChartDataTypeEnum.h" +#include "ChartOneDataTypeEnum.h" #include "EventListenerInterface.h" #include "MapYokingGroupEnum.h" #include "Model.h" @@ -89,12 +91,12 @@ namespace caret { virtual void receiveEvent(Event* event); - void getValidChartDataTypes(std::vector& validChartDataTypesOut) const; + void getValidChartOneDataTypes(std::vector& validChartDataTypesOut) const; - ChartDataTypeEnum::Enum getSelectedChartDataType(const int32_t tabIndex) const; + ChartOneDataTypeEnum::Enum getSelectedChartOneDataType(const int32_t tabIndex) const; - void setSelectedChartDataType(const int32_t tabIndex, - const ChartDataTypeEnum::Enum dataType); + void setSelectedChartOneDataType(const int32_t tabIndex, + const ChartOneDataTypeEnum::Enum dataType); ChartModelDataSeries* getSelectedDataSeriesChartModel(const int32_t tabIndex); @@ -145,13 +147,15 @@ namespace caret { const ChartModelTimeSeries* getSelectedTimeSeriesChartModelHelper(const int32_t tabIndex) const; + void updateChartOverlaySets(const int32_t tabIndex); + void saveChartModelsToScene(const SceneAttributes* sceneAttributes, SceneClass* sceneClass, const std::vector& tabIndices, std::set& validChartDataIDsOut); - void restoreChartModelsFromScene(const SceneAttributes* sceneAttributes, - const SceneClass* sceneClass); + void restoreVersionOneChartModelsFromScene(const SceneAttributes* sceneAttributes, + const SceneClass* sceneClass); void getTabsAndBrainordinateChartFilesForLineChartLoading(std::map >& chartBrainordinateFileEnabledTabsOut) const; @@ -161,10 +165,16 @@ namespace caret { ChartData* loadCartesianChartWhenRestoringScene(const ChartData* chartData); + virtual void saveVersionOneModelSpecificInformationToScene(const SceneAttributes* sceneAttributes, + SceneClass* sceneClass); + + void restoreVersionOneModelSpecificInformationFromScene(const SceneAttributes* sceneAttributes, + const SceneClass* sceneClass); + /** Overlays sets for this model and for each tab */ OverlaySetArray* m_overlaySetArray; - mutable ChartDataTypeEnum::Enum m_selectedChartDataType[BrainConstants::MAXIMUM_NUMBER_OF_BROWSER_TABS]; + mutable ChartOneDataTypeEnum::Enum m_selectedChartOneDataType[BrainConstants::MAXIMUM_NUMBER_OF_BROWSER_TABS]; /** Chart model for data-series data */ ChartModelDataSeries* m_chartModelDataSeries[BrainConstants::MAXIMUM_NUMBER_OF_BROWSER_TABS]; @@ -190,7 +200,9 @@ namespace caret { CaretDataFileSelectionModel* m_chartableMatrixSeriesFileSelectionModel[BrainConstants::MAXIMUM_NUMBER_OF_BROWSER_TABS]; - SceneClassAssistant* m_sceneAssistant; + std::unique_ptr m_sceneAssistant; + + friend class ModelChartTwo; }; } // namespace diff --git a/src/Brain/ModelChartTwo.cxx b/src/Brain/ModelChartTwo.cxx new file mode 100644 index 0000000000000000000000000000000000000000..d4038cbcff6f1e72bd549f9e08a362c86268cef4 --- /dev/null +++ b/src/Brain/ModelChartTwo.cxx @@ -0,0 +1,1197 @@ +/*LICENSE_START*/ +/* + * Copyright (C) 2014 Washington University School of Medicine + * + * 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 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ +/*LICENSE_END*/ + +#include +#include +#include + +#include "AnnotationColorBar.h" +#include "Brain.h" +#include "BrowserTabContent.h" +#include "CaretAssert.h" +#include "CaretLogger.h" +#include "CaretMappableDataFile.h" +#include "CaretDataFileSelectionModel.h" +#include "CaretLogger.h" +#include "ChartData.h" +#include "ChartDataCartesian.h" +#include "ChartDataSource.h" +#include "ChartableTwoFileDelegate.h" +#include "ChartableMatrixParcelInterface.h" +#include "ChartableMatrixSeriesInterface.h" +#include "ChartMatrixDisplayProperties.h" +#include "ChartModelDataSeries.h" +#include "ChartModelFrequencySeries.h" +#include "ChartModelTimeSeries.h" +#include "ChartableTwoFileDelegate.h" +#include "ChartableTwoFileMatrixChart.h" +#include "ChartableTwoFileLineSeriesChart.h" +#include "ChartTwoLineSeriesHistory.h" +#include "ChartTwoOverlay.h" +#include "ChartTwoOverlaySet.h" +#include "ChartTwoOverlaySetArray.h" +#include "ChartingVersionEnum.h" +#include "ConnectivityDataLoaded.h" +#include "CiftiMappableConnectivityMatrixDataFile.h" +#include "CiftiMappableDataFile.h" +#include "CiftiScalarDataSeriesFile.h" +#include "EventBrowserTabGet.h" +#include "EventBrowserTabGetAll.h" +#include "EventBrowserTabIndicesGetAll.h" +#include "EventCaretMappableDataFilesGet.h" +#include "EventChartTwoLoadLineSeriesData.h" +#include "EventManager.h" +#include "EventNodeIdentificationColorsGetFromCharts.h" +#include "MapFileDataSelector.h" +#include "ModelChart.h" +#include "ModelChartTwo.h" +#include "OverlaySet.h" +#include "OverlaySetArray.h" +#include "PlainTextStringBuilder.h" +#include "SceneClass.h" +#include "SceneClassArray.h" +#include "SceneClassAssistant.h" +#include "SceneObjectMapIntegerKey.h" +#include "SurfaceFile.h" + +using namespace caret; + +/** + * Constructor. + * + */ +ModelChartTwo::ModelChartTwo(Brain* brain) +: Model(ModelTypeEnum::MODEL_TYPE_CHART_TWO, + brain) +{ + std::vector overlaySurfaceStructures; + m_overlaySetArray = new OverlaySetArray(overlaySurfaceStructures, + Overlay::INCLUDE_VOLUME_FILES_YES, + "Chart View"); + + m_histogramChartOverlaySetArray = std::unique_ptr(new ChartTwoOverlaySetArray(ChartTwoDataTypeEnum::CHART_DATA_TYPE_HISTOGRAM, + "Histogram Chart Overlays")); + m_matrixChartOverlaySetArray = std::unique_ptr(new ChartTwoOverlaySetArray(ChartTwoDataTypeEnum::CHART_DATA_TYPE_MATRIX, + "Matrix Chart Overlays")); + m_lineSeriesChartOverlaySetArray = std::unique_ptr(new ChartTwoOverlaySetArray(ChartTwoDataTypeEnum::CHART_DATA_TYPE_LINE_SERIES, + "Line Series Chart Overlays")); + + initializeCharts(); + + m_sceneAssistant = std::unique_ptr(new SceneClassAssistant()); + m_sceneAssistant->addTabIndexedEnumeratedTypeArray("m_selectedChartTwoDataType", + m_selectedChartTwoDataType); + EventManager::get()->addEventListener(this, + EventTypeEnum::EVENT_NODE_IDENTIFICATION_COLORS_GET_FROM_CHARTS); +} + +/** + * Destructor + */ +ModelChartTwo::~ModelChartTwo() +{ + delete m_overlaySetArray; + EventManager::get()->removeAllEventsFromListener(this); + + removeAllCharts(); +} + +void +ModelChartTwo::initializeCharts() +{ + for (int32_t i = 0; i < BrainConstants::MAXIMUM_NUMBER_OF_BROWSER_TABS; i++) { + m_selectedChartTwoDataType[i] = ChartTwoDataTypeEnum::CHART_DATA_TYPE_INVALID; + } +} + +/** + * Reset this model. + */ +void +ModelChartTwo::reset() +{ + removeAllCharts(); + + initializeCharts(); +} + +/** + * Remove all of the charts. + */ +void +ModelChartTwo::removeAllCharts() +{ +} + +/** + * Load chart data for a surface node. + * + * @param structure + * The surface structure + * @param surfaceNumberOfNodes + * Number of nodes in surface. + * @param nodeIndex + * Index of node. + * @throws + * DataFileException if there is an error loading data. + */ +void +ModelChartTwo::loadChartDataForSurfaceNode(const StructureEnum::Enum structure, + const int32_t surfaceNumberOfNodes, + const int32_t nodeIndex) +{ + std::vector tabIndices; + EventBrowserTabIndicesGetAll tabIndicesEvent; + EventManager::get()->sendEvent(tabIndicesEvent.getPointer()); + + MapFileDataSelector mapFileDataSelector; + mapFileDataSelector.setSurfaceVertex(structure, + surfaceNumberOfNodes, + nodeIndex); + EventChartTwoLoadLineSeriesData chartTwoLineSeriesEvent(tabIndicesEvent.getAllBrowserTabIndices(), + mapFileDataSelector); + EventManager::get()->sendEvent(chartTwoLineSeriesEvent.getPointer()); +} + +/** + * Load chart data for an average of surface nodes. + * + * @param structure + * The surface structure + * @param surfaceNumberOfNodes + * Number of nodes in surface. + * @param nodeIndices + * Indices of node. + * @throws + * DataFileException if there is an error loading data. + */ +void +ModelChartTwo::loadAverageChartDataForSurfaceNodes(const StructureEnum::Enum structure, + const int32_t surfaceNumberOfNodes, + const std::vector& nodeIndices) +{ + std::vector tabIndices; + EventBrowserTabIndicesGetAll tabIndicesEvent; + EventManager::get()->sendEvent(tabIndicesEvent.getPointer()); + + MapFileDataSelector mapFileDataSelector; + mapFileDataSelector.setSurfaceVertexAverage(structure, + surfaceNumberOfNodes, + nodeIndices); + EventChartTwoLoadLineSeriesData chartTwoLineSeriesEvent(tabIndicesEvent.getAllBrowserTabIndices(), + mapFileDataSelector); + EventManager::get()->sendEvent(chartTwoLineSeriesEvent.getPointer()); +} + +/** + * Load chart data for voxel at the given coordinate. + * + * @param xyz + * Coordinate of voxel. + * @throws + * DataFileException if there is an error loading data. + */ +void +ModelChartTwo::loadChartDataForVoxelAtCoordinate(const float xyz[3]) +{ + std::vector tabIndices; + EventBrowserTabIndicesGetAll tabIndicesEvent; + EventManager::get()->sendEvent(tabIndicesEvent.getPointer()); + + MapFileDataSelector mapFileDataSelector; + mapFileDataSelector.setVolumeVoxelXYZ(xyz); + EventChartTwoLoadLineSeriesData chartTwoLineSeriesEvent(tabIndicesEvent.getAllBrowserTabIndices(), + mapFileDataSelector); + EventManager::get()->sendEvent(chartTwoLineSeriesEvent.getPointer()); +} + +/** + * Load chart data from given file at the given row. + * + * @param ciftiMapFile + * The CIFTI file. + * @param rowIndex + * Index of row in the file. + */ +void +ModelChartTwo::loadChartDataForCiftiMappableFileRow(CiftiMappableDataFile* ciftiMapFile, + const int32_t rowIndex) +{ + CaretAssert(ciftiMapFile); + + //std::vector tabIndices; + EventBrowserTabIndicesGetAll tabIndicesEvent; + EventManager::get()->sendEvent(tabIndicesEvent.getPointer()); + + MapFileDataSelector mapFileDataSelector; + mapFileDataSelector.setRowIndex(ciftiMapFile, + ciftiMapFile->getFileName(), + rowIndex); + EventChartTwoLoadLineSeriesData chartTwoLineSeriesEvent(tabIndicesEvent.getAllBrowserTabIndices(), + mapFileDataSelector); + EventManager::get()->sendEvent(chartTwoLineSeriesEvent.getPointer()); +} + +/** + * Load line chart data for yoked CIFTI scalar data series files + * + * @param mapYokingGroup + * The map yoking group. + * @param mapIndex + * The map index. + */ +void +ModelChartTwo::loadChartDataForYokedScalarDataSeriesFiles(const MapYokingGroupEnum::Enum mapYokingGroup, + const int32_t mapIndex) +{ + if (mapYokingGroup == MapYokingGroupEnum::MAP_YOKING_GROUP_OFF) { + return; + } + + EventBrowserTabIndicesGetAll tabIndicesEvent; + EventManager::get()->sendEvent(tabIndicesEvent.getPointer()); + const std::vector tabIndices = tabIndicesEvent.getAllBrowserTabIndices(); + + /* + * Find Cifti Scalar Data Series Files in valid Chart Overlays that + * have chart line series data loading enabled + */ + std::set dataSeriesFiles; + for (auto tabIndex : tabIndices) { + CaretAssertArrayIndex(m_selectedChartTwoDataType, BrainConstants::MAXIMUM_NUMBER_OF_BROWSER_TABS, tabIndex); + if (m_selectedChartTwoDataType[tabIndex] == ChartTwoDataTypeEnum::CHART_DATA_TYPE_LINE_SERIES) { + const ChartTwoOverlaySet* overlaySet = m_lineSeriesChartOverlaySetArray->getChartTwoOverlaySet(tabIndex); + CaretAssert(overlaySet); + + const int32_t numOverlays = overlaySet->getNumberOfDisplayedOverlays(); + for (int32_t iOverlay = 0; iOverlay < numOverlays; iOverlay++) { + const ChartTwoOverlay* overlay = overlaySet->getOverlay(iOverlay); + CaretAssert(overlay); + + if (overlay->isEnabled() + && overlay->isLineSeriesLoadingEnabled()) { + if (overlay->getMapYokingGroup() == mapYokingGroup) { + CaretMappableDataFile* mapFile = overlay->getSelectedMapFile(); + if (mapFile != NULL) { + if (mapFile->getDataFileType() == DataFileTypeEnum::CONNECTIVITY_SCALAR_DATA_SERIES) { + CiftiScalarDataSeriesFile* scalarDataSeriesFile = dynamic_cast(mapFile); + CaretAssert(scalarDataSeriesFile); + dataSeriesFiles.insert(scalarDataSeriesFile); + } + } + } + } + } + } + } + + for (auto seriesFile : dataSeriesFiles) { + loadChartDataForCiftiMappableFileRow(seriesFile, mapIndex); + } +} + +/** + * Select yoked row/column in scalar data series file's overlay + * + * @param mapYokingGroup + * The map yoking group. + * @param mapIndex + * The map index. + */ +void +ModelChartTwo::selectRowColumnInYokedScalarDataSeriesFileOverlay(const MapYokingGroupEnum::Enum mapYokingGroup, + const int32_t mapIndex) +{ + if (mapYokingGroup == MapYokingGroupEnum::MAP_YOKING_GROUP_OFF) { + return; + } + + EventBrowserTabIndicesGetAll tabIndicesEvent; + EventManager::get()->sendEvent(tabIndicesEvent.getPointer()); + const std::vector tabIndices = tabIndicesEvent.getAllBrowserTabIndices(); + + /* + * Find Cifti Scalar Data Series Files in valid Chart Overlays that + * have the matching yoking group + */ + for (auto tabIndex : tabIndices) { + CaretAssertArrayIndex(m_selectedChartTwoDataType, BrainConstants::MAXIMUM_NUMBER_OF_BROWSER_TABS, tabIndex); + if (m_selectedChartTwoDataType[tabIndex] == ChartTwoDataTypeEnum::CHART_DATA_TYPE_MATRIX) { + ChartTwoOverlaySet* overlaySet = m_matrixChartOverlaySetArray->getChartTwoOverlaySet(tabIndex); + CaretAssert(overlaySet); + + const int32_t numOverlays = overlaySet->getNumberOfDisplayedOverlays(); + for (int32_t iOverlay = 0; iOverlay < numOverlays; iOverlay++) { + ChartTwoOverlay* overlay = overlaySet->getOverlay(iOverlay); + CaretAssert(overlay); + + if (overlay->isEnabled()) { + if (overlay->getMapYokingGroup() == mapYokingGroup) { + CaretMappableDataFile* mapFile = overlay->getSelectedMapFile(); + if (mapFile != NULL) { + if (mapFile->getDataFileType() == DataFileTypeEnum::CONNECTIVITY_SCALAR_DATA_SERIES) { + overlay->setSelectionData(mapFile, mapIndex); + } + } + } + } + } + + } + } +} + +/** + * Receive an event. + * + * @param event + * The event that the receive can respond to. + */ +void +ModelChartTwo::receiveEvent(Event* event) +{ + if (event->getEventType() == EventTypeEnum::EVENT_NODE_IDENTIFICATION_COLORS_GET_FROM_CHARTS) { + EventNodeIdentificationColorsGetFromCharts* nodeChartID = + dynamic_cast(event); + CaretAssert(nodeChartID); + + EventBrowserTabGetAll allTabsEvent; + EventManager::get()->sendEvent(allTabsEvent.getPointer()); + std::vector validTabIndices = allTabsEvent.getBrowserTabIndices(); + + + const AString structureName = nodeChartID->getStructureName(); + + std::vector cartesianChartData; + +// for (std::list >::iterator dsIter = m_dataSeriesChartData.begin(); +// dsIter != m_dataSeriesChartData.end(); +// dsIter++) { +// QSharedPointer spCart = dsIter->toStrongRef(); +// if ( ! spCart.isNull()) { +// cartesianChartData.push_back(spCart.data()); +// } +// } +// for (std::list >::iterator tsIter = m_frequencySeriesChartData.begin(); +// tsIter != m_frequencySeriesChartData.end(); +// tsIter++) { +// QSharedPointer spCart = tsIter->toStrongRef(); +// if ( ! spCart.isNull()) { +// cartesianChartData.push_back(spCart.data()); +// } +// } +// for (std::list >::iterator tsIter = m_timeSeriesChartData.begin(); +// tsIter != m_timeSeriesChartData.end(); +// tsIter++) { +// QSharedPointer spCart = tsIter->toStrongRef(); +// if ( ! spCart.isNull()) { +// cartesianChartData.push_back(spCart.data()); +// } +// } +// +// +// /* +// * Iterate over node indices for which colors are desired. +// */ +// const std::vector nodeIndices = nodeChartID->getNodeIndices(); +// for (std::vector::const_iterator nodeIter = nodeIndices.begin(); +// nodeIter != nodeIndices.end(); +// nodeIter++) { +// const int32_t nodeIndex = *nodeIter; +// +// /* +// * Iterate over the data in the cartesian chart +// */ +// for (std::vector::iterator cdIter = cartesianChartData.begin(); +// cdIter != cartesianChartData.end(); +// cdIter++) { +// const ChartDataCartesian* cdc = *cdIter; +// const ChartDataSource* cds = cdc->getChartDataSource(); +// if (cds->isSurfaceNodeSourceOfData(structureName, nodeIndex)) { +// /* +// * Found node index so add its color to the event +// */ +// const CaretColorEnum::Enum color = cdc->getColor(); +// const float* rgb = CaretColorEnum::toRGB(color); +// nodeChartID->addNode(nodeIndex, +// rgb); +// break; +// } +// } +// } + + nodeChartID->setEventProcessed(); + } +} + +/** + * Get the name for use in a GUI. + * + * @param includeStructureFlag - Prefix label with structure to which + * this structure model belongs. + * @return Name for use in a GUI. + * + */ +AString +ModelChartTwo::getNameForGUI(const bool /*includeStructureFlag*/) const +{ + AString name = "Chart"; + return name; +} + +/** + * @return The name that should be displayed in the tab + * displaying this model. + */ +AString +ModelChartTwo::getNameForBrowserTab() const +{ + AString name = "Chart"; + return name; +} + +/** + * Get the overlay set for the given tab. + * @param tabIndex + * Index of tab. + * @return + * Overlay set at the given tab index. + */ +OverlaySet* +ModelChartTwo::getOverlaySet(const int tabIndex) +{ + CaretAssertArrayIndex(m_overlaySetArray, + BrainConstants::MAXIMUM_NUMBER_OF_BROWSER_TABS, + tabIndex); + return m_overlaySetArray->getOverlaySet(tabIndex); +} + +/** + * Get the overlay set for the given tab. + * @param tabIndex + * Index of tab. + * @return + * Overlay set at the given tab index. + */ +const OverlaySet* +ModelChartTwo::getOverlaySet(const int tabIndex) const +{ + CaretAssertArrayIndex(m_overlaySetArray, + BrainConstants::MAXIMUM_NUMBER_OF_BROWSER_TABS, + tabIndex); + return m_overlaySetArray->getOverlaySet(tabIndex); +} + +/** + * Initilize the overlays for this model. + */ +void +ModelChartTwo::initializeOverlays() +{ + m_overlaySetArray->initializeOverlaySelections(); +} + +/** + * Save information specific to this type of model to the scene. + * + * @param sceneAttributes + * Attributes for the scene. Scenes may be of different types + * (full, generic, etc) and the attributes should be checked when + * saving the scene. + * + * @param sceneClass + * SceneClass to which model specific information is added. + */ +void +ModelChartTwo::saveModelSpecificInformationToScene(const SceneAttributes* sceneAttributes, + SceneClass* sceneClass) +{ + const std::vector validTabIndices = sceneAttributes->getIndicesOfTabsForSavingToScene(); + + std::vector histogramChartTabIndices; + std::vector lineSeriesChartTabIndices; + std::vector matrixChartTabIndices; + for (auto tabIndex : validTabIndices) { + switch (m_selectedChartTwoDataType[tabIndex]) { + case ChartTwoDataTypeEnum::CHART_DATA_TYPE_INVALID: + break; + case ChartTwoDataTypeEnum::CHART_DATA_TYPE_HISTOGRAM: + histogramChartTabIndices.push_back(tabIndex); + break; + case ChartTwoDataTypeEnum::CHART_DATA_TYPE_LINE_SERIES: + lineSeriesChartTabIndices.push_back(tabIndex); + break; + case ChartTwoDataTypeEnum::CHART_DATA_TYPE_MATRIX: + matrixChartTabIndices.push_back(tabIndex); + break; + } + } + + if ( ! histogramChartTabIndices.empty()) { + SceneClass* histogramClass = m_histogramChartOverlaySetArray->saveTabIndicesToScene(histogramChartTabIndices, + sceneAttributes, + "m_histogramChartOverlaySetArray"); + if (histogramClass != NULL) { + sceneClass->addClass(histogramClass); + } + } + + if ( ! lineSeriesChartTabIndices.empty()) { + SceneClass* lineSeriesClass = m_lineSeriesChartOverlaySetArray->saveTabIndicesToScene(lineSeriesChartTabIndices, + sceneAttributes, + "m_lineSeriesChartOverlaySetArray"); + if (lineSeriesClass != NULL) { + sceneClass->addClass(lineSeriesClass); + } + } + + if ( ! matrixChartTabIndices.empty()) { + SceneClass* matrixClass = m_matrixChartOverlaySetArray->saveTabIndicesToScene(matrixChartTabIndices, + sceneAttributes, + "m_matrixChartOverlaySetArray"); + if (matrixClass != NULL) { + sceneClass->addClass(matrixClass); + } + } + + m_sceneAssistant->saveMembers(sceneAttributes, + sceneClass); +} + +/** + * Restore information specific to the type of model from the scene. + * + * @param sceneAttributes + * Attributes for the scene. Scenes may be of different types + * (full, generic, etc) and the attributes should be checked when + * restoring the scene. + * + * @param sceneClass + * sceneClass from which model specific information is obtained. + */ +void +ModelChartTwo::restoreModelSpecificInformationFromScene(const SceneAttributes* sceneAttributes, + const SceneClass* sceneClass) +{ + reset(); + + m_sceneAssistant->restoreMembers(sceneAttributes, + sceneClass); + + m_histogramChartOverlaySetArray->restoreFromScene(sceneAttributes, + sceneClass->getClass("m_histogramChartOverlaySetArray")); + m_lineSeriesChartOverlaySetArray->restoreFromScene(sceneAttributes, + sceneClass->getClass("m_lineSeriesChartOverlaySetArray")); + m_matrixChartOverlaySetArray->restoreFromScene(sceneAttributes, + sceneClass->getClass("m_matrixChartOverlaySetArray")); +} + +/** + * Try to restore a scene from a version one chart model + * by copying data from the chart one model. + * + * @param modelChartOne + * The charting version one model. + */ +void +ModelChartTwo::restoreSceneFromChartOneModel(ModelChart* modelChartOne) +{ + if (isRestoredFromScene()) { + CaretAssert(0); + } + setRestoredFromScene(true); + + EventBrowserTabGetAll allTabsEvent; + EventManager::get()->sendEvent(allTabsEvent.getPointer()); + std::vector validTabIndices = allTabsEvent.getBrowserTabIndices(); + + bool haveLineSeriesModelsFlag = true; + + for (auto tabIndex : validTabIndices) { + switch (modelChartOne->getSelectedChartOneDataType(tabIndex)) { + case ChartOneDataTypeEnum::CHART_DATA_TYPE_INVALID: + break; + case ChartOneDataTypeEnum::CHART_DATA_TYPE_LINE_DATA_SERIES: + case ChartOneDataTypeEnum::CHART_DATA_TYPE_LINE_FREQUENCY_SERIES: + case ChartOneDataTypeEnum::CHART_DATA_TYPE_LINE_TIME_SERIES: + setSelectedChartTwoDataType(tabIndex, + ChartTwoDataTypeEnum::CHART_DATA_TYPE_LINE_SERIES); + haveLineSeriesModelsFlag = true; + break; + case ChartOneDataTypeEnum::CHART_DATA_TYPE_MATRIX_LAYER: + setSelectedChartTwoDataType(tabIndex, + ChartTwoDataTypeEnum::CHART_DATA_TYPE_MATRIX); + restoreMatrixChartFromChartOneModel(modelChartOne, + tabIndex); + break; + case ChartOneDataTypeEnum::CHART_DATA_TYPE_MATRIX_SERIES: + setSelectedChartTwoDataType(tabIndex, + ChartTwoDataTypeEnum::CHART_DATA_TYPE_MATRIX); + restoreMatrixChartFromChartOneModel(modelChartOne, + tabIndex); + break; + } + } + + if (haveLineSeriesModelsFlag) { + restoreLineSeriesChartFromChartOneModel(modelChartOne); + } +} + +/** + * Attempt to restore line series data from a chart version one scene. + * + * @param modelChartOne + * The chart one model. + */ +void +ModelChartTwo::restoreLineSeriesChartFromChartOneModel(ModelChart* modelChartOne) +{ + CaretAssert(modelChartOne); + + std::set dataSources; + + int32_t maxDisplayCount = 0; + + std::vector validTabIndices; + std::map dataFileNamesAndColors; + + /* + * Loop through all tabs to find those containing line-series style charts + */ + for (int32_t iTab = 0; iTab < BrainConstants::MAXIMUM_NUMBER_OF_BROWSER_TABS; iTab++) { + ChartModelCartesian* cartesianModel = NULL; + switch (modelChartOne->getSelectedChartOneDataType(iTab)) { + case ChartOneDataTypeEnum::CHART_DATA_TYPE_INVALID: + case ChartOneDataTypeEnum::CHART_DATA_TYPE_LINE_DATA_SERIES: + cartesianModel = modelChartOne->getSelectedDataSeriesChartModel(iTab); + break; + case ChartOneDataTypeEnum::CHART_DATA_TYPE_LINE_FREQUENCY_SERIES: + cartesianModel = modelChartOne->getSelectedFrequencySeriesChartModel(iTab); + break; + case ChartOneDataTypeEnum::CHART_DATA_TYPE_LINE_TIME_SERIES: + cartesianModel = modelChartOne->getSelectedTimeSeriesChartModel(iTab); + break; + case ChartOneDataTypeEnum::CHART_DATA_TYPE_MATRIX_LAYER: + break; + case ChartOneDataTypeEnum::CHART_DATA_TYPE_MATRIX_SERIES: + break; + } + + if (cartesianModel != NULL) { + validTabIndices.push_back(iTab); + + maxDisplayCount = std::max(maxDisplayCount, + cartesianModel->getMaximumNumberOfChartDatasToDisplay()); + + /* + * Examine all lines in the tab + */ + const int32_t numChartLines = cartesianModel->getNumberOfChartData(); + for (int32_t i = (numChartLines - 1); i >= 0; i--) { + const ChartData* chartData = cartesianModel->getChartDataAtIndex(i); + if (chartData != NULL) { + const ChartDataSource* chartDataSource = chartData->getChartDataSource(); + if (chartData->isSelected(iTab)) { + ChartDataSource dataSourceCopy(*chartDataSource); + dataSources.insert(dataSourceCopy); + const ChartDataCartesian* cartesianData = dynamic_cast(chartData); + CaretColorEnum::Enum color = CaretColorEnum::RED; + if (cartesianData != NULL) { + color = cartesianData->getColor(); + } + dataFileNamesAndColors.insert(std::make_pair(chartDataSource->getChartableFileName(), + color)); + } + } + } + } + } + + std::vector allDataFiles; + EventCaretMappableDataFilesGet eventGetMapDataFiles; + EventManager::get()->sendEvent(eventGetMapDataFiles.getPointer()); + eventGetMapDataFiles.getAllFiles(allDataFiles); + + std::vector overlayMapFiles; + + /* + * Find the data files and setup for loading of + * lines series data. + */ + for (const auto filenameAndColor : dataFileNamesAndColors) { + const AString filename = filenameAndColor.first; + CaretColorEnum::Enum color = filenameAndColor.second; + CaretMappableDataFile* mapFileFullPath = NULL; + CaretMappableDataFile* mapFileNoPath = NULL; + for (auto mapFile : allDataFiles) { + if (filename == mapFile->getFileName()) { + mapFileFullPath = mapFile; + } + if (mapFile->getFileName().endsWith(filename)) { + mapFileNoPath = mapFile; + } + } + + ChartTwoLineSeriesHistory* lineSeriesHistory = NULL; + if (mapFileFullPath != NULL) { + lineSeriesHistory = mapFileFullPath->getChartingDelegate()->getLineSeriesCharting()->getHistory(); + overlayMapFiles.push_back(mapFileFullPath); + } + else if (mapFileNoPath != NULL) { + lineSeriesHistory = mapFileNoPath->getChartingDelegate()->getLineSeriesCharting()->getHistory(); + overlayMapFiles.push_back(mapFileNoPath); + } + else { + CaretLogWarning("Unable to find data file with name: " + + filename); + } + if (lineSeriesHistory != NULL) { + lineSeriesHistory->setLoadingEnabled(true); + if (maxDisplayCount <= 0) { + maxDisplayCount = 1; + } + lineSeriesHistory->setDisplayCount(maxDisplayCount); + lineSeriesHistory->setDefaultColor(color); + } + } + + /* + * Send events to load line charts for the brainordinates + * found in the older charts + */ + for (const auto& ds : dataSources) { + MapFileDataSelector mapFileDataSelector; + switch (ds.getDataSourceMode()) { + case ChartDataSourceModeEnum::CHART_DATA_SOURCE_MODE_FILE_ROW: + break; + case ChartDataSourceModeEnum::CHART_DATA_SOURCE_MODE_INVALID: + break; + case ChartDataSourceModeEnum::CHART_DATA_SOURCE_MODE_SURFACE_NODE_INDEX: + { + int32_t surfaceNumberOfVertices = -1; + int32_t vertexIndex = -1; + AString structureName; + ds.getSurfaceNode(structureName, surfaceNumberOfVertices, vertexIndex); + bool validFlag = false; + const StructureEnum::Enum structure = StructureEnum::fromName(structureName, + &validFlag); + if (validFlag) { + mapFileDataSelector.setSurfaceVertex(structure, + surfaceNumberOfVertices, + vertexIndex); + } + else { + CaretLogWarning("Unrecogized structure name :" + + structureName); + } + } + break; + case ChartDataSourceModeEnum::CHART_DATA_SOURCE_MODE_SURFACE_NODE_INDICES_AVERAGE: + { + int32_t surfaceNumberOfVertices = -1; + AString structureName; + std::vector vertices; + ds.getSurfaceNodeAverage(structureName, surfaceNumberOfVertices, vertices); + bool validFlag = false; + const StructureEnum::Enum structure = StructureEnum::fromName(structureName, + &validFlag); + if (validFlag) { + mapFileDataSelector.setSurfaceVertexAverage(structure, + surfaceNumberOfVertices, + vertices); + } + else { + CaretLogWarning("Unrecogized structure name :" + + structureName); + } + } + break; + case ChartDataSourceModeEnum::CHART_DATA_SOURCE_MODE_VOXEL_IJK: + { + float xyz[3]; + ds.getVolumeVoxel(xyz); + mapFileDataSelector.setVolumeVoxelXYZ(xyz); + } + break; + } + + if (mapFileDataSelector.getDataSelectionType() != MapFileDataSelector::DataSelectionType::INVALID) { + EventChartTwoLoadLineSeriesData loadLineDataEvent(validTabIndices, + mapFileDataSelector); + EventManager::get()->sendEvent(loadLineDataEvent.getPointer()); + } + } + + /* + * Put line-series chart files into chart overlays + */ + const int32_t maxChartOverlays = 3; + const int32_t numChartOverlayMapFiles = std::min(maxChartOverlays, static_cast(overlayMapFiles.size())); + for (auto tabIndex : validTabIndices) { + for (int32_t i = 0; i < numChartOverlayMapFiles; i++) { + CaretAssertVectorIndex(overlayMapFiles, i); + getChartTwoOverlaySet(tabIndex)->getOverlay(i)->setSelectionData(overlayMapFiles[i], 0); + } + } +} + +/** + * Restore matrix from model one chart by copying from the model. + * + * @param modelChartOne + * The charting version one model. + * @param tabIndex + * Index of tab being restored + */ +void +ModelChartTwo::restoreMatrixChartFromChartOneModel(ModelChart* modelChartOne, + const int32_t tabIndex) +{ + CaretAssert(modelChartOne); + + CaretDataFileSelectionModel* fileSelectionModel = NULL; + switch (modelChartOne->getSelectedChartOneDataType(tabIndex)) { + case ChartOneDataTypeEnum::CHART_DATA_TYPE_INVALID: + case ChartOneDataTypeEnum::CHART_DATA_TYPE_LINE_DATA_SERIES: + case ChartOneDataTypeEnum::CHART_DATA_TYPE_LINE_FREQUENCY_SERIES: + case ChartOneDataTypeEnum::CHART_DATA_TYPE_LINE_TIME_SERIES: + CaretAssert(0); + break; + case ChartOneDataTypeEnum::CHART_DATA_TYPE_MATRIX_LAYER: + fileSelectionModel = modelChartOne->getChartableMatrixParcelFileSelectionModel(tabIndex); + break; + case ChartOneDataTypeEnum::CHART_DATA_TYPE_MATRIX_SERIES: + fileSelectionModel = modelChartOne->getChartableMatrixSeriesFileSelectionModel(tabIndex); + break; + } + + CaretDataFile* caretDataFile = fileSelectionModel->getSelectedFile(); + if (caretDataFile == NULL) { + return; + } + CaretMappableDataFile* mapFile = dynamic_cast(caretDataFile); + if (mapFile == NULL) { + return; + } + + ChartableTwoFileMatrixChart* fileMatrixChart = mapFile->getChartingDelegate()->getMatrixCharting(); + if (fileMatrixChart == NULL) { + return; + } + + ChartableMatrixParcelInterface* chartOneMatrixParcelFile = dynamic_cast(mapFile); + ChartableMatrixSeriesInterface* chartOneMatrixSeriesFile = dynamic_cast(mapFile); + + int32_t selectedRowColumnIndex = -1; + MapYokingGroupEnum::Enum chartOneYoking = MapYokingGroupEnum::MAP_YOKING_GROUP_OFF; + ChartMatrixDisplayProperties* chartOneMatrixDisplayProperties = NULL; + if (chartOneMatrixParcelFile != NULL) { + chartOneMatrixDisplayProperties = chartOneMatrixParcelFile->getChartMatrixDisplayProperties(tabIndex); + + switch (chartOneMatrixParcelFile->getMatrixLoadingDimension()) { + case ChartMatrixLoadingDimensionEnum::CHART_MATRIX_LOADING_BY_COLUMN: + fileMatrixChart->setSelectedRowColumnDimension(ChartTwoMatrixLoadingDimensionEnum::CHART_MATRIX_LOADING_BY_COLUMN); + break; + case ChartMatrixLoadingDimensionEnum::CHART_MATRIX_LOADING_BY_ROW: + fileMatrixChart->setSelectedRowColumnDimension(ChartTwoMatrixLoadingDimensionEnum::CHART_MATRIX_LOADING_BY_ROW); + break; + } + + CiftiMappableConnectivityMatrixDataFile* connMatrixFile = dynamic_cast(mapFile); + if (connMatrixFile != NULL) { + const ConnectivityDataLoaded* connDataLoaded = connMatrixFile->getConnectivityDataLoaded(); + if (connDataLoaded != NULL) { + int64_t loadedRowIndex = -1; + int64_t loadedColumnIndex = -1; + connDataLoaded->getRowColumnLoading(loadedRowIndex, + loadedColumnIndex); + if (loadedRowIndex >= 0) { + selectedRowColumnIndex = loadedRowIndex; + } + else if (loadedColumnIndex >= 0) { + selectedRowColumnIndex = loadedColumnIndex; + } + } + } + } + else if (chartOneMatrixSeriesFile != NULL) { + chartOneMatrixDisplayProperties = chartOneMatrixSeriesFile->getChartMatrixDisplayProperties(tabIndex); + + chartOneYoking = chartOneMatrixSeriesFile->getMatrixRowColumnMapYokingGroup(tabIndex); + fileMatrixChart->setSelectedRowColumnDimension(ChartTwoMatrixLoadingDimensionEnum::CHART_MATRIX_LOADING_BY_ROW); + selectedRowColumnIndex = chartOneMatrixSeriesFile->getSelectedMapIndex(tabIndex); + } + else { + CaretAssertMessage(0, "Has new matrix chart type been added?"); + return; + } + + + CaretAssert(chartOneMatrixDisplayProperties); + AnnotationColorBar* chartOneColorBar = chartOneMatrixDisplayProperties->getColorBar(); + + EventBrowserTabGet getTabEvent(tabIndex); + EventManager::get()->sendEvent(getTabEvent.getPointer()); + BrowserTabContent* browserTabContent = getTabEvent.getBrowserTab(); + if (browserTabContent != NULL) { + ChartTwoOverlaySet* chartTwoOverlaySet = browserTabContent->getChartTwoOverlaySet(); + CaretAssert(chartTwoOverlaySet); + for (int32_t iOverlay = 0; iOverlay < BrainConstants::MAXIMUM_NUMBER_OF_OVERLAYS; iOverlay++) { + ChartTwoOverlay* chartTwoOverlay = chartTwoOverlaySet->getOverlay(iOverlay); + CaretAssert(chartTwoOverlay); + if (iOverlay == 0) { + chartTwoOverlay->setEnabled(true); + chartTwoOverlay->setSelectionData(mapFile, + selectedRowColumnIndex); + chartTwoOverlay->setMatrixTriangularViewingMode(ChartTwoMatrixTriangularViewingModeEnum::MATRIX_VIEW_FULL); + chartTwoOverlay->setAllMapsSelected(false); + chartTwoOverlay->setCartesianVerticalAxisLocation(ChartAxisLocationEnum::CHART_AXIS_LOCATION_LEFT); + chartTwoOverlay->setMapYokingGroup(chartOneYoking); + AnnotationColorBar* chartTwoColorBar = chartTwoOverlay->getColorBar(); + if ((chartOneColorBar != NULL) + && (chartTwoColorBar != NULL)) { + *chartTwoColorBar = *chartOneColorBar; + } + } + else { + chartTwoOverlay->setEnabled(false); + } + } + } +} + +/** + * Copy the tab content from the source tab index to the + * destination tab index. + * + * @param sourceTabIndex + * Source from which tab content is copied. + * @param destinationTabIndex + * Destination to which tab content is copied. + */ +void +ModelChartTwo::copyTabContent(const int32_t sourceTabIndex, + const int32_t destinationTabIndex) +{ + Model::copyTabContent(sourceTabIndex, + destinationTabIndex); + + m_overlaySetArray->copyOverlaySet(sourceTabIndex, + destinationTabIndex); + CaretAssertArrayIndex(m_selectedChartTwoDataType, BrainConstants::MAXIMUM_NUMBER_OF_BROWSER_TABS, sourceTabIndex); + CaretAssertArrayIndex(m_selectedChartTwoDataType, BrainConstants::MAXIMUM_NUMBER_OF_BROWSER_TABS, destinationTabIndex); + m_selectedChartTwoDataType[destinationTabIndex] = m_selectedChartTwoDataType[sourceTabIndex]; + + m_histogramChartOverlaySetArray->copyChartOverlaySet(sourceTabIndex, + destinationTabIndex); + m_lineSeriesChartOverlaySetArray->copyChartOverlaySet(sourceTabIndex, + destinationTabIndex); + m_matrixChartOverlaySetArray->copyChartOverlaySet(sourceTabIndex, + destinationTabIndex); +} + +/** + * Copy cartesian axes from one tab to another. + * + * @param sourceTabIndex + * Source from which tab content is copied. + * @param destinationTabIndex + * Destination to which tab content is copied. + */ +void +ModelChartTwo::copyChartTwoCartesianAxes(const int32_t sourceTabIndex, + const int32_t destinationTabIndex) +{ + CaretAssertArrayIndex(m_histogramChartOverlaySetArray, BrainConstants::MAXIMUM_NUMBER_OF_BROWSER_TABS, sourceTabIndex); + CaretAssertArrayIndex(m_lineSeriesChartOverlaySetArray, BrainConstants::MAXIMUM_NUMBER_OF_BROWSER_TABS, destinationTabIndex); + + m_histogramChartOverlaySetArray->copyChartOverlaySetCartesianAxes(sourceTabIndex, + destinationTabIndex); + m_lineSeriesChartOverlaySetArray->copyChartOverlaySetCartesianAxes(sourceTabIndex, + destinationTabIndex); +} + +/** + * Set the type of chart selected in the given tab. + * + * @param tabIndex + * Index of tab. + * @param dataType + * Type of data for chart. + */ +void +ModelChartTwo::setSelectedChartTwoDataType(const int32_t tabIndex, + const ChartTwoDataTypeEnum::Enum dataType) +{ + CaretAssertArrayIndex(m_selectedChartTwoDataType, + BrainConstants::MAXIMUM_NUMBER_OF_BROWSER_TABS, + tabIndex); + m_selectedChartTwoDataType[tabIndex] = dataType; +} + +/** + * Get the valid chart data types based upon the currently loaded files. + * + * @param validChartDataTypesOut + * Output containing valid chart data types. + */ +void +ModelChartTwo::getValidChartTwoDataTypes(std::vector& validChartDataTypesOut) const +{ + validChartDataTypesOut.clear(); + + /** + * Get the data files. + */ + std::vector allDataFiles; + EventCaretMappableDataFilesGet eventGetMapDataFiles; + EventManager::get()->sendEvent(eventGetMapDataFiles.getPointer()); + eventGetMapDataFiles.getAllFiles(allDataFiles); + + std::set chartTypeSet; + + for (auto mapFile : allDataFiles) { + ChartableTwoFileDelegate* chartFile = mapFile->getChartingDelegate(); + std::vector fileChartTypes; + chartFile->getSupportedChartTwoDataTypes(fileChartTypes); + + chartTypeSet.insert(fileChartTypes.begin(), + fileChartTypes.end()); + } + + validChartDataTypesOut.insert(validChartDataTypesOut.end(), + chartTypeSet.begin(), + chartTypeSet.end()); +} + +/** + * Get the type of chart selected in the given tab. + * + * @param tabIndex + * Index of tab. + * @return + * Chart type in the given tab. + */ +ChartTwoDataTypeEnum::Enum +ModelChartTwo::getSelectedChartTwoDataType(const int32_t tabIndex) const +{ + CaretAssertArrayIndex(m_selectedChartTwoDataType, + BrainConstants::MAXIMUM_NUMBER_OF_BROWSER_TABS, + tabIndex); + ChartTwoDataTypeEnum::Enum chartDataType = m_selectedChartTwoDataType[tabIndex]; + + std::vector validChartDataTypes; + getValidChartTwoDataTypes(validChartDataTypes); + + /* + * Test if selected chart type is still valid + */ + if (std::find(validChartDataTypes.begin(), + validChartDataTypes.end(), + chartDataType) == validChartDataTypes.end()) { + chartDataType = ChartTwoDataTypeEnum::CHART_DATA_TYPE_INVALID; + } + + /* + * Find a valid chart type? + */ + if (chartDataType == ChartTwoDataTypeEnum::CHART_DATA_TYPE_INVALID) { + if ( ! validChartDataTypes.empty()) { + CaretAssertVectorIndex(validChartDataTypes, 0); + chartDataType = validChartDataTypes[0]; + } + } + + m_selectedChartTwoDataType[tabIndex] = chartDataType; + return chartDataType; +} + +/** + * Update the chart overlay sets. + * + * @param tabIndex + * Index of the tab. + */ +void +ModelChartTwo::updateChartOverlaySets(const int32_t tabIndex) +{ + CaretAssertArrayIndex(m_selectedChartTwoDataType, BrainConstants::MAXIMUM_NUMBER_OF_BROWSER_TABS, tabIndex); + switch (m_selectedChartTwoDataType[tabIndex]) { + case ChartTwoDataTypeEnum::CHART_DATA_TYPE_INVALID: + break; + case ChartTwoDataTypeEnum::CHART_DATA_TYPE_HISTOGRAM: + break; + case ChartTwoDataTypeEnum::CHART_DATA_TYPE_LINE_SERIES: + break; + case ChartTwoDataTypeEnum::CHART_DATA_TYPE_MATRIX: + break; + } +} + +/** + * Get the chart overlay set for this model. + * + * @param tabIndex + * Index for the chart overlay set. + * @return + * Chart overlay set or NULL if not valid for this model. + */ +ChartTwoOverlaySet* +ModelChartTwo::getChartTwoOverlaySet(const int tabIndex) +{ + ChartTwoOverlaySet* chartOverlaySet = NULL; + + switch (getSelectedChartTwoDataType(tabIndex)) { + case ChartTwoDataTypeEnum::CHART_DATA_TYPE_INVALID: + break; + case ChartTwoDataTypeEnum::CHART_DATA_TYPE_HISTOGRAM: + chartOverlaySet = m_histogramChartOverlaySetArray->getChartTwoOverlaySet(tabIndex); + break; + case ChartTwoDataTypeEnum::CHART_DATA_TYPE_LINE_SERIES: + chartOverlaySet = m_lineSeriesChartOverlaySetArray->getChartTwoOverlaySet(tabIndex); + break; + case ChartTwoDataTypeEnum::CHART_DATA_TYPE_MATRIX: + chartOverlaySet = m_matrixChartOverlaySetArray->getChartTwoOverlaySet(tabIndex); + break; + } + + return chartOverlaySet; +} + +/** + * Get the chart overlay set for this model. + * + * @param tabIndex + * Index for the chart overlay set. + * @return + * Chart overlay set or NULL if not valid for this model. + */ +const ChartTwoOverlaySet* +ModelChartTwo::getChartTwoOverlaySet(const int tabIndex) const +{ + ModelChartTwo* nonConstModelChart = const_cast(this); + CaretAssert(nonConstModelChart); + const ChartTwoOverlaySet* chartOverlaySet = nonConstModelChart->getChartTwoOverlaySet(tabIndex); + + return chartOverlaySet; +} + + diff --git a/src/Brain/ModelChartTwo.h b/src/Brain/ModelChartTwo.h new file mode 100644 index 0000000000000000000000000000000000000000..63f5824a32c1d1ed7d0ce04db82f57ae5e076cda --- /dev/null +++ b/src/Brain/ModelChartTwo.h @@ -0,0 +1,152 @@ +#ifndef __MODEL_CHART_TWO_H__ +#define __MODEL_CHART_TWO_H__ + +/*LICENSE_START*/ +/* + * Copyright (C) 2014 Washington University School of Medicine + * + * 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 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ +/*LICENSE_END*/ + +#include + +#include +#include + +#include +#include +#include + +#include "CaretDataFileSelectionModel.h" +#include "ChartTwoDataTypeEnum.h" +#include "EventListenerInterface.h" +#include "MapYokingGroupEnum.h" +#include "Model.h" +#include "StructureEnum.h" + +namespace caret { + + class CaretDataFileSelectionModel; + class ChartTwoOverlaySetArray; + class CiftiConnectivityMatrixParcelFile; + class CiftiMappableDataFile; + class ModelChart; + class OverlaySetArray; + class SurfaceFile; + + /// Controls the display of a chart. + class ModelChartTwo : public Model, public EventListenerInterface { + + public: + ModelChartTwo(Brain* brain); + + virtual ~ModelChartTwo(); + + virtual void initializeOverlays() override; + + virtual AString getNameForGUI(const bool includeStructureFlag) const override; + + virtual AString getNameForBrowserTab() const override; + + void loadChartDataForSurfaceNode(const StructureEnum::Enum structure, + const int32_t surfaceNumberOfNodes, + const int32_t nodeIndex); + + void loadAverageChartDataForSurfaceNodes(const StructureEnum::Enum structure, + const int32_t surfaceNumberOfNodes, + const std::vector& nodeIndices); + + void loadChartDataForVoxelAtCoordinate(const float xyz[3]); + + void loadChartDataForCiftiMappableFileRow(CiftiMappableDataFile* ciftiMapFile, + const int32_t rowIndex); + + void loadChartDataForYokedScalarDataSeriesFiles(const MapYokingGroupEnum::Enum mapYokingGroup, + const int32_t mapIndex); + + void selectRowColumnInYokedScalarDataSeriesFileOverlay(const MapYokingGroupEnum::Enum mapYokingGroup, + const int32_t mapIndex); + + virtual OverlaySet* getOverlaySet(const int tabIndex) override; + + virtual const OverlaySet* getOverlaySet(const int tabIndex) const override; + + virtual ChartTwoOverlaySet* getChartTwoOverlaySet(const int tabIndex) override; + + virtual const ChartTwoOverlaySet* getChartTwoOverlaySet(const int tabIndex) const override; + + virtual void receiveEvent(Event* event) override; + + void getValidChartTwoDataTypes(std::vector& validChartDataTypesOut) const; + + ChartTwoDataTypeEnum::Enum getSelectedChartTwoDataType(const int32_t tabIndex) const; + + void setSelectedChartTwoDataType(const int32_t tabIndex, + const ChartTwoDataTypeEnum::Enum dataType); + + virtual void copyTabContent(const int32_t sourceTabIndex, + const int32_t destinationTabIndex) override; + + void reset(); + + void restoreSceneFromChartOneModel(ModelChart* modelChartOne); + + void copyChartTwoCartesianAxes(const int32_t sourceTabIndex, + const int32_t destinationTabIndex); + + protected: + virtual void saveModelSpecificInformationToScene(const SceneAttributes* sceneAttributes, + SceneClass* sceneClass) override; + + virtual void restoreModelSpecificInformationFromScene(const SceneAttributes* sceneAttributes, + const SceneClass* sceneClass) override; + + private: + ModelChartTwo(const ModelChartTwo&); + + ModelChartTwo& operator=(const ModelChartTwo&); + + void initializeCharts(); + + void removeAllCharts(); + + void updateChartOverlaySets(const int32_t tabIndex); + + void restoreMatrixChartFromChartOneModel(ModelChart* modelChartOne, + const int32_t tabIndex); + + void restoreLineSeriesChartFromChartOneModel(ModelChart* modelChartOne); + + /** Overlays sets for this model and for each tab */ + OverlaySetArray* m_overlaySetArray; + + mutable ChartTwoDataTypeEnum::Enum m_selectedChartTwoDataType[BrainConstants::MAXIMUM_NUMBER_OF_BROWSER_TABS]; + + /** Chart Overlay sets for XX data type */ + std::unique_ptr m_histogramChartOverlaySetArray; + + /** Chart Overlay sets for XX data type */ + std::unique_ptr m_lineSeriesChartOverlaySetArray; + + /** Chart Overlay sets for XX data type */ + std::unique_ptr m_matrixChartOverlaySetArray; + + std::unique_ptr m_sceneAssistant; + }; + +} // namespace + +#endif // __MODEL_CHART_TWO_H__ diff --git a/src/Brain/ModelSurfaceMontage.cxx b/src/Brain/ModelSurfaceMontage.cxx index d72122dc1f6aed9fd0206fd0c7f6f262be04b782..56a127e3fda7db42064c15287d9361fd335e3acd 100644 --- a/src/Brain/ModelSurfaceMontage.cxx +++ b/src/Brain/ModelSurfaceMontage.cxx @@ -635,6 +635,7 @@ ModelSurfaceMontage::restoreModelSpecificInformationFromScene(const SceneAttribu iter++) { const int32_t key = iter->first; const SceneClass* cerebellarSceneClass = dynamic_cast(iter->second); + cerebellarSceneClass->setRestored(true); // prevents "failed to restore" CaretAssertArrayIndex(m_cerebellarConfiguration, BrainConstants::MAXIMUM_NUMBER_OF_BROWSER_TABS, @@ -656,7 +657,7 @@ ModelSurfaceMontage::restoreModelSpecificInformationFromScene(const SceneAttribu iter++) { const int32_t key = iter->first; const SceneClass* cerebralSceneClass = dynamic_cast(iter->second); - + cerebralSceneClass->setRestored(true);// prevents "failed to restore" CaretAssertArrayIndex(m_cerebralConfiguration, BrainConstants::MAXIMUM_NUMBER_OF_BROWSER_TABS, key); @@ -677,6 +678,7 @@ ModelSurfaceMontage::restoreModelSpecificInformationFromScene(const SceneAttribu iter++) { const int32_t key = iter->first; const SceneClass* flatClass = dynamic_cast(iter->second); + flatClass->setRestored(true); // prevents "failed to restore" CaretAssertArrayIndex(m_flatMapsConfiguration, BrainConstants::MAXIMUM_NUMBER_OF_BROWSER_TABS, diff --git a/src/Brain/ModelTypeEnum.cxx b/src/Brain/ModelTypeEnum.cxx index b73b4b8a35477ce97ce2cb16cc62e4dbe7b25525..be37b3ef85d2623bc0947feeac28d88e4ceed628 100644 --- a/src/Brain/ModelTypeEnum.cxx +++ b/src/Brain/ModelTypeEnum.cxx @@ -70,36 +70,40 @@ ModelTypeEnum::initialize() } initializedFlag = true; - enumData.push_back(ModelTypeEnum(MODEL_TYPE_INVALID, - 0, - "MODEL_TYPE_INVALID", - "Invalid")); + enumData.push_back(ModelTypeEnum(MODEL_TYPE_INVALID, + 0, + "MODEL_TYPE_INVALID", + "Invalid")); - enumData.push_back(ModelTypeEnum(MODEL_TYPE_CHART, - 1, - "MODEL_TYPE_CHART", - "Chart")); + enumData.push_back(ModelTypeEnum(MODEL_TYPE_CHART, + 1, + "MODEL_TYPE_CHART", + "Chart")); - enumData.push_back(ModelTypeEnum(MODEL_TYPE_SURFACE, + enumData.push_back(ModelTypeEnum(MODEL_TYPE_CHART_TWO, 2, + "MODEL_TYPE_CHART_TWO", + "Chart")); + + enumData.push_back(ModelTypeEnum(MODEL_TYPE_SURFACE, + 3, "MODEL_TYPE_SURFACE", "Surface")); enumData.push_back(ModelTypeEnum(MODEL_TYPE_SURFACE_MONTAGE, - 3, - "MODEL_TYPE_SURFACE_MONTAGE", - "Surface Montage")); - - enumData.push_back(ModelTypeEnum(MODEL_TYPE_VOLUME_SLICES, - 4, - "MODEL_TYPE_VOLUME_SLICES", - "Volume")); + 4, + "MODEL_TYPE_SURFACE_MONTAGE", + "Surface Montage")); - enumData.push_back(ModelTypeEnum(MODEL_TYPE_WHOLE_BRAIN, - 5, - "MODEL_TYPE_WHOLE_BRAIN", - "Whole Brain")); + enumData.push_back(ModelTypeEnum(MODEL_TYPE_VOLUME_SLICES, + 5, + "MODEL_TYPE_VOLUME_SLICES", + "Volume")); + enumData.push_back(ModelTypeEnum(MODEL_TYPE_WHOLE_BRAIN, + 6, + "MODEL_TYPE_WHOLE_BRAIN", + "Whole Brain")); } /** diff --git a/src/Brain/ModelTypeEnum.h b/src/Brain/ModelTypeEnum.h index 287fa04c619e521aee4d83fed6f5291e70ac13b3..3d52e072ca56edb9bbffb9daa7862ff79f419803 100644 --- a/src/Brain/ModelTypeEnum.h +++ b/src/Brain/ModelTypeEnum.h @@ -40,6 +40,8 @@ public: MODEL_TYPE_INVALID, /** Chart */ MODEL_TYPE_CHART, + /** Chart Two */ + MODEL_TYPE_CHART_TWO, /** Surface */ MODEL_TYPE_SURFACE, /** Surface Montage */ diff --git a/src/Brain/Overlay.cxx b/src/Brain/Overlay.cxx index 0d1bdab9b9441112e6036290fb315469c006e169..2144afe1098991b12e16acb1aeb298b7a9076c17 100644 --- a/src/Brain/Overlay.cxx +++ b/src/Brain/Overlay.cxx @@ -73,7 +73,7 @@ m_includeVolumeFiles(includeVolumeFiles) m_wholeBrainVoxelDrawingMode = WholeBrainVoxelDrawingMode::DRAW_VOXELS_ON_TWO_D_SLICES; m_colorBar = new AnnotationColorBar(AnnotationAttributesDefaultTypeEnum::NORMAL); - m_colorBar->setCoordinateSpace(AnnotationCoordinateSpaceEnum::WINDOW); + m_colorBar->setCoordinateSpace(AnnotationCoordinateSpaceEnum::TAB); m_sceneAssistant = new SceneClassAssistant(); m_sceneAssistant->add("m_opacity", &m_opacity); diff --git a/src/Brain/SelectionItem.cxx b/src/Brain/SelectionItem.cxx index babbe437170d2de7c4dce71cfa727470eb1b3667..7c29cbfc7499f4138785919046a080593d06cdf5 100644 --- a/src/Brain/SelectionItem.cxx +++ b/src/Brain/SelectionItem.cxx @@ -285,8 +285,19 @@ SelectionItem::isOtherScreenDepthCloserToViewer(const double otherScreenDepth) c return true; } - if (otherScreenDepth < m_screenDepth) { - return true; + /* + * Multiple annotations that are on top of each other may be drawn at the same + * depth so using "<=" should select the one that appears to be "on top" to the user. + */ + if (m_itemDataType == SelectionItemDataTypeEnum::ANNOTATION) { + if (otherScreenDepth <= m_screenDepth) { + return true; + } + } + else { + if (otherScreenDepth < m_screenDepth) { + return true; + } } return false; diff --git a/src/Brain/SelectionItemChartTwoHistogram.cxx b/src/Brain/SelectionItemChartTwoHistogram.cxx new file mode 100644 index 0000000000000000000000000000000000000000..d54b50778ccba647a5a52f2a5ab19dcc27208b34 --- /dev/null +++ b/src/Brain/SelectionItemChartTwoHistogram.cxx @@ -0,0 +1,197 @@ + +/*LICENSE_START*/ +/* + * Copyright (C) 2017 Washington University School of Medicine + * + * 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 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ +/*LICENSE_END*/ + +#define __SELECTION_ITEM_CHART_TWO_HISTOGRAM_DECLARE__ +#include "SelectionItemChartTwoHistogram.h" +#undef __SELECTION_ITEM_CHART_TWO_HISTOGRAM_DECLARE__ + +#include "CaretAssert.h" +using namespace caret; + + + +/** + * \class caret::SelectionItemChartTwoHistogram + * \brief Selection of charting two histogram + * \ingroup Brain + */ + +/** + * Constructor. + */ +SelectionItemChartTwoHistogram::SelectionItemChartTwoHistogram() +: SelectionItem(SelectionItemDataTypeEnum::CHART_TWO_HISTOGRAM) +{ + resetPrivate(); +} + +/** + * Destructor. + */ +SelectionItemChartTwoHistogram::~SelectionItemChartTwoHistogram() +{ +} + +/** + * Copy constructor. + * @param obj + * Object that is copied. + */ +SelectionItemChartTwoHistogram::SelectionItemChartTwoHistogram(const SelectionItemChartTwoHistogram& obj) +: SelectionItem(obj) +{ + this->copyHelperSelectionItemChartTwoHistogram(obj); +} + +/** + * Assignment operator. + * @param obj + * Data copied from obj to this. + * @return + * Reference to this object. + */ +SelectionItemChartTwoHistogram& +SelectionItemChartTwoHistogram::operator=(const SelectionItemChartTwoHistogram& obj) +{ + if (this != &obj) { + SelectionItem::operator=(obj); + this->copyHelperSelectionItemChartTwoHistogram(obj); + } + return *this; +} + +/** + * Helps with copying an object of this type. + * @param obj + * Object that is copied. + */ +void +SelectionItemChartTwoHistogram::copyHelperSelectionItemChartTwoHistogram(const SelectionItemChartTwoHistogram& obj) +{ + m_fileHistogramChart = obj.m_fileHistogramChart; + m_mapIndex = obj.m_mapIndex; + m_bucketIndex = obj.m_bucketIndex; + m_allMapsSelectedFlag = obj.m_allMapsSelectedFlag; +} + + +ChartableTwoFileHistogramChart* +SelectionItemChartTwoHistogram::getFileHistogramChart() const +{ + return m_fileHistogramChart; +} + +/** + * @return Index of map index selected. Also + * should test isAllMapsSelected() + */ +int32_t +SelectionItemChartTwoHistogram::getMapIndex() const +{ + return m_mapIndex; +} + +/** + * @return Index of histogram bucket selected. + */ +int32_t +SelectionItemChartTwoHistogram::getBucketIndex() const +{ + return m_bucketIndex; +} + +/** + * @return Is all maps selected? + */ +bool +SelectionItemChartTwoHistogram::isAllMapsSelected() const +{ + return m_allMapsSelectedFlag; +} + + +/* + * Set histogram identification. + * + * @param fileHistogramChart + * The histogram chart. + * @param mapIndex + * Index of the map whose histogram is displayed (negative indicates all maps). + * @param bucketIndex + * Index of the histogram bucket. + * @param allMapsSelected + * True if all maps selected, else false. + */ +void +SelectionItemChartTwoHistogram::setHistogramChart(ChartableTwoFileHistogramChart* fileHistogramChart, + const int32_t mapIndex, + const int32_t bucketIndex, + const bool allMapsSelected) +{ + m_fileHistogramChart = fileHistogramChart; + m_mapIndex = mapIndex; + m_bucketIndex = bucketIndex; + m_allMapsSelectedFlag = allMapsSelected; +} + +/** + * @return True if the selected chart is valid, else false. + */ +bool +SelectionItemChartTwoHistogram::isValid() const +{ + /* + * Map index is negative if all maps selected. + */ + if ((m_fileHistogramChart != NULL) + && (m_bucketIndex >= 0) + && ((m_mapIndex >= 0) + || m_allMapsSelectedFlag)) { + return true; + } + + return false; +} + +/** + * Reset the selections PRIVATE. + * Note that reset() is virtual and cannot + * be called from constructor. + */ +void +SelectionItemChartTwoHistogram::resetPrivate() +{ + m_fileHistogramChart = NULL; + m_mapIndex = -1; + m_bucketIndex = -1; + m_allMapsSelectedFlag = false; +} + +/** + * Reset the selections. + */ +void +SelectionItemChartTwoHistogram::reset() +{ + resetPrivate(); +} + + diff --git a/src/Brain/SelectionItemChartTwoHistogram.h b/src/Brain/SelectionItemChartTwoHistogram.h new file mode 100644 index 0000000000000000000000000000000000000000..2f424a9be3fd60f098fbf23d93fe850aa1b80a61 --- /dev/null +++ b/src/Brain/SelectionItemChartTwoHistogram.h @@ -0,0 +1,83 @@ +#ifndef __SELECTION_ITEM_CHART_TWO_HISTOGRAM_H__ +#define __SELECTION_ITEM_CHART_TWO_HISTOGRAM_H__ + +/*LICENSE_START*/ +/* + * Copyright (C) 2017 Washington University School of Medicine + * + * 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 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ +/*LICENSE_END*/ + + +#include "SelectionItem.h" + + + +namespace caret { + + class ChartableTwoFileHistogramChart; + + class SelectionItemChartTwoHistogram : public SelectionItem { + + public: + SelectionItemChartTwoHistogram(); + + virtual ~SelectionItemChartTwoHistogram(); + + SelectionItemChartTwoHistogram(const SelectionItemChartTwoHistogram& obj); + + SelectionItemChartTwoHistogram& operator=(const SelectionItemChartTwoHistogram& obj); + + virtual bool isValid() const override; + + virtual void reset() override; + + ChartableTwoFileHistogramChart* getFileHistogramChart() const; + + int32_t getBucketIndex() const; + + int32_t getMapIndex() const; + + bool isAllMapsSelected() const; + + void setHistogramChart(ChartableTwoFileHistogramChart* fileHistogramChart, + const int32_t mapIndex, + const int32_t bucketIndex, + const bool allMapsSelected); + + // ADD_NEW_METHODS_HERE + + private: + void copyHelperSelectionItemChartTwoHistogram(const SelectionItemChartTwoHistogram& obj); + + void resetPrivate(); + + ChartableTwoFileHistogramChart* m_fileHistogramChart; + + int32_t m_mapIndex; + int32_t m_bucketIndex; + bool m_allMapsSelectedFlag; + + // ADD_NEW_MEMBERS_HERE + + }; + +#ifdef __SELECTION_ITEM_CHART_TWO_HISTOGRAM_DECLARE__ + // +#endif // __SELECTION_ITEM_CHART_TWO_HISTOGRAM_DECLARE__ + +} // namespace +#endif //__SELECTION_ITEM_CHART_TWO_HISTOGRAM_H__ diff --git a/src/Brain/SelectionItemChartTwoLabel.cxx b/src/Brain/SelectionItemChartTwoLabel.cxx new file mode 100644 index 0000000000000000000000000000000000000000..c823b619097093d3877787064d7897a1c63bd1b8 --- /dev/null +++ b/src/Brain/SelectionItemChartTwoLabel.cxx @@ -0,0 +1,165 @@ + +/*LICENSE_START*/ +/* + * Copyright (C) 2017 Washington University School of Medicine + * + * 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 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ +/*LICENSE_END*/ + +#define __SELECTION_ITEM_CHART_TWO_LABEL_DECLARE__ +#include "SelectionItemChartTwoLabel.h" +#undef __SELECTION_ITEM_CHART_TWO_LABEL_DECLARE__ + +#include "CaretAssert.h" +using namespace caret; + + + +/** + * \class caret::SelectionItemChartTwoLabel + * \brief Selection of chart version two label + * \ingroup Brain + */ + +/** + * Constructor. + */ +SelectionItemChartTwoLabel::SelectionItemChartTwoLabel() +: SelectionItem(SelectionItemDataTypeEnum::CHART_TWO_LABEL) +{ + m_chartTwoCartesianAxis = NULL; + m_chartTwoOverlaySet = NULL; +} + +/** + * Destructor. + */ +SelectionItemChartTwoLabel::~SelectionItemChartTwoLabel() +{ +} + +/** + * Copy constructor. + * @param obj + * Object that is copied. + */ +SelectionItemChartTwoLabel::SelectionItemChartTwoLabel(const SelectionItemChartTwoLabel& obj) +: SelectionItem(obj) +{ + this->copyHelperSelectionItemChartTwoLabel(obj); +} + +/** + * Assignment operator. + * @param obj + * Data copied from obj to this. + * @return + * Reference to this object. + */ +SelectionItemChartTwoLabel& +SelectionItemChartTwoLabel::operator=(const SelectionItemChartTwoLabel& obj) +{ + if (this != &obj) { + SelectionItem::operator=(obj); + this->copyHelperSelectionItemChartTwoLabel(obj); + } + return *this; +} + +/** + * @return The cartesian axis. + */ +const ChartTwoCartesianAxis* +SelectionItemChartTwoLabel::getChartTwoCartesianAxis() const +{ + return m_chartTwoCartesianAxis; +} + +/** + * @return The cartesian axis. + */ +ChartTwoCartesianAxis* +SelectionItemChartTwoLabel::getChartTwoCartesianAxis() +{ + return m_chartTwoCartesianAxis; +} + +/** + * @return The chart overlay set. + */ +ChartTwoOverlaySet* +SelectionItemChartTwoLabel::getChartOverlaySet() +{ + return m_chartTwoOverlaySet; +} + +/** + * @return The chart overlay set. + */ +const ChartTwoOverlaySet* +SelectionItemChartTwoLabel::getChartOverlaySet() const +{ + return m_chartTwoOverlaySet; +} + +/** + * Set selection. + * + * @param chartTwoCartesianAxis + * The cartesian axis. + * @param chartOverlaySet + * The chart overlay set + */ +void +SelectionItemChartTwoLabel::setChartTwoCartesianAxis(ChartTwoCartesianAxis* chartTwoCartesianAxis, + ChartTwoOverlaySet* chartOverlaySet) +{ + m_chartTwoCartesianAxis = chartTwoCartesianAxis; + m_chartTwoOverlaySet = chartOverlaySet; +} + +/** + * Helps with copying an object of this type. + * @param obj + * Object that is copied. + */ +void +SelectionItemChartTwoLabel::copyHelperSelectionItemChartTwoLabel(const SelectionItemChartTwoLabel& obj) +{ + m_chartTwoCartesianAxis = obj.m_chartTwoCartesianAxis; + m_chartTwoOverlaySet = obj.m_chartTwoOverlaySet; +} + +/** + * @return True if the selected chart label is valid, else false. + */ +bool +SelectionItemChartTwoLabel::isValid() const +{ + return ((m_chartTwoCartesianAxis != NULL) + && (m_chartTwoOverlaySet != NULL)); +} + +/** + * Reset the selections. + */ +void +SelectionItemChartTwoLabel::reset() +{ + m_chartTwoCartesianAxis = NULL; + m_chartTwoOverlaySet = NULL; +} + diff --git a/src/Brain/SelectionItemChartTwoLabel.h b/src/Brain/SelectionItemChartTwoLabel.h new file mode 100644 index 0000000000000000000000000000000000000000..075f03e02a80a44f5a0edfd87c1ea8b5c3920a5a --- /dev/null +++ b/src/Brain/SelectionItemChartTwoLabel.h @@ -0,0 +1,78 @@ +#ifndef __SELECTION_ITEM_CHART_TWO_LABEL_H__ +#define __SELECTION_ITEM_CHART_TWO_LABEL_H__ + +/*LICENSE_START*/ +/* + * Copyright (C) 2017 Washington University School of Medicine + * + * 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 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ +/*LICENSE_END*/ + + +#include "SelectionItem.h" + + + +namespace caret { + + class ChartTwoCartesianAxis; + class ChartTwoOverlaySet; + + class SelectionItemChartTwoLabel : public SelectionItem { + + public: + SelectionItemChartTwoLabel(); + + virtual ~SelectionItemChartTwoLabel(); + + SelectionItemChartTwoLabel(const SelectionItemChartTwoLabel& obj); + + SelectionItemChartTwoLabel& operator=(const SelectionItemChartTwoLabel& obj); + + virtual bool isValid() const override; + + virtual void reset() override; + + const ChartTwoCartesianAxis* getChartTwoCartesianAxis() const; + + ChartTwoCartesianAxis* getChartTwoCartesianAxis(); + + const ChartTwoOverlaySet* getChartOverlaySet() const; + + ChartTwoOverlaySet* getChartOverlaySet(); + + void setChartTwoCartesianAxis(ChartTwoCartesianAxis* ChartTwoCartesianAxis, + ChartTwoOverlaySet* chartOverlaySet); + + // ADD_NEW_METHODS_HERE + + private: + void copyHelperSelectionItemChartTwoLabel(const SelectionItemChartTwoLabel& obj); + + ChartTwoCartesianAxis* m_chartTwoCartesianAxis; + + ChartTwoOverlaySet* m_chartTwoOverlaySet; + + // ADD_NEW_MEMBERS_HERE + + }; + +#ifdef __SELECTION_ITEM_CHART_TWO_LABEL_DECLARE__ + // +#endif // __SELECTION_ITEM_CHART_TWO_LABEL_DECLARE__ + +} // namespace +#endif //__SELECTION_ITEM_CHART_TWO_LABEL_H__ diff --git a/src/Brain/SelectionItemChartTwoLineSeries.cxx b/src/Brain/SelectionItemChartTwoLineSeries.cxx new file mode 100644 index 0000000000000000000000000000000000000000..45b0f0d4d17c7848d42e0ef677c2c3a509516269 --- /dev/null +++ b/src/Brain/SelectionItemChartTwoLineSeries.cxx @@ -0,0 +1,164 @@ + +/*LICENSE_START*/ +/* + * Copyright (C) 2017 Washington University School of Medicine + * + * 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 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ +/*LICENSE_END*/ + +#define __SELECTION_ITEM_CHART_TWO_LINE_SERIES_DECLARE__ +#include "SelectionItemChartTwoLineSeries.h" +#undef __SELECTION_ITEM_CHART_TWO_LINE_SERIES_DECLARE__ + +#include "CaretAssert.h" +using namespace caret; + + + +/** + * \class caret::SelectionItemChartTwoLineSeries + * \brief Selection of chart version two line series + * \ingroup Brain + */ + +/** + * Constructor. + */ +SelectionItemChartTwoLineSeries::SelectionItemChartTwoLineSeries() +: SelectionItem(SelectionItemDataTypeEnum::CHART_TWO_LINE_SERIES) +{ + m_fileLineSeriesChart = NULL; + m_chartTwoCartesianData = NULL; + m_lineSegmentIndex = -1; +} + +/** + * Destructor. + */ +SelectionItemChartTwoLineSeries::~SelectionItemChartTwoLineSeries() +{ +} + +/** + * Copy constructor. + * @param obj + * Object that is copied. + */ +SelectionItemChartTwoLineSeries::SelectionItemChartTwoLineSeries(const SelectionItemChartTwoLineSeries& obj) +: SelectionItem(obj) +{ + this->copyHelperSelectionItemChartTwoLineSeries(obj); +} + +/** + * Assignment operator. + * @param obj + * Data copied from obj to this. + * @return + * Reference to this object. + */ +SelectionItemChartTwoLineSeries& +SelectionItemChartTwoLineSeries::operator=(const SelectionItemChartTwoLineSeries& obj) +{ + if (this != &obj) { + SelectionItem::operator=(obj); + this->copyHelperSelectionItemChartTwoLineSeries(obj); + } + return *this; +} + +/** + * @return The File line series chart. + */ +const ChartableTwoFileLineSeriesChart* +SelectionItemChartTwoLineSeries::getFileLineSeriesChart() const +{ + return m_fileLineSeriesChart; +} + +/** + * @return The cartesian data. + */ +const ChartTwoDataCartesian* +SelectionItemChartTwoLineSeries::getChartTwoCartesianData() const +{ + return m_chartTwoCartesianData; +} + +/** + * @return The line segment index. + */ +int32_t +SelectionItemChartTwoLineSeries::getLineSegmentIndex() const +{ + return m_lineSegmentIndex; +} + +/** + * Set selection. + * + * @param fileLineSeriesChart + * The line series chart. + * @param chartTwoCartesianData + * The cartesian data. + * @param lineSegmentIndex + * Index of the line segment. + */ +void +SelectionItemChartTwoLineSeries::setLineSeriesChart(ChartableTwoFileLineSeriesChart* fileLineSeriesChart, + ChartTwoDataCartesian* chartTwoCartesianData, + const int32_t lineSegmentIndex) +{ + m_fileLineSeriesChart = fileLineSeriesChart; + m_chartTwoCartesianData = chartTwoCartesianData; + m_lineSegmentIndex = lineSegmentIndex; +} + +/** + * Helps with copying an object of this type. + * @param obj + * Object that is copied. + */ +void +SelectionItemChartTwoLineSeries::copyHelperSelectionItemChartTwoLineSeries(const SelectionItemChartTwoLineSeries& obj) +{ + m_fileLineSeriesChart = obj.m_fileLineSeriesChart; + m_chartTwoCartesianData = obj.m_chartTwoCartesianData; + m_lineSegmentIndex = obj.m_lineSegmentIndex; +} + +/** + * @return True if the selected chart is valid, else false. + */ +bool +SelectionItemChartTwoLineSeries::isValid() const +{ + return ((m_fileLineSeriesChart != NULL) + && (m_chartTwoCartesianData != NULL) + && (m_lineSegmentIndex >= 0)); +} + +/** + * Reset the selections. + */ +void +SelectionItemChartTwoLineSeries::reset() +{ + m_fileLineSeriesChart = NULL; + m_chartTwoCartesianData = NULL; + m_lineSegmentIndex = -1; +} + diff --git a/src/Brain/SelectionItemChartTwoLineSeries.h b/src/Brain/SelectionItemChartTwoLineSeries.h new file mode 100644 index 0000000000000000000000000000000000000000..d479217e642d145aa58997b98b602d5edcf2569f --- /dev/null +++ b/src/Brain/SelectionItemChartTwoLineSeries.h @@ -0,0 +1,79 @@ +#ifndef __SELECTION_ITEM_CHART_TWO_LINE_SERIES_H__ +#define __SELECTION_ITEM_CHART_TWO_LINE_SERIES_H__ + +/*LICENSE_START*/ +/* + * Copyright (C) 2017 Washington University School of Medicine + * + * 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 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ +/*LICENSE_END*/ + + +#include "SelectionItem.h" + + + +namespace caret { + + class ChartTwoDataCartesian; + class ChartableTwoFileLineSeriesChart; + + class SelectionItemChartTwoLineSeries : public SelectionItem { + + public: + SelectionItemChartTwoLineSeries(); + + virtual ~SelectionItemChartTwoLineSeries(); + + SelectionItemChartTwoLineSeries(const SelectionItemChartTwoLineSeries& obj); + + SelectionItemChartTwoLineSeries& operator=(const SelectionItemChartTwoLineSeries& obj); + + virtual bool isValid() const override; + + virtual void reset() override; + + const ChartableTwoFileLineSeriesChart* getFileLineSeriesChart() const; + + const ChartTwoDataCartesian* getChartTwoCartesianData() const; + + int32_t getLineSegmentIndex() const; + + void setLineSeriesChart(ChartableTwoFileLineSeriesChart* fileLineSeriesChart, + ChartTwoDataCartesian* chartTwoCartesianData, + const int32_t lineSegmentIndex); + + // ADD_NEW_METHODS_HERE + + private: + void copyHelperSelectionItemChartTwoLineSeries(const SelectionItemChartTwoLineSeries& obj); + + ChartableTwoFileLineSeriesChart* m_fileLineSeriesChart; + + ChartTwoDataCartesian* m_chartTwoCartesianData; + + int32_t m_lineSegmentIndex; + + // ADD_NEW_MEMBERS_HERE + + }; + +#ifdef __SELECTION_ITEM_CHART_TWO_LINE_SERIES_DECLARE__ + // +#endif // __SELECTION_ITEM_CHART_TWO_LINE_SERIES_DECLARE__ + +} // namespace +#endif //__SELECTION_ITEM_CHART_TWO_LINE_SERIES_H__ diff --git a/src/Brain/SelectionItemChartTwoMatrix.cxx b/src/Brain/SelectionItemChartTwoMatrix.cxx new file mode 100644 index 0000000000000000000000000000000000000000..b6c5602496924f9b409b3bddbfdc04ecedefe8e1 --- /dev/null +++ b/src/Brain/SelectionItemChartTwoMatrix.cxx @@ -0,0 +1,169 @@ + +/*LICENSE_START*/ +/* + * Copyright (C) 2017 Washington University School of Medicine + * + * 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 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ +/*LICENSE_END*/ + +#define __SELECTION_ITEM_CHART_TWO_MATRIX_DECLARE__ +#include "SelectionItemChartTwoMatrix.h" +#undef __SELECTION_ITEM_CHART_TWO_MATRIX_DECLARE__ + +#include "CaretAssert.h" +using namespace caret; + + + +/** + * \class caret::SelectionItemChartTwoMatrix + * \brief Selection of chart two matrix + * \ingroup Brain + */ + +/** + * Constructor. + */ +SelectionItemChartTwoMatrix::SelectionItemChartTwoMatrix() +: SelectionItem(SelectionItemDataTypeEnum::CHART_TWO_MATRIX) +{ + m_fileMatrixChart = NULL; + m_rowIndex = -1; + m_columnIndex = -1; +} + +/** + * Destructor. + */ +SelectionItemChartTwoMatrix::~SelectionItemChartTwoMatrix() +{ +} + +/** + * Copy constructor. + * @param obj + * Object that is copied. + */ +SelectionItemChartTwoMatrix::SelectionItemChartTwoMatrix(const SelectionItemChartTwoMatrix& obj) +: SelectionItem(obj) +{ + this->copyHelperSelectionItemChartTwoMatrix(obj); +} + +/** + * Assignment operator. + * @param obj + * Data copied from obj to this. + * @return + * Reference to this object. + */ +SelectionItemChartTwoMatrix& +SelectionItemChartTwoMatrix::operator=(const SelectionItemChartTwoMatrix& obj) +{ + if (this != &obj) { + SelectionItem::operator=(obj); + this->copyHelperSelectionItemChartTwoMatrix(obj); + } + return *this; +} + +/** + * Helps with copying an object of this type. + * @param obj + * Object that is copied. + */ +void +SelectionItemChartTwoMatrix::copyHelperSelectionItemChartTwoMatrix(const SelectionItemChartTwoMatrix& obj) +{ + m_fileMatrixChart = obj.m_fileMatrixChart; + m_rowIndex = obj.m_rowIndex; + m_columnIndex = obj.m_columnIndex; +} + +/** + * @return True if the selected chart is valid, else false. + */ +bool +SelectionItemChartTwoMatrix::isValid() const +{ + if ((m_fileMatrixChart != NULL) + && (m_rowIndex >= 0) + && (m_columnIndex >= 0)) { + return true; + } + + return false; +} + +/** + * Reset the selections. + */ +void +SelectionItemChartTwoMatrix::reset() +{ + m_fileMatrixChart = NULL; + m_rowIndex = -1; + m_columnIndex = -1; +} + +/** + * @return Matrix chart that was selected. + */ +ChartableTwoFileMatrixChart* +SelectionItemChartTwoMatrix::getFileMatrixChart() const +{ + return m_fileMatrixChart; +} + +/** + * @return Row index selected. + */ +int32_t +SelectionItemChartTwoMatrix::getRowIndex() const +{ + return m_rowIndex; +} + +/** + * @return column index selected. + */ +int32_t +SelectionItemChartTwoMatrix::getColumnIndex() const +{ + return m_columnIndex; +} + +/** + * Set the matrix chart. + * + * @param fileMatrixChart + * Matrix chart that was selected. + * @param rowIndex + * Index of row selected. + * @param columnIndex + * Index of column selected. + */ +void +SelectionItemChartTwoMatrix::setMatrixChart(ChartableTwoFileMatrixChart* fileMatrixChart, + const int32_t rowIndex, + const int32_t columnIndex) +{ + m_fileMatrixChart = fileMatrixChart; + m_rowIndex = rowIndex; + m_columnIndex = columnIndex; +} + + diff --git a/src/Brain/SelectionItemChartTwoMatrix.h b/src/Brain/SelectionItemChartTwoMatrix.h new file mode 100644 index 0000000000000000000000000000000000000000..98081dbb8942bc669b9a9a407f02041e13ba9d0d --- /dev/null +++ b/src/Brain/SelectionItemChartTwoMatrix.h @@ -0,0 +1,78 @@ +#ifndef __SELECTION_ITEM_CHART_TWO_MATRIX_H__ +#define __SELECTION_ITEM_CHART_TWO_MATRIX_H__ + +/*LICENSE_START*/ +/* + * Copyright (C) 2017 Washington University School of Medicine + * + * 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 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ +/*LICENSE_END*/ + + +#include "SelectionItem.h" + + + +namespace caret { + + class ChartableTwoFileMatrixChart; + + class SelectionItemChartTwoMatrix : public SelectionItem { + + public: + SelectionItemChartTwoMatrix(); + + virtual ~SelectionItemChartTwoMatrix(); + + SelectionItemChartTwoMatrix(const SelectionItemChartTwoMatrix& obj); + + SelectionItemChartTwoMatrix& operator=(const SelectionItemChartTwoMatrix& obj); + + virtual bool isValid() const override; + + virtual void reset() override; + + ChartableTwoFileMatrixChart* getFileMatrixChart() const; + + int32_t getRowIndex() const; + + int32_t getColumnIndex() const; + + void setMatrixChart(ChartableTwoFileMatrixChart* fileMatrixChart, + const int32_t rowIndex, + const int32_t columnIndex); + + // ADD_NEW_METHODS_HERE + + private: + void copyHelperSelectionItemChartTwoMatrix(const SelectionItemChartTwoMatrix& obj); + + ChartableTwoFileMatrixChart* m_fileMatrixChart; + + int32_t m_rowIndex; + + int32_t m_columnIndex; + + // ADD_NEW_MEMBERS_HERE + + }; + +#ifdef __SELECTION_ITEM_CHART_TWO_MATRIX_DECLARE__ + // +#endif // __SELECTION_ITEM_CHART_TWO_MATRIX_DECLARE__ + +} // namespace +#endif //__SELECTION_ITEM_CHART_TWO_MATRIX_H__ diff --git a/src/Brain/SelectionItemDataTypeEnum.cxx b/src/Brain/SelectionItemDataTypeEnum.cxx index 7e4816ff28901def4cd0756f28d924955fa47bff..7d1b29855af94f0d8373b053ed20e617ca90bf13 100644 --- a/src/Brain/SelectionItemDataTypeEnum.cxx +++ b/src/Brain/SelectionItemDataTypeEnum.cxx @@ -99,6 +99,22 @@ SelectionItemDataTypeEnum::initialize() "CHART_TIME_SERIES", "Time-Series Chart")); + enumData.push_back(SelectionItemDataTypeEnum(CHART_TWO_HISTOGRAM, + "CHART_TWO_HISTOGRAM", + "Histogram Chart Two")); + + enumData.push_back(SelectionItemDataTypeEnum(CHART_TWO_LINE_SERIES, + "CHART_TWO_LINE_SERIES", + "Line-Series Chart Two")); + + enumData.push_back(SelectionItemDataTypeEnum(CHART_TWO_LABEL, + "CHART_TWO_LABEL", + "Label Chart Two")); + + enumData.push_back(SelectionItemDataTypeEnum(CHART_TWO_MATRIX, + "CHART_TWO_MATRIX", + "Matrix Chart Two")); + enumData.push_back(SelectionItemDataTypeEnum(CIFTI_CONNECTIVITY_MATRIX_ROW_COLUMN, "CIFTI_CONNECTIVITY_MATRIX_ROW_COLUMN", "CIFTI Connectivity Row or Column")); diff --git a/src/Brain/SelectionItemDataTypeEnum.h b/src/Brain/SelectionItemDataTypeEnum.h index 22f0b72761f957bbf23215c6be89362199a902c6..5ce2bf9d1a4ba26223b8cda9eb802662bb6021be 100644 --- a/src/Brain/SelectionItemDataTypeEnum.h +++ b/src/Brain/SelectionItemDataTypeEnum.h @@ -57,6 +57,14 @@ public: CHART_MATRIX, /** Time-Series Chart */ CHART_TIME_SERIES, + /** Version Two Chart Histogram */ + CHART_TWO_HISTOGRAM, + /** Version Two Chart Label */ + CHART_TWO_LABEL, + /** Version Two Chart Line-Series */ + CHART_TWO_LINE_SERIES, + /** Version Two Chart Matrix */ + CHART_TWO_MATRIX, /** CIFTI Connectivity Matrix Row or Column */ CIFTI_CONNECTIVITY_MATRIX_ROW_COLUMN, /** Focus on Surface */ diff --git a/src/Brain/SelectionManager.cxx b/src/Brain/SelectionManager.cxx index 7946f522286a459be43bcaec692d161adbc6710d..91b24486aba5c474dece21b140b795677d01f020 100644 --- a/src/Brain/SelectionManager.cxx +++ b/src/Brain/SelectionManager.cxx @@ -37,6 +37,10 @@ #include "SelectionItemChartFrequencySeries.h" #include "SelectionItemChartMatrix.h" #include "SelectionItemChartTimeSeries.h" +#include "SelectionItemChartTwoHistogram.h" +#include "SelectionItemChartTwoLabel.h" +#include "SelectionItemChartTwoLineSeries.h" +#include "SelectionItemChartTwoMatrix.h" #include "SelectionItemCiftiConnectivityMatrixRowColumn.h" #include "SelectionItemFocusSurface.h" #include "SelectionItemFocusVolume.h" @@ -72,6 +76,11 @@ SelectionManager::SelectionManager() m_chartDataSeriesIdentification = new SelectionItemChartDataSeries(); m_chartDataFrequencyIdentification = new SelectionItemChartFrequencySeries(); m_chartMatrixIdentification = new SelectionItemChartMatrix(); + m_chartTwoHistogramIdentification = std::unique_ptr(new SelectionItemChartTwoHistogram()); + m_chartTwoLabelIdentification = std::unique_ptr(new SelectionItemChartTwoLabel()); + m_chartTwoLineSeriesIdentification = std::unique_ptr(new SelectionItemChartTwoLineSeries()); + m_chartTwoMatrixIdentification = std::unique_ptr(new SelectionItemChartTwoMatrix()); + m_ciftiConnectivityMatrixRowColumnIdentfication = new SelectionItemCiftiConnectivityMatrixRowColumn(); m_chartTimeSeriesIdentification = new SelectionItemChartTimeSeries(); m_surfaceFocusIdentification = new SelectionItemFocusSurface(); @@ -91,6 +100,10 @@ SelectionManager::SelectionManager() m_allSelectionItems.push_back(m_chartDataFrequencyIdentification); m_allSelectionItems.push_back(m_chartMatrixIdentification); m_allSelectionItems.push_back(m_chartTimeSeriesIdentification); + m_allSelectionItems.push_back(m_chartTwoHistogramIdentification.get()); + m_allSelectionItems.push_back(m_chartTwoLabelIdentification.get()); + m_allSelectionItems.push_back(m_chartTwoLineSeriesIdentification.get()); + m_allSelectionItems.push_back(m_chartTwoMatrixIdentification.get()); m_allSelectionItems.push_back(m_ciftiConnectivityMatrixRowColumnIdentfication); m_allSelectionItems.push_back(m_surfaceFocusIdentification); m_allSelectionItems.push_back(m_surfaceNodeIdentification); @@ -704,7 +717,6 @@ SelectionManager::getChartFrequencySeriesIdentification() const return m_chartDataFrequencyIdentification; } - /** * @return Identification for matrix chart. */ @@ -723,6 +735,78 @@ SelectionManager::getChartMatrixIdentification() const return m_chartMatrixIdentification; } +/** + * @return Identification for chart two histogram identification + */ +SelectionItemChartTwoHistogram* +SelectionManager::getChartTwoHistogramIdentification() +{ + return m_chartTwoHistogramIdentification.get(); +} + +/** + * @return Identification for chart two histogram identification + */ +const SelectionItemChartTwoHistogram* +SelectionManager::getChartTwoHistogramIdentification() const +{ + return m_chartTwoHistogramIdentification.get(); +} + +/** + * @return Identification for chart two line series identification + */ +SelectionItemChartTwoLineSeries* +SelectionManager::getChartTwoLineSeriesIdentification() +{ + return m_chartTwoLineSeriesIdentification.get(); +} + +/** + * @return Identification for chart two line series identification + */ +const SelectionItemChartTwoLineSeries* +SelectionManager::getChartTwoLineSeriesIdentification() const +{ + return m_chartTwoLineSeriesIdentification.get(); +} + +/** + * @return Identification for chart two label identification + */ +SelectionItemChartTwoLabel* +SelectionManager::getChartTwoLabelIdentification() +{ + return m_chartTwoLabelIdentification.get(); +} + +/** + * @return Identification for chart two label identification + */ +const SelectionItemChartTwoLabel* +SelectionManager::getChartTwoLabelIdentification() const +{ + return m_chartTwoLabelIdentification.get(); +} + +/** + * @return Identification for chart two matrix identification. + */ +SelectionItemChartTwoMatrix* +SelectionManager::getChartTwoMatrixIdentification() +{ + return m_chartTwoMatrixIdentification.get(); +} + +/** + * @return Identification for chart two matrix identification. + */ +const SelectionItemChartTwoMatrix* +SelectionManager::getChartTwoMatrixIdentification() const +{ + return m_chartTwoMatrixIdentification.get(); +} + /** * @return Identification for CIFTI Connectivity Matrix Row/Column. */ diff --git a/src/Brain/SelectionManager.h b/src/Brain/SelectionManager.h index ae33c68c94c19ea865599f1c44470d3a9191a4c2..731496ee3ca5df7ea8f4fb5d101a9d74165019a4 100644 --- a/src/Brain/SelectionManager.h +++ b/src/Brain/SelectionManager.h @@ -21,6 +21,8 @@ */ /*LICENSE_END*/ +#include + #include "CaretObject.h" #include "EventListenerInterface.h" @@ -35,6 +37,10 @@ namespace caret { class SelectionItemChartFrequencySeries; class SelectionItemChartMatrix; class SelectionItemChartTimeSeries; + class SelectionItemChartTwoHistogram; + class SelectionItemChartTwoLabel; + class SelectionItemChartTwoLineSeries; + class SelectionItemChartTwoMatrix; class SelectionItemCiftiConnectivityMatrixRowColumn; class SelectionItemFocusSurface; class SelectionItemFocusVolume; @@ -128,6 +134,22 @@ namespace caret { const SelectionItemCiftiConnectivityMatrixRowColumn* getCiftiConnectivityMatrixRowColumnIdentification() const; + SelectionItemChartTwoHistogram* getChartTwoHistogramIdentification(); + + const SelectionItemChartTwoHistogram* getChartTwoHistogramIdentification() const; + + SelectionItemChartTwoLineSeries* getChartTwoLineSeriesIdentification(); + + const SelectionItemChartTwoLineSeries* getChartTwoLineSeriesIdentification() const; + + SelectionItemChartTwoLabel* getChartTwoLabelIdentification(); + + const SelectionItemChartTwoLabel* getChartTwoLabelIdentification() const; + + SelectionItemChartTwoMatrix* getChartTwoMatrixIdentification(); + + const SelectionItemChartTwoMatrix* getChartTwoMatrixIdentification() const; + AString getIdentificationText(const Brain* brain) const; void filterSelections(const bool applySelectionBackgroundFiltering); @@ -177,6 +199,14 @@ namespace caret { SelectionItemChartTimeSeries* m_chartTimeSeriesIdentification; + std::unique_ptr m_chartTwoHistogramIdentification; + + std::unique_ptr m_chartTwoLineSeriesIdentification; + + std::unique_ptr m_chartTwoLabelIdentification; + + std::unique_ptr m_chartTwoMatrixIdentification; + SelectionItemCiftiConnectivityMatrixRowColumn* m_ciftiConnectivityMatrixRowColumnIdentfication; SelectionItemFocusSurface* m_surfaceFocusIdentification; diff --git a/src/Brain/SessionManager.cxx b/src/Brain/SessionManager.cxx index 5a466d276c47098d29f9186bcbad3c85c3ec4f8e..d1cc889e333a2cc233990f7b46c71fd388fb8646 100644 --- a/src/Brain/SessionManager.cxx +++ b/src/Brain/SessionManager.cxx @@ -29,6 +29,7 @@ #include "BackgroundAndForegroundColorsSceneHelper.h" #include "Brain.h" #include "BrowserTabContent.h" +#include "BrowserWindowContent.h" #include "CaretAssert.h" #include "CaretLogger.h" #include "CaretPreferences.h" @@ -41,14 +42,18 @@ #include "EventBrowserTabGetAll.h" #include "EventBrowserTabIndicesGetAll.h" #include "EventBrowserTabNew.h" +#include "EventBrowserWindowContent.h" +#include "EventCaretPreferencesGet.h" #include "EventModelAdd.h" #include "EventModelDelete.h" #include "EventModelGetAll.h" +#include "EventModelGetAllDisplayed.h" #include "EventProgressUpdate.h" #include "ImageCaptureSettings.h" #include "LogManager.h" #include "MapYokingGroupEnum.h" #include "ModelWholeBrain.h" +#include "Scene.h" #include "SceneAttributes.h" #include "SceneClass.h" #include "SceneClassArray.h" @@ -76,17 +81,24 @@ SessionManager::SessionManager() m_browserTabs[i] = NULL; } + for (int32_t i = 0; i < BrainConstants::MAXIMUM_NUMBER_OF_BROWSER_WINDOWS; i++) { + CaretAssertStdArrayIndex(m_browserWindowContent, i); + m_browserWindowContent[i] = new BrowserWindowContent(i); + } EventManager::get()->addEventListener(this, EventTypeEnum::EVENT_BROWSER_TAB_DELETE); EventManager::get()->addEventListener(this, EventTypeEnum::EVENT_BROWSER_TAB_GET); EventManager::get()->addEventListener(this, EventTypeEnum::EVENT_BROWSER_TAB_GET_ALL); EventManager::get()->addEventListener(this, EventTypeEnum::EVENT_BROWSER_TAB_INDICES_GET_ALL); EventManager::get()->addEventListener(this, EventTypeEnum::EVENT_BROWSER_TAB_NEW); + EventManager::get()->addEventListener(this, EventTypeEnum::EVENT_BROWSER_WINDOW_CONTENT); + EventManager::get()->addEventListener(this, EventTypeEnum::EVENT_CARET_PREFERENCES_GET); EventManager::get()->addEventListener(this, EventTypeEnum::EVENT_MODEL_ADD); EventManager::get()->addEventListener(this, EventTypeEnum::EVENT_MODEL_DELETE); EventManager::get()->addEventListener(this, EventTypeEnum::EVENT_MODEL_GET_ALL); + EventManager::get()->addEventListener(this, EventTypeEnum::EVENT_MODEL_GET_ALL_DISPLAYED); - Brain* brain = new Brain(); + Brain* brain = new Brain(m_caretPreferences); m_brains.push_back(brain); } @@ -107,6 +119,10 @@ SessionManager::~SessionManager() } } + std::for_each(m_browserWindowContent.begin(), + m_browserWindowContent.end(), + [](BrowserWindowContent* bwc) { if (bwc != NULL) delete bwc; } ); + int32_t numberOfBrains = getNumberOfBrains(); for (int32_t i = (numberOfBrains - 1); i >= 0; i--) { delete m_brains[i]; @@ -340,6 +356,39 @@ SessionManager::receiveEvent(Event* event) } } } + else if (event->getEventType() == EventTypeEnum::EVENT_BROWSER_WINDOW_CONTENT) { + EventBrowserWindowContent* windowEvent = + dynamic_cast(event); + CaretAssert(windowEvent); + + windowEvent->setEventProcessed(); + + const int32_t windowIndex = windowEvent->getBrowserWindowIndex(); + CaretAssertStdArrayIndex(m_browserWindowContent, windowIndex); + + CaretAssert(m_browserWindowContent[windowIndex]); + + switch (windowEvent->getMode()) { + case EventBrowserWindowContent::Mode::DELETER: + m_browserWindowContent[windowIndex]->setValid(false); + break; + case EventBrowserWindowContent::Mode::GET: + windowEvent->setBrowserWindowContent(m_browserWindowContent[windowIndex]); + break; + case EventBrowserWindowContent::Mode::NEW: + m_browserWindowContent[windowIndex]->setValid(true); + windowEvent->setBrowserWindowContent(m_browserWindowContent[windowIndex]); + break; + } + } + else if (event->getEventType() == EventTypeEnum::EVENT_CARET_PREFERENCES_GET) { + EventCaretPreferencesGet* preferencesEvent = + dynamic_cast(event); + CaretAssert(preferencesEvent); + + preferencesEvent->setCaretPreferences(m_caretPreferences); + preferencesEvent->setEventProcessed(); + } else if (event->getEventType() == EventTypeEnum::EVENT_MODEL_ADD) { EventModelAdd* addModelsEvent = dynamic_cast(event); @@ -381,6 +430,19 @@ SessionManager::receiveEvent(Event* event) getModelsEvent->addModels(m_models); } + else if (event->getEventType() == EventTypeEnum::EVENT_MODEL_GET_ALL_DISPLAYED) { + EventModelGetAllDisplayed* getDisplayedModelsEvent = + dynamic_cast(event); + CaretAssert(getDisplayedModelsEvent); + + for (const auto tab : m_browserTabs) { + if (tab != NULL) { + getDisplayedModelsEvent->addModel(tab->getModelForDisplay()); + } + } + + getDisplayedModelsEvent->setEventProcessed(); + } } /** @@ -501,6 +563,19 @@ SessionManager::saveToScene(const SceneAttributes* sceneAttributes, sceneClass->addChild(new SceneClassArray("m_browserTabs", browserTabSceneClasses)); + /* + * Save browser windows + */ + SceneObjectMapIntegerKey* browserClassMap = new SceneObjectMapIntegerKey("browserWindowContentMap", + SceneObjectDataTypeEnum::SCENE_CLASS); + for (auto bw : m_browserWindowContent) { + if (bw->isValid()) { + browserClassMap->addClass(bw->getWindowIndex(), + bw->saveToScene(sceneAttributes, "m_browserWindowContent")); + } + } + sceneClass->addChild(browserClassMap); + sceneClass->addChild(m_imageCaptureDialogSettings->saveToScene(sceneAttributes, "m_imageCaptureDialogSettings")); @@ -696,7 +771,7 @@ SessionManager::restoreFromScene(const SceneAttributes* sceneAttributes, brainClass); } else { - Brain* brain = new Brain(); + Brain* brain = new Brain(m_caretPreferences); brain->restoreFromScene(sceneAttributes, brainClass); } @@ -744,6 +819,72 @@ SessionManager::restoreFromScene(const SceneAttributes* sceneAttributes, m_browserTabs[tabIndex] = tab; } + /* + * Restore windows + */ + const SceneObjectMapIntegerKey* browserClassMap = sceneClass->getMapIntegerKey("browserWindowContentMap"); + if (browserClassMap != NULL) { + /* + * m_browserWindowContent was added in Feb 2018 at which time the + * aspect locking buttons for window and tab were consolidated into + * one button. + */ + const std::vector windowIndices = browserClassMap->getKeys(); + for (const auto windowIndex : windowIndices) { + const SceneClass* windowClass = browserClassMap->classValue(windowIndex); + CaretAssert(windowClass); + CaretAssertStdArrayIndex(m_browserWindowContent, windowIndex); + m_browserWindowContent[windowIndex]->restoreFromScene(sceneAttributes, + windowClass); + m_browserWindowContent[windowIndex]->setValid(true); + } + } + else { + /* + * For scenes before Feb 2018, need to restore from the GUI's browser window + * to the browser window content. + */ + const Scene* scene = sceneAttributes->getScene(); + CaretAssert(scene); + + const SceneClass* guiManagerClass = scene->getClassWithName("guiManager"); + if (guiManagerClass->getName() != "guiManager") { + sceneAttributes->addToErrorMessage("Top level scene class should be guiManager but it is: " + + guiManagerClass->getName()); + return; + } + + const SceneClassArray* browserWindowArray = guiManagerClass->getClassArray("m_brainBrowserWindows"); + if (browserWindowArray != NULL) { + const int32_t numBrowserClasses = browserWindowArray->getNumberOfArrayElements(); + for (int32_t i = 0; i < numBrowserClasses; i++) { + const SceneClass* browserClass = browserWindowArray->getClassAtIndex(i); + CaretAssert(browserClass); + int32_t windowIndex = browserClass->getIntegerValue("m_browserWindowIndex", -1); + if (windowIndex < 0) { + windowIndex = i; + } + CaretAssert(windowIndex >= 0); + CaretAssertStdArrayIndex(m_browserWindowContent, windowIndex); + m_browserWindowContent[windowIndex]->restoreFromOldBrainBrowserWindowScene(sceneAttributes, + browserClass); + m_browserWindowContent[windowIndex]->setValid(true); + } + } + } + + const int32_t numValidBrowserWindows = std::count_if(m_browserWindowContent.begin(), + m_browserWindowContent.end(), + [](BrowserWindowContent* bwc) { return bwc->isValid(); }); + if (numValidBrowserWindows <= 0) { + sceneAttributes->addToErrorMessage("Scene error, no browser window content was restored"); + return; + } + + for (auto brainPtr : m_brains) { + brainPtr->restoreModelChartOneToModelChartTwo(); + } + /* * Restore foreground and background colors to scene foreground and background colors */ diff --git a/src/Brain/SessionManager.h b/src/Brain/SessionManager.h index c5a49b5220eeeb457a67a8ee93bdbebdf4a49750..9590293272177760b37c8ba0e39e62efc45f0550 100644 --- a/src/Brain/SessionManager.h +++ b/src/Brain/SessionManager.h @@ -21,6 +21,8 @@ */ /*LICENSE_END*/ +#include + #include "ApplicationTypeEnum.h" #include "BrainConstants.h" #include "CaretObject.h" @@ -31,6 +33,7 @@ namespace caret { class Brain; class BrowserTabContent; + class BrowserWindowContent; class CaretPreferences; class CiftiConnectivityMatrixDataFileManager; class CiftiFiberTrajectoryManager; @@ -97,6 +100,9 @@ namespace caret { /** The browser tabs */ BrowserTabContent* m_browserTabs[BrainConstants::MAXIMUM_NUMBER_OF_BROWSER_TABS]; + /** The browser window content */ + std::array m_browserWindowContent; + /** Holds valid models */ std::vector m_models; diff --git a/src/Brain/SurfaceNodeColoring.cxx b/src/Brain/SurfaceNodeColoring.cxx index 1996f510ecb697912225754c2fe919dd91efadf6..14a7b0d6eaa3f8d57a410da2c3b8207600f934d8 100644 --- a/src/Brain/SurfaceNodeColoring.cxx +++ b/src/Brain/SurfaceNodeColoring.cxx @@ -59,7 +59,6 @@ #include "OverlaySet.h" #include "Palette.h" #include "PaletteColorMapping.h" -#include "PaletteFile.h" #include "PaletteScalarAndColor.h" #include "RgbaFile.h" #include "SessionManager.h" @@ -251,10 +250,10 @@ SurfaceNodeColoring::showBrainordinateHighlightRegionOfInterest(const Brain* bra uint8_t foregroundColorByte[4]; prefs->getBackgroundAndForegroundColors()->getColorForegroundSurfaceView(foregroundColorByte); const float foregroundColor[4] = { - foregroundColorByte[0], - foregroundColorByte[1], - foregroundColorByte[2], - 1.0 + static_cast(foregroundColorByte[0]) / 255.0f, + static_cast(foregroundColorByte[1]) / 255.0f, + static_cast(foregroundColorByte[2]) / 255.0f, + 1.0f }; const BrainordinateRegionOfInterest* roi = brain->getBrainordinateHighlightRegionOfInterest(); @@ -689,7 +688,7 @@ SurfaceNodeColoring::assignLabelTableColors(const GiftiLabelTable* labelTable, float outlineRGBA[4]; - CaretColorEnum::toRGBFloat(outlineColor, outlineRGBA); + CaretColorEnum::toRGBAFloat(outlineColor, outlineRGBA); outlineRGBA[3] = 1.0; /* @@ -819,14 +818,51 @@ SurfaceNodeColoring::assignMetricColoring(const BrainStructure* brainStructure, return false; } - const PaletteColorMapping* paletteColorMapping = metricFile->getPaletteColorMapping(displayColumn); + PaletteColorMapping* paletteColorMapping = metricFile->getPaletteColorMapping(displayColumn); + + bool useThreshMapFileFlag = false; + switch (paletteColorMapping->getThresholdType()) { + case PaletteThresholdTypeEnum::THRESHOLD_TYPE_FILE: + useThreshMapFileFlag = true; + break; + case PaletteThresholdTypeEnum::THRESHOLD_TYPE_MAPPED: + break; + case PaletteThresholdTypeEnum::THRESHOLD_TYPE_MAPPED_AVERAGE_AREA: + break; + case PaletteThresholdTypeEnum::THRESHOLD_TYPE_NORMAL: + break; + case PaletteThresholdTypeEnum::THRESHOLD_TYPE_OFF: + break; + } + + const float* metricDisplayData = metricFile->getValuePointerForColumn(displayColumn); + float* metricThresholdData = const_cast(metricDisplayData); + PaletteColorMapping* thresholdPaletteColorMapping = paletteColorMapping; + + if (useThreshMapFileFlag) { + const CaretMappableDataFileAndMapSelectionModel* threshFileModel = metricFile->getMapThresholdFileSelectionModel(displayColumn); + CaretAssert(threshFileModel); + const CaretMappableDataFile* threshMapFile = threshFileModel->getSelectedFile(); + if (threshMapFile != NULL) { + const MetricFile* threshMetricFile = dynamic_cast(threshMapFile); + if (threshMetricFile != NULL) { + const int32_t threshMapIndex = threshFileModel->getSelectedMapIndex(); + if ((threshMapIndex >= 0) + && (threshMapIndex < threshMapFile->getNumberOfMaps())) { + metricThresholdData = const_cast(threshMetricFile->getValuePointerForColumn(threshMapIndex)); + thresholdPaletteColorMapping = const_cast(threshMapFile->getMapPaletteColorMapping(threshMapIndex)); + CaretAssert(thresholdPaletteColorMapping); + } + } + } + } /* * Invalidate all coloring. */ for (int32_t i = 0; i < numberOfNodes; i++) { rgbv[i*4+3] = 0.0; - } + } /* * Get min/max ranges. @@ -835,8 +871,6 @@ SurfaceNodeColoring::assignMetricColoring(const BrainStructure* brainStructure, if (thresholdColumn < 0) { thresholdColumn = displayColumn; } - const float* metricDisplayData = metricFile->getValuePointerForColumn(displayColumn); - const float* metricThresholdData = metricFile->getValuePointerForColumn(thresholdColumn); FastStatistics* statistics = NULL; @@ -850,24 +884,16 @@ SurfaceNodeColoring::assignMetricColoring(const BrainStructure* brainStructure, } CaretAssert(statistics); - //const FastStatistics* statistics = metricFile->getMapFastStatistics(displayColumn); - - const Brain* brain = brainStructure->getBrain(); - const AString paletteName = paletteColorMapping->getSelectedPaletteName(); - const Palette* palette = brain->getPaletteFile()->getPaletteByName(paletteName); - if ((statistics != NULL) - && (palette != NULL)) { + if (statistics != NULL) { NodeAndVoxelColoring::colorScalarsWithPalette(statistics, paletteColorMapping, - palette, - metricDisplayData, + metricDisplayData, + thresholdPaletteColorMapping, metricThresholdData, numberOfNodes, rgbv); } - else { - CaretLogSevere("Selected palette for metric is invalid: \"" + paletteName + "\""); - } + return true; } @@ -912,8 +938,7 @@ SurfaceNodeColoring::assignCiftiMappableConnectivityMatrixColoring(const BrainSt const StructureEnum::Enum structure = brainStructure->getStructure(); std::vector dataValues(numberOfNodes); - ciftiConnectivityMatrixFile->getMapSurfaceNodeColoring(brainStructure->getBrain()->getPaletteFile(), - mapIndex, + ciftiConnectivityMatrixFile->getMapSurfaceNodeColoring(mapIndex, structure, rgbv, &dataValues[0], @@ -1057,8 +1082,7 @@ SurfaceNodeColoring::assignCiftiDenseLabelColoring(const DisplayPropertiesLabels * Update coloring */ if (ciftiLabelFile->isMapColoringValid(mapIndex) == false) { - ciftiLabelFile->updateScalarColoringForMap(mapIndex, - brain->getPaletteFile()); + ciftiLabelFile->updateScalarColoringForMap(mapIndex); } std::vector dataValues(numberOfNodes); @@ -1067,8 +1091,7 @@ SurfaceNodeColoring::assignCiftiDenseLabelColoring(const DisplayPropertiesLabels * Assigns colors for all nodes. */ const StructureEnum::Enum structure = brainStructure->getStructure(); - ciftiLabelFile->getMapSurfaceNodeColoring(brainStructure->getBrain()->getPaletteFile(), - mapIndex, + ciftiLabelFile->getMapSurfaceNodeColoring(mapIndex, structure, rgbv, &dataValues[0], @@ -1155,8 +1178,7 @@ SurfaceNodeColoring::assignCiftiParcelLabelColoring(const DisplayPropertiesLabel * Update coloring */ if (ciftiParcelLabelFile->isMapColoringValid(mapIndex) == false) { - ciftiParcelLabelFile->updateScalarColoringForMap(mapIndex, - brain->getPaletteFile()); + ciftiParcelLabelFile->updateScalarColoringForMap(mapIndex); } std::vector dataValues(numberOfNodes); @@ -1165,8 +1187,7 @@ SurfaceNodeColoring::assignCiftiParcelLabelColoring(const DisplayPropertiesLabel * Assigns colors for all nodes. */ const StructureEnum::Enum structure = brainStructure->getStructure(); - ciftiParcelLabelFile->getMapSurfaceNodeColoring(brainStructure->getBrain()->getPaletteFile(), - mapIndex, + ciftiParcelLabelFile->getMapSurfaceNodeColoring(mapIndex, structure, rgbv, &dataValues[0], @@ -1239,14 +1260,12 @@ SurfaceNodeColoring::assignCiftiScalarColoring(const BrainStructure* brainStruct * Update coloring */ if (ciftiScalarFile->isMapColoringValid(mapIndex) == false) { - ciftiScalarFile->updateScalarColoringForMap(mapIndex, - brain->getPaletteFile()); + ciftiScalarFile->updateScalarColoringForMap(mapIndex); } std::vector dataValues(numberOfNodes); const StructureEnum::Enum structure = brainStructure->getStructure(); - ciftiScalarFile->getMapSurfaceNodeColoring(brainStructure->getBrain()->getPaletteFile(), - mapIndex, + ciftiScalarFile->getMapSurfaceNodeColoring(mapIndex, structure, rgbv, &dataValues[0], @@ -1298,14 +1317,12 @@ SurfaceNodeColoring::assignCiftiParcelScalarColoring(const BrainStructure* brain * Update coloring */ if (ciftiParcelScalarFile->isMapColoringValid(mapIndex) == false) { - ciftiParcelScalarFile->updateScalarColoringForMap(mapIndex, - brain->getPaletteFile()); + ciftiParcelScalarFile->updateScalarColoringForMap(mapIndex); } std::vector dataValues(numberOfNodes); const StructureEnum::Enum structure = brainStructure->getStructure(); - ciftiParcelScalarFile->getMapSurfaceNodeColoring(brainStructure->getBrain()->getPaletteFile(), - mapIndex, + ciftiParcelScalarFile->getMapSurfaceNodeColoring(mapIndex, structure, rgbv, &dataValues[0], @@ -1357,8 +1374,7 @@ SurfaceNodeColoring::assignCiftiDataSeriesColoring(const BrainStructure* brainSt * Update coloring */ if (ciftiDataSeriesFile->isMapColoringValid(mapIndex) == false) { - ciftiDataSeriesFile->updateScalarColoringForMap(mapIndex, - brain->getPaletteFile()); + ciftiDataSeriesFile->updateScalarColoringForMap(mapIndex); } /* @@ -1366,8 +1382,7 @@ SurfaceNodeColoring::assignCiftiDataSeriesColoring(const BrainStructure* brainSt */ std::vector dataValues(numberOfNodes); const StructureEnum::Enum structure = brainStructure->getStructure(); - ciftiDataSeriesFile->getMapSurfaceNodeColoring(brainStructure->getBrain()->getPaletteFile(), - mapIndex, + ciftiDataSeriesFile->getMapSurfaceNodeColoring(mapIndex, structure, rgbv, &dataValues[0], @@ -1419,8 +1434,7 @@ SurfaceNodeColoring::assignCiftiParcelSeriesColoring(const BrainStructure* brain * Update coloring */ if (ciftiParcelSeriesFile->isMapColoringValid(mapIndex) == false) { - ciftiParcelSeriesFile->updateScalarColoringForMap(mapIndex, - brain->getPaletteFile()); + ciftiParcelSeriesFile->updateScalarColoringForMap(mapIndex); } /* @@ -1428,8 +1442,7 @@ SurfaceNodeColoring::assignCiftiParcelSeriesColoring(const BrainStructure* brain */ std::vector dataValues(numberOfNodes); const StructureEnum::Enum structure = brainStructure->getStructure(); - ciftiParcelSeriesFile->getMapSurfaceNodeColoring(brainStructure->getBrain()->getPaletteFile(), - mapIndex, + ciftiParcelSeriesFile->getMapSurfaceNodeColoring(mapIndex, structure, rgbv, &dataValues[0], @@ -1456,19 +1469,24 @@ SurfaceNodeColoring::assignCiftiParcelSeriesColoring(const BrainStructure* brain * True if coloring is valid, else false. */ bool -SurfaceNodeColoring::assignRgbaColoring(const BrainStructure* /*brainStructure*/, - const RgbaFile* /*rgbaFile*/, +SurfaceNodeColoring::assignRgbaColoring(const BrainStructure* brainStructure, + const RgbaFile* rgbaFile, const int32_t /*mapIndex*/, const int32_t numberOfNodes, float* rgbv) { - CaretAssertMessage(0, "Add implementation."); + if ( ! rgbaFile->isMappableToSurfaceStructure(brainStructure->getStructure())) { + return false; + } + + if (rgbaFile->isEmpty()) { + return false; + } + for (int32_t i = 0; i < numberOfNodes; i++) { const int32_t i4 = i * 4; - rgbv[i4] = 0.0; - rgbv[i4+1] = 0.0; - rgbv[i4+2] = 1.0; - rgbv[i4+3] = 1.0; + + rgbaFile->getVertexRGBA(i, &rgbv[i4]); } return true; diff --git a/src/Brain/SurfaceSelectionModel.cxx b/src/Brain/SurfaceSelectionModel.cxx index 44b8aa8c0852c730a9110a896fa47fa1eec4e8c4..57add87cab283aecf0e6b36aad060bc50a5d81d9 100644 --- a/src/Brain/SurfaceSelectionModel.cxx +++ b/src/Brain/SurfaceSelectionModel.cxx @@ -389,6 +389,8 @@ SurfaceSelectionModel::saveToScene(const SceneAttributes* /*sceneAttributes*/, Surface* surface = getSurface(); if (surface != NULL) { + sceneClass->addString("m_selectedSurfaceFullPath", + surface->getFileName()); sceneClass->addString("m_selectedSurface", surface->getFileNameNoPath()); } @@ -416,21 +418,55 @@ SurfaceSelectionModel::restoreFromScene(const SceneAttributes* /*sceneAttributes return; } + std::vector allSurfaces = getAvailableSurfaces(); + + const AString& surfaceFileNameFullPath = sceneClass->getStringValue("m_selectedSurfaceFullPath", + ""); + /* + * For full path, find the best match using the right-most characters that + * will contain any relative path. When scene files are moved to different + * computers the full path may change the parts of the path nearest the + * name of the file will match. + */ + Surface* pathNameMatchSurface = NULL; + int32_t pathNameMatchLength = 0; + if ( ! surfaceFileNameFullPath.isEmpty()) { + for (auto surface : allSurfaces) { + const AString name = surface->getFileName(); + const int32_t numMatch = name.countMatchingCharactersFromEnd(surfaceFileNameFullPath); + if (numMatch > pathNameMatchLength) { + pathNameMatchLength = numMatch; + pathNameMatchSurface = surface; + } + } + } + + /* + * Match name of file with NO path + * Always restore this so that the object is marked as restored + * (within the 'get' method). Otherwise if compiled debug, this + * object will get logged as 'not restored'. + */ + Surface* nameMatchSurface = NULL; const AString& surfaceFileName = sceneClass->getStringValue("m_selectedSurface", ""); - if (surfaceFileName.isEmpty() == false) { - std::vector surfaces = getAvailableSurfaces(); - - for (std::vector::iterator iter = surfaces.begin(); - iter != surfaces.end(); - iter++) { - Surface* s = *iter; - if (s->getFileNameNoPath() == surfaceFileName) { - setSurface(s); + if ( ! surfaceFileName.isEmpty()) { + for (auto surface : allSurfaces) { + if (surface->getFileNameNoPath() == surfaceFileName) { + nameMatchSurface = surface; break; } } - + } + + if (pathNameMatchSurface != NULL) { + setSurface(pathNameMatchSurface); + } + else if (nameMatchSurface != NULL) { + setSurface(nameMatchSurface); + } + else { + setSurface(NULL); } } diff --git a/src/Brain/VolumeSliceInterpolationEdgeEffectsMaskingEnum.cxx b/src/Brain/VolumeSliceInterpolationEdgeEffectsMaskingEnum.cxx new file mode 100644 index 0000000000000000000000000000000000000000..97b2ca9a2bf658f9e059853af4aeaaad3ed57745 --- /dev/null +++ b/src/Brain/VolumeSliceInterpolationEdgeEffectsMaskingEnum.cxx @@ -0,0 +1,410 @@ + +/*LICENSE_START*/ +/* + * Copyright (C) 2017 Washington University School of Medicine + * + * 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 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ +/*LICENSE_END*/ + +#include +#define __VOLUME_SLICE_INTERPOLATION_EDGE_EFFECTS_MASKING_ENUM_DECLARE__ +#include "VolumeSliceInterpolationEdgeEffectsMaskingEnum.h" +#undef __VOLUME_SLICE_INTERPOLATION_EDGE_EFFECTS_MASKING_ENUM_DECLARE__ + +#include "CaretAssert.h" + +using namespace caret; + + +/** + * \class caret::VolumeSliceInterpolationEdgeEffectsMaskingEnum + * \brief Enumerated type for edge effects masking. + * + * Using this enumerated type in the GUI with an EnumComboBoxTemplate + * + * Header File (.h) + * Forward declare the data type: + * class EnumComboBoxTemplate; + * + * Declare the member: + * EnumComboBoxTemplate* m_volumeSliceInterpolationEdgeEffectsMaskingEnumComboBox; + * + * Declare a slot that is called when user changes selection + * private slots: + * void volumeSliceInterpolationEdgeEffectsMaskingEnumComboBoxItemActivated(); + * + * Implementation File (.cxx) + * Include the header files + * #include "EnumComboBoxTemplate.h" + * #include "VolumeSliceInterpolationEdgeEffectsMaskingEnum.h" + * + * Instatiate: + * m_volumeSliceInterpolationEdgeEffectsMaskingEnumComboBox = new EnumComboBoxTemplate(this); + * m_volumeSliceInterpolationEdgeEffectsMaskingEnumComboBox->setup(); + * + * Get notified when the user changes the selection: + * QObject::connect(m_volumeSliceInterpolationEdgeEffectsMaskingEnumComboBox, SIGNAL(itemActivated()), + * this, SLOT(volumeSliceInterpolationEdgeEffectsMaskingEnumComboBoxItemActivated())); + * + * Update the selection: + * m_volumeSliceInterpolationEdgeEffectsMaskingEnumComboBox->setSelectedItem(NEW_VALUE); + * + * Read the selection: + * const VolumeSliceInterpolationEdgeEffectsMaskingEnum::Enum VARIABLE = m_volumeSliceInterpolationEdgeEffectsMaskingEnumComboBox->getSelectedItem(); + * + */ + +/** + * Constructor. + * + * @param enumValue + * An enumerated value. + * @param name + * Name of enumerated value. + * + * @param guiName + * User-friendly name for use in user-interface. + */ +VolumeSliceInterpolationEdgeEffectsMaskingEnum::VolumeSliceInterpolationEdgeEffectsMaskingEnum(const Enum enumValue, + const AString& name, + const AString& guiName) +{ + this->enumValue = enumValue; + this->integerCode = integerCodeCounter++; + this->name = name; + this->guiName = guiName; +} + +/** + * Destructor. + */ +VolumeSliceInterpolationEdgeEffectsMaskingEnum::~VolumeSliceInterpolationEdgeEffectsMaskingEnum() +{ +} + +AString +VolumeSliceInterpolationEdgeEffectsMaskingEnum::getToolTip() +{ + AString toolTip("" + "Masking is used to remove edge effects from cubic
" + "interpolation. In extreme instances, the artifacts
" + "result in blocky and/or striped patterns.
" + "
"); + + std::vector allEnums; + getAllEnums(allEnums); + + for (const auto enumValue : allEnums) { + AString description(toGuiName(enumValue) + " - "); + switch (enumValue) { + case OFF: + description.append("No masking"); + break; + case LOOSE: + description.append("Mask with Trilinear Interpolation"); + break; + case TIGHT: + description.append("Mask with Enclosing Voxel"); + break; + } + toolTip.append(description + "
"); + } + + toolTip.append(""); + + return toolTip; +} + +/** + * Initialize the enumerated metadata. + */ +void +VolumeSliceInterpolationEdgeEffectsMaskingEnum::initialize() +{ + if (initializedFlag) { + return; + } + initializedFlag = true; + + enumData.push_back(VolumeSliceInterpolationEdgeEffectsMaskingEnum(OFF, + "OFF", + "Masking Off")); + + enumData.push_back(VolumeSliceInterpolationEdgeEffectsMaskingEnum(LOOSE, + "LOOSE", + "Masking Loose")); + + enumData.push_back(VolumeSliceInterpolationEdgeEffectsMaskingEnum(TIGHT, + "TIGHT", + "Masking Tight")); + +} + +/** + * Find the data for and enumerated value. + * @param enumValue + * The enumerated value. + * @return Pointer to data for this enumerated type + * or NULL if no data for type or if type is invalid. + */ +const VolumeSliceInterpolationEdgeEffectsMaskingEnum* +VolumeSliceInterpolationEdgeEffectsMaskingEnum::findData(const Enum enumValue) +{ + if (initializedFlag == false) initialize(); + + size_t num = enumData.size(); + for (size_t i = 0; i < num; i++) { + const VolumeSliceInterpolationEdgeEffectsMaskingEnum* d = &enumData[i]; + if (d->enumValue == enumValue) { + return d; + } + } + + return NULL; +} + +/** + * Get a string representation of the enumerated type. + * @param enumValue + * Enumerated value. + * @return + * String representing enumerated value. + */ +AString +VolumeSliceInterpolationEdgeEffectsMaskingEnum::toName(Enum enumValue) { + if (initializedFlag == false) initialize(); + + const VolumeSliceInterpolationEdgeEffectsMaskingEnum* enumInstance = findData(enumValue); + return enumInstance->name; +} + +/** + * Get an enumerated value corresponding to its name. + * @param name + * Name of enumerated value. + * @param isValidOut + * If not NULL, it is set indicating that a + * enum value exists for the input name. + * @return + * Enumerated value. + */ +VolumeSliceInterpolationEdgeEffectsMaskingEnum::Enum +VolumeSliceInterpolationEdgeEffectsMaskingEnum::fromName(const AString& name, bool* isValidOut) +{ + if (initializedFlag == false) initialize(); + + bool validFlag = false; + Enum enumValue = VolumeSliceInterpolationEdgeEffectsMaskingEnum::enumData[0].enumValue; + + for (std::vector::iterator iter = enumData.begin(); + iter != enumData.end(); + iter++) { + const VolumeSliceInterpolationEdgeEffectsMaskingEnum& d = *iter; + if (d.name == name) { + enumValue = d.enumValue; + validFlag = true; + break; + } + } + + if (isValidOut != 0) { + *isValidOut = validFlag; + } + else if (validFlag == false) { + CaretAssertMessage(0, AString("Name " + name + "failed to match enumerated value for type VolumeSliceInterpolationEdgeEffectsMaskingEnum")); + } + return enumValue; +} + +/** + * Get a GUI string representation of the enumerated type. + * @param enumValue + * Enumerated value. + * @return + * String representing enumerated value. + */ +AString +VolumeSliceInterpolationEdgeEffectsMaskingEnum::toGuiName(Enum enumValue) { + if (initializedFlag == false) initialize(); + + const VolumeSliceInterpolationEdgeEffectsMaskingEnum* enumInstance = findData(enumValue); + return enumInstance->guiName; +} + +/** + * Get an enumerated value corresponding to its GUI name. + * @param s + * Name of enumerated value. + * @param isValidOut + * If not NULL, it is set indicating that a + * enum value exists for the input name. + * @return + * Enumerated value. + */ +VolumeSliceInterpolationEdgeEffectsMaskingEnum::Enum +VolumeSliceInterpolationEdgeEffectsMaskingEnum::fromGuiName(const AString& guiName, bool* isValidOut) +{ + if (initializedFlag == false) initialize(); + + bool validFlag = false; + Enum enumValue = VolumeSliceInterpolationEdgeEffectsMaskingEnum::enumData[0].enumValue; + + for (std::vector::iterator iter = enumData.begin(); + iter != enumData.end(); + iter++) { + const VolumeSliceInterpolationEdgeEffectsMaskingEnum& d = *iter; + if (d.guiName == guiName) { + enumValue = d.enumValue; + validFlag = true; + break; + } + } + + if (isValidOut != 0) { + *isValidOut = validFlag; + } + else if (validFlag == false) { + CaretAssertMessage(0, AString("guiName " + guiName + "failed to match enumerated value for type VolumeSliceInterpolationEdgeEffectsMaskingEnum")); + } + return enumValue; +} + +/** + * Get the integer code for a data type. + * + * @return + * Integer code for data type. + */ +int32_t +VolumeSliceInterpolationEdgeEffectsMaskingEnum::toIntegerCode(Enum enumValue) +{ + if (initializedFlag == false) initialize(); + const VolumeSliceInterpolationEdgeEffectsMaskingEnum* enumInstance = findData(enumValue); + return enumInstance->integerCode; +} + +/** + * Find the data type corresponding to an integer code. + * + * @param integerCode + * Integer code for enum. + * @param isValidOut + * If not NULL, on exit isValidOut will indicate if + * integer code is valid. + * @return + * Enum for integer code. + */ +VolumeSliceInterpolationEdgeEffectsMaskingEnum::Enum +VolumeSliceInterpolationEdgeEffectsMaskingEnum::fromIntegerCode(const int32_t integerCode, bool* isValidOut) +{ + if (initializedFlag == false) initialize(); + + bool validFlag = false; + Enum enumValue = VolumeSliceInterpolationEdgeEffectsMaskingEnum::enumData[0].enumValue; + + for (std::vector::iterator iter = enumData.begin(); + iter != enumData.end(); + iter++) { + const VolumeSliceInterpolationEdgeEffectsMaskingEnum& enumInstance = *iter; + if (enumInstance.integerCode == integerCode) { + enumValue = enumInstance.enumValue; + validFlag = true; + break; + } + } + + if (isValidOut != 0) { + *isValidOut = validFlag; + } + else if (validFlag == false) { + CaretAssertMessage(0, AString("Integer code " + AString::number(integerCode) + "failed to match enumerated value for type VolumeSliceInterpolationEdgeEffectsMaskingEnum")); + } + return enumValue; +} + +/** + * Get all of the enumerated type values. The values can be used + * as parameters to toXXX() methods to get associated metadata. + * + * @param allEnums + * A vector that is OUTPUT containing all of the enumerated values. + */ +void +VolumeSliceInterpolationEdgeEffectsMaskingEnum::getAllEnums(std::vector& allEnums) +{ + if (initializedFlag == false) initialize(); + + allEnums.clear(); + + for (std::vector::iterator iter = enumData.begin(); + iter != enumData.end(); + iter++) { + allEnums.push_back(iter->enumValue); + } +} + +/** + * Get all of the names of the enumerated type values. + * + * @param allNames + * A vector that is OUTPUT containing all of the names of the enumerated values. + * @param isSorted + * If true, the names are sorted in alphabetical order. + */ +void +VolumeSliceInterpolationEdgeEffectsMaskingEnum::getAllNames(std::vector& allNames, const bool isSorted) +{ + if (initializedFlag == false) initialize(); + + allNames.clear(); + + for (std::vector::iterator iter = enumData.begin(); + iter != enumData.end(); + iter++) { + allNames.push_back(VolumeSliceInterpolationEdgeEffectsMaskingEnum::toName(iter->enumValue)); + } + + if (isSorted) { + std::sort(allNames.begin(), allNames.end()); + } +} + +/** + * Get all of the GUI names of the enumerated type values. + * + * @param allNames + * A vector that is OUTPUT containing all of the GUI names of the enumerated values. + * @param isSorted + * If true, the names are sorted in alphabetical order. + */ +void +VolumeSliceInterpolationEdgeEffectsMaskingEnum::getAllGuiNames(std::vector& allGuiNames, const bool isSorted) +{ + if (initializedFlag == false) initialize(); + + allGuiNames.clear(); + + for (std::vector::iterator iter = enumData.begin(); + iter != enumData.end(); + iter++) { + allGuiNames.push_back(VolumeSliceInterpolationEdgeEffectsMaskingEnum::toGuiName(iter->enumValue)); + } + + if (isSorted) { + std::sort(allGuiNames.begin(), allGuiNames.end()); + } +} + diff --git a/src/Brain/VolumeSliceInterpolationEdgeEffectsMaskingEnum.h b/src/Brain/VolumeSliceInterpolationEdgeEffectsMaskingEnum.h new file mode 100644 index 0000000000000000000000000000000000000000..bb1f06fcc7f57ef509ee91c71072c62311f1a05e --- /dev/null +++ b/src/Brain/VolumeSliceInterpolationEdgeEffectsMaskingEnum.h @@ -0,0 +1,108 @@ +#ifndef __VOLUME_SLICE_INTERPOLATION_EDGE_EFFECTS_MASKING_ENUM_H__ +#define __VOLUME_SLICE_INTERPOLATION_EDGE_EFFECTS_MASKING_ENUM_H__ + +/*LICENSE_START*/ +/* + * Copyright (C) 2017 Washington University School of Medicine + * + * 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 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ +/*LICENSE_END*/ + + +#include +#include +#include "AString.h" + +namespace caret { + +class VolumeSliceInterpolationEdgeEffectsMaskingEnum { + +public: + /** + * Enumerated values. + */ + enum Enum { + /** Off */ + OFF, + /** Loose - Trilinear */ + LOOSE, + /** Tight - Enclosing Voxel */ + TIGHT + }; + + + ~VolumeSliceInterpolationEdgeEffectsMaskingEnum(); + + static AString toName(Enum enumValue); + + static Enum fromName(const AString& name, bool* isValidOut); + + static AString toGuiName(Enum enumValue); + + static Enum fromGuiName(const AString& guiName, bool* isValidOut); + + static int32_t toIntegerCode(Enum enumValue); + + static Enum fromIntegerCode(const int32_t integerCode, bool* isValidOut); + + static void getAllEnums(std::vector& allEnums); + + static void getAllNames(std::vector& allNames, const bool isSorted); + + static void getAllGuiNames(std::vector& allGuiNames, const bool isSorted); + + static AString getToolTip(); + +private: + VolumeSliceInterpolationEdgeEffectsMaskingEnum(const Enum enumValue, + const AString& name, + const AString& guiName); + + static const VolumeSliceInterpolationEdgeEffectsMaskingEnum* findData(const Enum enumValue); + + /** Holds all instance of enum values and associated metadata */ + static std::vector enumData; + + /** Initialize instances that contain the enum values and metadata */ + static void initialize(); + + /** Indicates instance of enum values and metadata have been initialized */ + static bool initializedFlag; + + /** Auto generated integer codes */ + static int32_t integerCodeCounter; + + /** The enumerated type value for an instance */ + Enum enumValue; + + /** The integer code associated with an enumerated value */ + int32_t integerCode; + + /** The name, a text string that is identical to the enumerated value */ + AString name; + + /** A user-friendly name that is displayed in the GUI */ + AString guiName; +}; + +#ifdef __VOLUME_SLICE_INTERPOLATION_EDGE_EFFECTS_MASKING_ENUM_DECLARE__ +std::vector VolumeSliceInterpolationEdgeEffectsMaskingEnum::enumData; +bool VolumeSliceInterpolationEdgeEffectsMaskingEnum::initializedFlag = false; +int32_t VolumeSliceInterpolationEdgeEffectsMaskingEnum::integerCodeCounter = 0; +#endif // __VOLUME_SLICE_INTERPOLATION_EDGE_EFFECTS_MASKING_ENUM_DECLARE__ + +} // namespace +#endif //__VOLUME_SLICE_INTERPOLATION_EDGE_EFFECTS_MASKING_ENUM_H__ diff --git a/src/Brain/VolumeSliceSettings.cxx b/src/Brain/VolumeSliceSettings.cxx index d0de0a4aaec3d2f2eb116a9981772cbe8bd66e21..22f50b108ad79d79f6471247dcccb1b3d27d4f49 100644 --- a/src/Brain/VolumeSliceSettings.cxx +++ b/src/Brain/VolumeSliceSettings.cxx @@ -40,6 +40,8 @@ using namespace caret; * \ingroup Brain */ +const static VolumeSliceInterpolationEdgeEffectsMaskingEnum::Enum defaultVolumeSliceInterpolationEdgeMaskType = + VolumeSliceInterpolationEdgeEffectsMaskingEnum::LOOSE; /** * Constructor. */ @@ -47,8 +49,11 @@ VolumeSliceSettings::VolumeSliceSettings() : CaretObject() { m_sliceViewPlane = VolumeSliceViewPlaneEnum::AXIAL; + m_slicePlanesAllViewLayout = VolumeSliceViewAllPlanesLayoutEnum::GRID_LAYOUT; m_sliceDrawingType = VolumeSliceDrawingTypeEnum::VOLUME_SLICE_DRAW_SINGLE; m_sliceProjectionType = VolumeSliceProjectionTypeEnum::VOLUME_SLICE_PROJECTION_ORTHOGONAL; + m_volumeSliceInterpolationEdgeEffectsMaskingType = defaultVolumeSliceInterpolationEdgeMaskType; + m_montageNumberOfColumns = 6; // was 3; m_montageNumberOfRows = 4; m_montageSliceSpacing = 5; @@ -61,15 +66,18 @@ VolumeSliceSettings::VolumeSliceSettings() m_sliceEnabledParasagittal = true; m_initializedFlag = false; - //m_lastVolumeFile = NULL; - + m_sceneAssistant = new SceneClassAssistant(); m_sceneAssistant->add("m_sliceViewPlane", &m_sliceViewPlane); + m_sceneAssistant->add("m_slicePlanesAllViewLayout", + &m_slicePlanesAllViewLayout); m_sceneAssistant->add("m_sliceDrawingType", &m_sliceDrawingType); m_sceneAssistant->add("m_sliceProjectionType", &m_sliceProjectionType); + m_sceneAssistant->add("m_volumeSliceInterpolationEdgeEffectsMaskingType", + &m_volumeSliceInterpolationEdgeEffectsMaskingType); m_sceneAssistant->add("m_montageNumberOfColumns", &m_montageNumberOfColumns); m_sceneAssistant->add("m_montageNumberOfRows", @@ -130,8 +138,10 @@ void VolumeSliceSettings::copyHelperVolumeSliceSettings(const VolumeSliceSettings& obj) { m_sliceViewPlane = obj.m_sliceViewPlane; + m_slicePlanesAllViewLayout = obj.m_slicePlanesAllViewLayout; m_sliceDrawingType = obj.m_sliceDrawingType; m_sliceProjectionType = obj.m_sliceProjectionType; + m_volumeSliceInterpolationEdgeEffectsMaskingType = obj.m_volumeSliceInterpolationEdgeEffectsMaskingType; m_montageNumberOfColumns = obj.m_montageNumberOfColumns; m_montageNumberOfRows = obj.m_montageNumberOfRows; @@ -174,6 +184,8 @@ VolumeSliceSettings::getDescriptionOfContent(const ModelTypeEnum::Enum modelType switch (modelType) { case ModelTypeEnum::MODEL_TYPE_CHART: break; + case ModelTypeEnum::MODEL_TYPE_CHART_TWO: + break; case ModelTypeEnum::MODEL_TYPE_INVALID: break; case ModelTypeEnum::MODEL_TYPE_SURFACE: @@ -256,7 +268,29 @@ VolumeSliceSettings::setSliceViewPlane(const VolumeSliceViewPlaneEnum::Enum slic m_sliceViewPlane = slicePlane; } -/** +/** + * @return The layout for all slices view (grid, row, column) + */ +VolumeSliceViewAllPlanesLayoutEnum::Enum +VolumeSliceSettings::getSlicePlanesAllViewLayout() const +{ + return m_slicePlanesAllViewLayout; +} + +/** + * Set the layout for all slices view (grid, row, column) + * + * @param slicePlanesAllViewLayout + * New value for layout. + */ +void +VolumeSliceSettings::setSlicePlanesAllViewLayout(const VolumeSliceViewAllPlanesLayoutEnum::Enum slicePlanesAllViewLayout) +{ + m_slicePlanesAllViewLayout = slicePlanesAllViewLayout; +} + + +/** * @return Type of slice drawing (single/montage) */ VolumeSliceDrawingTypeEnum::Enum @@ -277,8 +311,29 @@ VolumeSliceSettings::setSliceDrawingType(const VolumeSliceDrawingTypeEnum::Enum m_sliceDrawingType = sliceDrawingType; } -/** - * @return Type of slice projection (oblique/orthogonal) +/** + * @return The masking used when drawing an oblique volume slice + */ +VolumeSliceInterpolationEdgeEffectsMaskingEnum::Enum +VolumeSliceSettings::getVolumeSliceInterpolationEdgeEffectsMaskingType() const +{ + return m_volumeSliceInterpolationEdgeEffectsMaskingType; +} + +/** + * Set the masking used when drawing an oblique volume slice. + * + * @param maskingType + * Type of masking. + */ +void +VolumeSliceSettings::setVolumeSliceInterpolationEdgeEffectsMaskingType(const VolumeSliceInterpolationEdgeEffectsMaskingEnum::Enum maskingType) +{ + m_volumeSliceInterpolationEdgeEffectsMaskingType = maskingType; +} + +/** + * @return Type of slice projection (oblique/orthogonal) */ VolumeSliceProjectionTypeEnum::Enum VolumeSliceSettings::getSliceProjectionType() const @@ -488,10 +543,13 @@ VolumeSliceSettings::setSliceIndexAxial(const VolumeMappableInterface* volumeFil const int64_t sliceIndexAxial) { CaretAssert(volumeFile); - float xyz[3]; - volumeFile->indexToSpace(0, 0, sliceIndexAxial, xyz); - m_sliceCoordinateAxial = xyz[2]; + float xyz[3]; + volumeFile->indexToSpace(getSliceIndexParasagittal(volumeFile), + getSliceIndexCoronal(volumeFile), + sliceIndexAxial, + xyz); + selectSlicesAtCoordinate(xyz); } /** @@ -546,10 +604,13 @@ VolumeSliceSettings::setSliceIndexCoronal(const VolumeMappableInterface* volumeF const int64_t sliceIndexCoronal) { CaretAssert(volumeFile); - float xyz[3]; - volumeFile->indexToSpace(0, sliceIndexCoronal, 0, xyz); - m_sliceCoordinateCoronal = xyz[1]; + float xyz[3]; + volumeFile->indexToSpace(getSliceIndexParasagittal(volumeFile), + sliceIndexCoronal, + getSliceIndexAxial(volumeFile), + xyz); + selectSlicesAtCoordinate(xyz); } /** @@ -601,10 +662,13 @@ VolumeSliceSettings::setSliceIndexParasagittal(const VolumeMappableInterface* vo const int64_t sliceIndexParasagittal) { CaretAssert(volumeFile); - float xyz[3]; - volumeFile->indexToSpace(sliceIndexParasagittal, 0, 0, xyz); - m_sliceCoordinateParasagittal = xyz[0]; + float xyz[3]; + volumeFile->indexToSpace(sliceIndexParasagittal, + getSliceIndexCoronal(volumeFile), + getSliceIndexAxial(volumeFile), + xyz); + selectSlicesAtCoordinate(xyz); } /** @@ -777,10 +841,11 @@ VolumeSliceSettings::restoreFromScene(const SceneAttributes* sceneAttributes, } /* - * Added in scene version 2 + * Added in scene version 2 or later */ m_sliceDrawingType = VolumeSliceDrawingTypeEnum::VOLUME_SLICE_DRAW_SINGLE; m_sliceProjectionType = VolumeSliceProjectionTypeEnum::VOLUME_SLICE_PROJECTION_ORTHOGONAL; + m_volumeSliceInterpolationEdgeEffectsMaskingType = defaultVolumeSliceInterpolationEdgeMaskType; m_sceneAssistant->restoreMembers(sceneAttributes, sceneClass); diff --git a/src/Brain/VolumeSliceSettings.h b/src/Brain/VolumeSliceSettings.h index f653ea75d95345181e94683a26f63e05ffba4750..9afbed1eacfc52c608f15c96213dfc20eaf6e4b7 100644 --- a/src/Brain/VolumeSliceSettings.h +++ b/src/Brain/VolumeSliceSettings.h @@ -26,7 +26,9 @@ #include "ModelTypeEnum.h" #include "SceneableInterface.h" #include "VolumeSliceDrawingTypeEnum.h" +#include "VolumeSliceInterpolationEdgeEffectsMaskingEnum.h" #include "VolumeSliceProjectionTypeEnum.h" +#include "VolumeSliceViewAllPlanesLayoutEnum.h" #include "VolumeSliceViewPlaneEnum.h" namespace caret { @@ -51,10 +53,18 @@ namespace caret { void setSliceViewPlane(VolumeSliceViewPlaneEnum::Enum sliceAxisMode); + VolumeSliceViewAllPlanesLayoutEnum::Enum getSlicePlanesAllViewLayout() const; + + void setSlicePlanesAllViewLayout(const VolumeSliceViewAllPlanesLayoutEnum::Enum slicePlanesAllViewLayout); + VolumeSliceDrawingTypeEnum::Enum getSliceDrawingType() const; void setSliceDrawingType(const VolumeSliceDrawingTypeEnum::Enum sliceDrawingType); + VolumeSliceInterpolationEdgeEffectsMaskingEnum::Enum getVolumeSliceInterpolationEdgeEffectsMaskingType() const; + + void setVolumeSliceInterpolationEdgeEffectsMaskingType(const VolumeSliceInterpolationEdgeEffectsMaskingEnum::Enum maskingType); + VolumeSliceProjectionTypeEnum::Enum getSliceProjectionType() const; void setSliceProjectionType(const VolumeSliceProjectionTypeEnum::Enum sliceProjectionType); @@ -144,10 +154,16 @@ namespace caret { /** Axis of slice being viewed */ VolumeSliceViewPlaneEnum::Enum m_sliceViewPlane; + + /** Layout of slice in all slices view */ + VolumeSliceViewAllPlanesLayoutEnum::Enum m_slicePlanesAllViewLayout; /** Type of slice drawing (single/montage) */ VolumeSliceDrawingTypeEnum::Enum m_sliceDrawingType; + /** Type of masking for oblique slice drawing */ + VolumeSliceInterpolationEdgeEffectsMaskingEnum::Enum m_volumeSliceInterpolationEdgeEffectsMaskingType; + /** Type of slice projection (oblique/orthogonal) */ VolumeSliceProjectionTypeEnum::Enum m_sliceProjectionType; @@ -165,7 +181,7 @@ namespace caret { mutable float m_sliceCoordinateCoronal; mutable float m_sliceCoordinateAxial; - + bool m_sliceEnabledParasagittal; bool m_sliceEnabledCoronal; diff --git a/src/Brain/VolumeSliceViewAllPlanesLayoutEnum.cxx b/src/Brain/VolumeSliceViewAllPlanesLayoutEnum.cxx new file mode 100644 index 0000000000000000000000000000000000000000..47cfe263507508a804363206e8f5835fce1f4696 --- /dev/null +++ b/src/Brain/VolumeSliceViewAllPlanesLayoutEnum.cxx @@ -0,0 +1,377 @@ + +/*LICENSE_START*/ +/* + * Copyright (C) 2017 Washington University School of Medicine + * + * 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 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ +/*LICENSE_END*/ + +#include +#define __VOLUME_SLICE_VIEW_ALL_PLANES_LAYOUT_ENUM_DECLARE__ +#include "VolumeSliceViewAllPlanesLayoutEnum.h" +#undef __VOLUME_SLICE_VIEW_ALL_PLANES_LAYOUT_ENUM_DECLARE__ + +#include "CaretAssert.h" + +using namespace caret; + + +/** + * \class caret::VolumeSliceViewAllPlanesLayoutEnum + * \brief Enumerated type for layout in slice view all mode. + * + * Using this enumerated type in the GUI with an EnumComboBoxTemplate + * + * Header File (.h) + * Forward declare the data type: + * class EnumComboBoxTemplate; + * + * Declare the member: + * EnumComboBoxTemplate* m_volumeSliceViewAllPlanesLayoutEnumComboBox; + * + * Declare a slot that is called when user changes selection + * private slots: + * void volumeSliceViewAllPlanesLayoutEnumComboBoxItemActivated(); + * + * Implementation File (.cxx) + * Include the header files + * #include "EnumComboBoxTemplate.h" + * #include "VolumeSliceViewAllPlanesLayoutEnum.h" + * + * Instatiate: + * m_volumeSliceViewAllPlanesLayoutEnumComboBox = new EnumComboBoxTemplate(this); + * m_volumeSliceViewAllPlanesLayoutEnumComboBox->setup(); + * + * Get notified when the user changes the selection: + * QObject::connect(m_volumeSliceViewAllPlanesLayoutEnumComboBox, SIGNAL(itemActivated()), + * this, SLOT(volumeSliceViewAllPlanesLayoutEnumComboBoxItemActivated())); + * + * Update the selection: + * m_volumeSliceViewAllPlanesLayoutEnumComboBox->setSelectedItem(NEW_VALUE); + * + * Read the selection: + * const VolumeSliceViewAllPlanesLayoutEnum::Enum VARIABLE = m_volumeSliceViewAllPlanesLayoutEnumComboBox->getSelectedItem(); + * + */ + +/** + * Constructor. + * + * @param enumValue + * An enumerated value. + * @param name + * Name of enumerated value. + * + * @param guiName + * User-friendly name for use in user-interface. + */ +VolumeSliceViewAllPlanesLayoutEnum::VolumeSliceViewAllPlanesLayoutEnum(const Enum enumValue, + const AString& name, + const AString& guiName) +{ + this->enumValue = enumValue; + this->integerCode = integerCodeCounter++; + this->name = name; + this->guiName = guiName; +} + +/** + * Destructor. + */ +VolumeSliceViewAllPlanesLayoutEnum::~VolumeSliceViewAllPlanesLayoutEnum() +{ +} + +/** + * Initialize the enumerated metadata. + */ +void +VolumeSliceViewAllPlanesLayoutEnum::initialize() +{ + if (initializedFlag) { + return; + } + initializedFlag = true; + + enumData.push_back(VolumeSliceViewAllPlanesLayoutEnum(GRID_LAYOUT, + "GRID_LAYOUT", + "Grid")); + + enumData.push_back(VolumeSliceViewAllPlanesLayoutEnum(COLUMN_LAYOUT, + "COLUMN_LAYOUT", + "Column")); + + enumData.push_back(VolumeSliceViewAllPlanesLayoutEnum(ROW_LAYOUT, + "ROW_LAYOUT", + "Row")); + +} + +/** + * Find the data for and enumerated value. + * @param enumValue + * The enumerated value. + * @return Pointer to data for this enumerated type + * or NULL if no data for type or if type is invalid. + */ +const VolumeSliceViewAllPlanesLayoutEnum* +VolumeSliceViewAllPlanesLayoutEnum::findData(const Enum enumValue) +{ + if (initializedFlag == false) initialize(); + + size_t num = enumData.size(); + for (size_t i = 0; i < num; i++) { + const VolumeSliceViewAllPlanesLayoutEnum* d = &enumData[i]; + if (d->enumValue == enumValue) { + return d; + } + } + + return NULL; +} + +/** + * Get a string representation of the enumerated type. + * @param enumValue + * Enumerated value. + * @return + * String representing enumerated value. + */ +AString +VolumeSliceViewAllPlanesLayoutEnum::toName(Enum enumValue) { + if (initializedFlag == false) initialize(); + + const VolumeSliceViewAllPlanesLayoutEnum* enumInstance = findData(enumValue); + return enumInstance->name; +} + +/** + * Get an enumerated value corresponding to its name. + * @param name + * Name of enumerated value. + * @param isValidOut + * If not NULL, it is set indicating that a + * enum value exists for the input name. + * @return + * Enumerated value. + */ +VolumeSliceViewAllPlanesLayoutEnum::Enum +VolumeSliceViewAllPlanesLayoutEnum::fromName(const AString& name, bool* isValidOut) +{ + if (initializedFlag == false) initialize(); + + bool validFlag = false; + Enum enumValue = VolumeSliceViewAllPlanesLayoutEnum::enumData[0].enumValue; + + for (std::vector::iterator iter = enumData.begin(); + iter != enumData.end(); + iter++) { + const VolumeSliceViewAllPlanesLayoutEnum& d = *iter; + if (d.name == name) { + enumValue = d.enumValue; + validFlag = true; + break; + } + } + + if (isValidOut != 0) { + *isValidOut = validFlag; + } + else if (validFlag == false) { + CaretAssertMessage(0, AString("Name " + name + "failed to match enumerated value for type VolumeSliceViewAllPlanesLayoutEnum")); + } + return enumValue; +} + +/** + * Get a GUI string representation of the enumerated type. + * @param enumValue + * Enumerated value. + * @return + * String representing enumerated value. + */ +AString +VolumeSliceViewAllPlanesLayoutEnum::toGuiName(Enum enumValue) { + if (initializedFlag == false) initialize(); + + const VolumeSliceViewAllPlanesLayoutEnum* enumInstance = findData(enumValue); + return enumInstance->guiName; +} + +/** + * Get an enumerated value corresponding to its GUI name. + * @param s + * Name of enumerated value. + * @param isValidOut + * If not NULL, it is set indicating that a + * enum value exists for the input name. + * @return + * Enumerated value. + */ +VolumeSliceViewAllPlanesLayoutEnum::Enum +VolumeSliceViewAllPlanesLayoutEnum::fromGuiName(const AString& guiName, bool* isValidOut) +{ + if (initializedFlag == false) initialize(); + + bool validFlag = false; + Enum enumValue = VolumeSliceViewAllPlanesLayoutEnum::enumData[0].enumValue; + + for (std::vector::iterator iter = enumData.begin(); + iter != enumData.end(); + iter++) { + const VolumeSliceViewAllPlanesLayoutEnum& d = *iter; + if (d.guiName == guiName) { + enumValue = d.enumValue; + validFlag = true; + break; + } + } + + if (isValidOut != 0) { + *isValidOut = validFlag; + } + else if (validFlag == false) { + CaretAssertMessage(0, AString("guiName " + guiName + "failed to match enumerated value for type VolumeSliceViewAllPlanesLayoutEnum")); + } + return enumValue; +} + +/** + * Get the integer code for a data type. + * + * @return + * Integer code for data type. + */ +int32_t +VolumeSliceViewAllPlanesLayoutEnum::toIntegerCode(Enum enumValue) +{ + if (initializedFlag == false) initialize(); + const VolumeSliceViewAllPlanesLayoutEnum* enumInstance = findData(enumValue); + return enumInstance->integerCode; +} + +/** + * Find the data type corresponding to an integer code. + * + * @param integerCode + * Integer code for enum. + * @param isValidOut + * If not NULL, on exit isValidOut will indicate if + * integer code is valid. + * @return + * Enum for integer code. + */ +VolumeSliceViewAllPlanesLayoutEnum::Enum +VolumeSliceViewAllPlanesLayoutEnum::fromIntegerCode(const int32_t integerCode, bool* isValidOut) +{ + if (initializedFlag == false) initialize(); + + bool validFlag = false; + Enum enumValue = VolumeSliceViewAllPlanesLayoutEnum::enumData[0].enumValue; + + for (std::vector::iterator iter = enumData.begin(); + iter != enumData.end(); + iter++) { + const VolumeSliceViewAllPlanesLayoutEnum& enumInstance = *iter; + if (enumInstance.integerCode == integerCode) { + enumValue = enumInstance.enumValue; + validFlag = true; + break; + } + } + + if (isValidOut != 0) { + *isValidOut = validFlag; + } + else if (validFlag == false) { + CaretAssertMessage(0, AString("Integer code " + AString::number(integerCode) + "failed to match enumerated value for type VolumeSliceViewAllPlanesLayoutEnum")); + } + return enumValue; +} + +/** + * Get all of the enumerated type values. The values can be used + * as parameters to toXXX() methods to get associated metadata. + * + * @param allEnums + * A vector that is OUTPUT containing all of the enumerated values. + */ +void +VolumeSliceViewAllPlanesLayoutEnum::getAllEnums(std::vector& allEnums) +{ + if (initializedFlag == false) initialize(); + + allEnums.clear(); + + for (std::vector::iterator iter = enumData.begin(); + iter != enumData.end(); + iter++) { + allEnums.push_back(iter->enumValue); + } +} + +/** + * Get all of the names of the enumerated type values. + * + * @param allNames + * A vector that is OUTPUT containing all of the names of the enumerated values. + * @param isSorted + * If true, the names are sorted in alphabetical order. + */ +void +VolumeSliceViewAllPlanesLayoutEnum::getAllNames(std::vector& allNames, const bool isSorted) +{ + if (initializedFlag == false) initialize(); + + allNames.clear(); + + for (std::vector::iterator iter = enumData.begin(); + iter != enumData.end(); + iter++) { + allNames.push_back(VolumeSliceViewAllPlanesLayoutEnum::toName(iter->enumValue)); + } + + if (isSorted) { + std::sort(allNames.begin(), allNames.end()); + } +} + +/** + * Get all of the GUI names of the enumerated type values. + * + * @param allNames + * A vector that is OUTPUT containing all of the GUI names of the enumerated values. + * @param isSorted + * If true, the names are sorted in alphabetical order. + */ +void +VolumeSliceViewAllPlanesLayoutEnum::getAllGuiNames(std::vector& allGuiNames, const bool isSorted) +{ + if (initializedFlag == false) initialize(); + + allGuiNames.clear(); + + for (std::vector::iterator iter = enumData.begin(); + iter != enumData.end(); + iter++) { + allGuiNames.push_back(VolumeSliceViewAllPlanesLayoutEnum::toGuiName(iter->enumValue)); + } + + if (isSorted) { + std::sort(allGuiNames.begin(), allGuiNames.end()); + } +} + diff --git a/src/Brain/VolumeSliceViewAllPlanesLayoutEnum.h b/src/Brain/VolumeSliceViewAllPlanesLayoutEnum.h new file mode 100644 index 0000000000000000000000000000000000000000..3fb1d09215123cb74f2d00d720cb94ecddac1894 --- /dev/null +++ b/src/Brain/VolumeSliceViewAllPlanesLayoutEnum.h @@ -0,0 +1,106 @@ +#ifndef __VOLUME_SLICE_VIEW_ALL_PLANES_LAYOUT_ENUM_H__ +#define __VOLUME_SLICE_VIEW_ALL_PLANES_LAYOUT_ENUM_H__ + +/*LICENSE_START*/ +/* + * Copyright (C) 2017 Washington University School of Medicine + * + * 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 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ +/*LICENSE_END*/ + + +#include +#include +#include "AString.h" + +namespace caret { + +class VolumeSliceViewAllPlanesLayoutEnum { + +public: + /** + * Enumerated values. + */ + enum Enum { + /** */ + GRID_LAYOUT, + /** */ + COLUMN_LAYOUT, + /** */ + ROW_LAYOUT + }; + + + ~VolumeSliceViewAllPlanesLayoutEnum(); + + static AString toName(Enum enumValue); + + static Enum fromName(const AString& name, bool* isValidOut); + + static AString toGuiName(Enum enumValue); + + static Enum fromGuiName(const AString& guiName, bool* isValidOut); + + static int32_t toIntegerCode(Enum enumValue); + + static Enum fromIntegerCode(const int32_t integerCode, bool* isValidOut); + + static void getAllEnums(std::vector& allEnums); + + static void getAllNames(std::vector& allNames, const bool isSorted); + + static void getAllGuiNames(std::vector& allGuiNames, const bool isSorted); + +private: + VolumeSliceViewAllPlanesLayoutEnum(const Enum enumValue, + const AString& name, + const AString& guiName); + + static const VolumeSliceViewAllPlanesLayoutEnum* findData(const Enum enumValue); + + /** Holds all instance of enum values and associated metadata */ + static std::vector enumData; + + /** Initialize instances that contain the enum values and metadata */ + static void initialize(); + + /** Indicates instance of enum values and metadata have been initialized */ + static bool initializedFlag; + + /** Auto generated integer codes */ + static int32_t integerCodeCounter; + + /** The enumerated type value for an instance */ + Enum enumValue; + + /** The integer code associated with an enumerated value */ + int32_t integerCode; + + /** The name, a text string that is identical to the enumerated value */ + AString name; + + /** A user-friendly name that is displayed in the GUI */ + AString guiName; +}; + +#ifdef __VOLUME_SLICE_VIEW_ALL_PLANES_LAYOUT_ENUM_DECLARE__ +std::vector VolumeSliceViewAllPlanesLayoutEnum::enumData; +bool VolumeSliceViewAllPlanesLayoutEnum::initializedFlag = false; +int32_t VolumeSliceViewAllPlanesLayoutEnum::integerCodeCounter = 0; +#endif // __VOLUME_SLICE_VIEW_ALL_PLANES_LAYOUT_ENUM_DECLARE__ + +} // namespace +#endif //__VOLUME_SLICE_VIEW_ALL_PLANES_LAYOUT_ENUM_H__ diff --git a/src/Brain/VolumeSurfaceOutlineColorOrTabModel.cxx b/src/Brain/VolumeSurfaceOutlineColorOrTabModel.cxx index 3595d50c7122e6d0a8fe754593d9ac693d3d0371..08fc0c622cb9e3078838e762d514ae280abbf53a 100644 --- a/src/Brain/VolumeSurfaceOutlineColorOrTabModel.cxx +++ b/src/Brain/VolumeSurfaceOutlineColorOrTabModel.cxx @@ -527,9 +527,6 @@ VolumeSurfaceOutlineColorOrTabModel::Item::saveToScene(const SceneAttributes* /* break; } - sceneClass->addInteger("m_browserTabIndex", - m_browserTabIndex); - return sceneClass; } diff --git a/src/Brain/VolumeSurfaceOutlineModel.cxx b/src/Brain/VolumeSurfaceOutlineModel.cxx index 5ebac64391e247e2ebe63c00f9e44d3b3326e344..25dc6e9e50200f0d92307fcd9f62d9886f3ebf5d 100644 --- a/src/Brain/VolumeSurfaceOutlineModel.cxx +++ b/src/Brain/VolumeSurfaceOutlineModel.cxx @@ -52,16 +52,27 @@ VolumeSurfaceOutlineModel::VolumeSurfaceOutlineModel() validSurfaceTypes.push_back(SurfaceTypeEnum::VERY_INFLATED); m_displayed = false; - m_thickness = VolumeSurfaceOutlineModel::DEFAULT_LINE_THICKNESS; + m_thicknessPixelsObsolete = VolumeSurfaceOutlineModel::DEFAULT_LINE_THICKNESS_PIXELS_OBSOLETE; m_surfaceSelectionModel = new SurfaceSelectionModel(validSurfaceTypes); m_colorOrTabModel = new VolumeSurfaceOutlineColorOrTabModel(); m_sceneAssistant = new SceneClassAssistant(); m_sceneAssistant->add("m_displayed", &m_displayed); - m_sceneAssistant->add("m_thickness", &m_thickness); + m_sceneAssistant->add("m_thickness", &m_thicknessPixelsObsolete); // NOTE: "m_thickness" is OLD name m_sceneAssistant->add("m_surfaceSelectionModel", "SurfaceSelectionModel", m_surfaceSelectionModel); m_sceneAssistant->add("m_colorOrTabModel", "VolumeSurfaceOutlineColorOrTabModel", m_colorOrTabModel); + /* + * Percentage viewport height thickness was added in Feb 2, 2017. + * So, we set the perentage thickness to -1.0f so that when an older + * scene is restored, the value will be -1.0f. The code that draws + * the volume surface outline see this negative value and convert + * the old pixel thickness to this new percentage thickness. + * After adding to the scene assistant, default the percentage thickness. + */ + m_thicknessPercentageViewportHeight = -1.0f; + m_sceneAssistant->add("m_thicknessPercentageViewportHeight", &m_thicknessPercentageViewportHeight); + m_thicknessPercentageViewportHeight = VolumeSurfaceOutlineModel::DEFAULT_LINE_THICKNESS_PERCENTAGE_VIEWPORT_HEIGHT; } /** @@ -84,7 +95,8 @@ void VolumeSurfaceOutlineModel::copyVolumeSurfaceOutlineModel(VolumeSurfaceOutlineModel* modelToCopy) { m_displayed = modelToCopy->m_displayed; - m_thickness = modelToCopy->m_thickness; + m_thicknessPixelsObsolete = modelToCopy->m_thicknessPixelsObsolete; + m_thicknessPercentageViewportHeight = modelToCopy->m_thicknessPercentageViewportHeight; m_surfaceSelectionModel->setSurface(modelToCopy->getSurface()); VolumeSurfaceOutlineColorOrTabModel* colorTabToCopy = modelToCopy->getColorOrTabModel(); @@ -122,23 +134,41 @@ VolumeSurfaceOutlineModel::setDisplayed(const bool displayed) } /** - * @return Thickness for drawing surface. + * @return Thickness for drawing surface outline + */ +float +VolumeSurfaceOutlineModel::getThicknessPercentageViewportHeight() const +{ + return m_thicknessPercentageViewportHeight; +} + +/** + * Set the thickness for drawing surface outline + */ +void +VolumeSurfaceOutlineModel::setThicknessPercentageViewportHeight(const float thickness) +{ + m_thicknessPercentageViewportHeight = thickness; +} + +/** + * @return Thickness for drawing surface (OBSOLETE) */ float -VolumeSurfaceOutlineModel::getThickness() const +VolumeSurfaceOutlineModel::getThicknessPixelsObsolete() const { - return m_thickness; + return m_thicknessPixelsObsolete; } /** - * Set the thickness for drawing the surface. + * Set the thickness for drawing the surface (OBSOLETE) * @param thickness * New value for thickness. */ void -VolumeSurfaceOutlineModel::setThickness(const float thickness) +VolumeSurfaceOutlineModel::setThicknessPixelsObsolete(const float thickness) { - m_thickness = thickness; + m_thicknessPixelsObsolete = thickness; } /** diff --git a/src/Brain/VolumeSurfaceOutlineModel.h b/src/Brain/VolumeSurfaceOutlineModel.h index 9181376d2b9a1f3cbbda3396c16700b5aba65341..64d7e47a3aa9bee0f3227c4f2916ccf5120143c0 100644 --- a/src/Brain/VolumeSurfaceOutlineModel.h +++ b/src/Brain/VolumeSurfaceOutlineModel.h @@ -45,9 +45,13 @@ namespace caret { void setDisplayed(const bool displayed); - float getThickness() const; + float getThicknessPercentageViewportHeight() const; - void setThickness(const float thickness); + void setThicknessPercentageViewportHeight(const float thickness); + + float getThicknessPixelsObsolete() const; + + void setThicknessPixelsObsolete(const float thickness); SurfaceSelectionModel* getSurfaceSelectionModel(); @@ -65,7 +69,8 @@ namespace caret { virtual void restoreFromScene(const SceneAttributes* sceneAttributes, const SceneClass* sceneClass); - static const int32_t DEFAULT_LINE_THICKNESS; + static const float DEFAULT_LINE_THICKNESS_PIXELS_OBSOLETE; + static const float DEFAULT_LINE_THICKNESS_PERCENTAGE_VIEWPORT_HEIGHT; private: VolumeSurfaceOutlineModel(const VolumeSurfaceOutlineModel&); @@ -78,7 +83,9 @@ namespace caret { private: bool m_displayed; - float m_thickness; + float m_thicknessPixelsObsolete; + + float m_thicknessPercentageViewportHeight; SurfaceSelectionModel* m_surfaceSelectionModel; @@ -88,8 +95,8 @@ namespace caret { }; #ifdef __VOLUME_SURFACE_OUTLINE_MODEL_DECLARE__ - const int32_t VolumeSurfaceOutlineModel::DEFAULT_LINE_THICKNESS = 2.0; - + const float VolumeSurfaceOutlineModel::DEFAULT_LINE_THICKNESS_PIXELS_OBSOLETE = 2.0f; + const float VolumeSurfaceOutlineModel::DEFAULT_LINE_THICKNESS_PERCENTAGE_VIEWPORT_HEIGHT = 0.4f; #endif // __VOLUME_SURFACE_OUTLINE_MODEL_DECLARE__ } // namespace diff --git a/src/Brain/VolumeSurfaceOutlineSetModel.cxx b/src/Brain/VolumeSurfaceOutlineSetModel.cxx index 9d334e36b5ae317efd12f5a88ac2cb1591fa6c3a..9af38a1862d7af1160476e0d18148e289132f5d4 100644 --- a/src/Brain/VolumeSurfaceOutlineSetModel.cxx +++ b/src/Brain/VolumeSurfaceOutlineSetModel.cxx @@ -64,7 +64,7 @@ VolumeSurfaceOutlineSetModel::VolumeSurfaceOutlineSetModel() for (int32_t i = 0; i < BrainConstants::MAXIMUM_NUMBER_OF_VOLUME_SURFACE_OUTLINES; i++) { m_outlineModels[i] = new VolumeSurfaceOutlineModel(); } - m_numberOfDisplayedVolumeSurfaceOutlines = 6; //BrainConstants::MINIMUM_NUMBER_OF_VOLUME_SURFACE_OUTLINES; + m_numberOfDisplayedVolumeSurfaceOutlines = 6; m_sceneAssistant = new SceneClassAssistant(); m_sceneAssistant->add("m_numberOfDisplayedVolumeSurfaceOutlines", @@ -233,40 +233,34 @@ VolumeSurfaceOutlineSetModel::selectSurfacesAfterSpecFileLoaded(Brain* brain, for (int32_t i = 0; i < BrainConstants::MAXIMUM_NUMBER_OF_VOLUME_SURFACE_OUTLINES; i++) { m_outlineModels[i]->getColorOrTabModel()->setColor(CaretColorEnum::BLACK); - m_outlineModels[i]->setThickness(VolumeSurfaceOutlineModel::DEFAULT_LINE_THICKNESS); + m_outlineModels[i]->setThicknessPixelsObsolete(VolumeSurfaceOutlineModel::DEFAULT_LINE_THICKNESS_PIXELS_OBSOLETE); } int nextOutlineIndex = 0; addSurfaceOutline(leftMidThickSurface, - VolumeSurfaceOutlineModel::DEFAULT_LINE_THICKNESS, leftTabIndex, CaretColorEnum::BLACK, nextOutlineIndex); addSurfaceOutline(rightMidThickSurface, - VolumeSurfaceOutlineModel::DEFAULT_LINE_THICKNESS, rightTabIndex, CaretColorEnum::BLACK, nextOutlineIndex); addSurfaceOutline(leftWhiteSurface, - VolumeSurfaceOutlineModel::DEFAULT_LINE_THICKNESS, -1, CaretColorEnum::LIME, nextOutlineIndex); addSurfaceOutline(rightWhiteSurface, - VolumeSurfaceOutlineModel::DEFAULT_LINE_THICKNESS, -1, CaretColorEnum::LIME, nextOutlineIndex); addSurfaceOutline(leftPialSurface, - VolumeSurfaceOutlineModel::DEFAULT_LINE_THICKNESS, -1, CaretColorEnum::BLUE, nextOutlineIndex); addSurfaceOutline(rightPialSurface, - VolumeSurfaceOutlineModel::DEFAULT_LINE_THICKNESS, -1, CaretColorEnum::BLUE, nextOutlineIndex); @@ -278,8 +272,6 @@ VolumeSurfaceOutlineSetModel::selectSurfacesAfterSpecFileLoaded(Brain* brain, * * @param surface * Surface that is added. If NULL, no action is taken. - * @param thickness - * Thickness for surface outline. * @param browserTabIndex * If greater than or equal to zero, the color source * is set to this tab index. @@ -294,7 +286,6 @@ VolumeSurfaceOutlineSetModel::selectSurfacesAfterSpecFileLoaded(Brain* brain, */ void VolumeSurfaceOutlineSetModel::addSurfaceOutline(Surface* surface, - const float thickness, const int32_t browserTabIndex, const CaretColorEnum::Enum color, int32_t& outlineIndex) @@ -304,7 +295,8 @@ VolumeSurfaceOutlineSetModel::addSurfaceOutline(Surface* surface, if (outlineIndex < BrainConstants::MAXIMUM_NUMBER_OF_VOLUME_SURFACE_OUTLINES) { VolumeSurfaceOutlineModel* vsos = m_outlineModels[outlineIndex]; vsos->getSurfaceSelectionModel()->setSurface(surface); - vsos->setThickness(thickness); + vsos->setThicknessPixelsObsolete(VolumeSurfaceOutlineModel::DEFAULT_LINE_THICKNESS_PIXELS_OBSOLETE); + vsos->setThicknessPercentageViewportHeight(VolumeSurfaceOutlineModel::DEFAULT_LINE_THICKNESS_PERCENTAGE_VIEWPORT_HEIGHT); if (browserTabIndex >= 0) { vsos->getColorOrTabModel()->setBrowserTabIndex(browserTabIndex); } @@ -336,7 +328,7 @@ VolumeSurfaceOutlineSetModel::saveToScene(const SceneAttributes* sceneAttributes { SceneClass* sceneClass = new SceneClass(instanceName, "VolumeSurfaceOutlineSetModel", - 1); + 2); m_sceneAssistant->saveMembers(sceneAttributes, sceneClass); @@ -371,6 +363,8 @@ VolumeSurfaceOutlineSetModel::restoreFromScene(const SceneAttributes* sceneAttri return; } + const int32_t version = sceneClass->getVersionNumber(); + m_sceneAssistant->restoreMembers(sceneAttributes, sceneClass); @@ -382,5 +376,19 @@ VolumeSurfaceOutlineSetModel::restoreFromScene(const SceneAttributes* sceneAttri m_outlineModels[i]->restoreFromScene(sceneAttributes, outlineModelsArrayClass->getClassAtIndex(i)); } + + /* + * In older scenes, last was drawn on top. + * Now, zero is drawn on top so need to reverse + * the order of the volume surface outlines + */ + if (version < 2) { + if (maxNum > 1) { + const int32_t numDisplayed = getNumberOfDislayedVolumeSurfaceOutlines(); + CaretAssertArrayIndex(m_outlineModels, BrainConstants::MAXIMUM_NUMBER_OF_VOLUME_SURFACE_OUTLINES, numDisplayed - 1); + std::reverse(m_outlineModels, + m_outlineModels + numDisplayed); + } + } } } diff --git a/src/Brain/VolumeSurfaceOutlineSetModel.h b/src/Brain/VolumeSurfaceOutlineSetModel.h index 3956eebd633e3de668ea14c9c3d3ebb8dce46bea..ccffcea2f9446077185ffecd5a6dfc2315e98abe 100644 --- a/src/Brain/VolumeSurfaceOutlineSetModel.h +++ b/src/Brain/VolumeSurfaceOutlineSetModel.h @@ -65,7 +65,6 @@ namespace caret { VolumeSurfaceOutlineSetModel& operator=(const VolumeSurfaceOutlineSetModel&); void addSurfaceOutline(Surface* surface, - const float thickness, const int32_t browserTabIndex, const CaretColorEnum::Enum color, int32_t& outlineIndex); diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 397d53048c997f6ca88f04142d23d64de1fdf7d7..edf0aba96d39a33741074fd808cdfa8c5a39307f 100755 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -3,119 +3,77 @@ # CMAKE_MINIMUM_REQUIRED (VERSION 2.8) -MESSAGE("\nCMake Version: ${CMAKE_VERSION}\n") - # +# Set to true for verbose output when debugging this file +# +SET(WB_CMAKE_VERBOSE_OUTPUT_FLAG TRUE) + +########################################################################################## # +# A function to get all user defined variables with a specified prefix +# From https://cmake.org/Wiki/CMake/Tutorials/SettingVariableGroups # -#message(INFORMATION "\nTo get the correct version of QT, qmake must be in the PATH\n") +function (getListOfVarsStartingWith _prefix _varResult) + get_cmake_property(_vars CACHE_VARIABLES) + string (REGEX MATCHALL "(^|;)${_prefix}[A-Za-z0-9_]*" _matchedVars "${_vars}") + set (${_varResult} ${_matchedVars} PARENT_SCOPE) +endfunction() -#TSC: use "CACHE " syntax in SET commands so they can be overridden by cmake options +########################################################################################## +# +# Workbench CACHE variables +# TSC: use "CACHE " syntax in SET commands so they can be overridden by cmake options +# +set (WORKBENCH_QT5_DISABLE_DEPRECATED FALSE CACHE BOOL + "If TRUE, use of Qt 5 deprecated functionality will cause compiler to fail") +set (WORKBENCH_USE_CMAKE_AUTOMOC FALSE CACHE BOOL + "If TRUE, use CMAKE_AUTOMOC command for generating 'moc' files") +set (WORKBENCH_USE_QT5 TRUE CACHE BOOL + "If TRUE, require Qt5, else Qt4.Specify as argument to cmake: -DWORKBENCH_USE_QT5=TRUE") +set (WORKBENCH_USE_QT5_QOPENGL_WIDGET FALSE CACHE BOOL + "With Qt5, QOpenGLWidget instead of deprecated QGLWidget") +set (WORKBENCH_MESA_DIR "" CACHE PATH + "Directory containing Mesa's 'include' and 'lib' directories. Mesa is optional and used by wb_command's -show-scene operation.") + +########################################################################################## # # Setting the compiler MUST be done before the PROJECT # statement or else an infinite loop will occur indicating # that the compiler has been redefined. # - - - IF(APPLE) ADD_DEFINITIONS(-DCARET_OS_MACOSX) ELSEIF(UNIX) IF(${CMAKE_SYSTEM_NAME} MATCHES "Linux") ADD_DEFINITIONS(-DCARET_OS_LINUX) + SET(CMAKE_POSITION_INDEPENDENT_CODE ON) ENDIF(${CMAKE_SYSTEM_NAME} MATCHES "Linux") ELSEIF(WIN32) ADD_DEFINITIONS(-DCARET_OS_WINDOWS) + # Policy CMP0020 is related to Qt + # https://cmake.org/cmake/help/v3.5/policy/CMP0020.html + if(POLICY CMP0020) + cmake_policy(SET CMP0020 NEW) + endif() IF(MSVC) ADD_DEFINITIONS(-DCARET_OS_WINDOWS_MSVC) - IF(CMAKE_CL_64) -## SET(CMAKE_GENERATOR_TOOLSET "v120_CTP_Nov2012" CACHE STRING "Platform Toolset" FORCE) ADD_DEFINITIONS(-D_USE_MATH_DEFINES -DNOMINMAX) SET( CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -MP -wd4290 -wd4244 -wd4267 -wd4305 -wd4100 -wd4005" ) - ##SET( CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wall -we4061") - SET( ZLIB_INCLUDE_DIR "C:\\dev64\\install\\zlib\\include" CACHE STRING "zlib include directory (headers)") -# SET( ZLIB_LIBRARY "C:\\dev64\\install\\zlib\\lib\\zlib.lib" CACHE STRING "zlib library (binary)") - SET(ZLIB_LIBRARY optimized "C:\\dev64\\install\\zlib\\lib\\zlib.lib" debug "C:\\dev64\\install\\zlib\\lib\\zlibd.lib" CACHE STRING "zlib library (binary)") - SET( OPENSSL_ROOT_DIR "c:\\dev64\\install\\openssl-1.0.1t" CACHE_STRING "open ssl root directory") - + IF(CMAKE_CL_64) ELSE() -## SET(CMAKE_GENERATOR_TOOLSET "v120_CTP_Nov2012" CACHE STRING "Platform Toolset" FORCE) - ADD_DEFINITIONS(-D_USE_MATH_DEFINES -DNOMINMAX) - SET( CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -MP -wd4290 -wd4244 -wd4267 -wd4305 -wd4100 -wd4005" ) - ##SET( CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wall -we4061") - SET( ZLIB_INCLUDE_DIR "C:\\dev32\\install\\zlib\\include" CACHE STRING "zlib include directory (headers)") -# SET( ZLIB_LIBRARY "C:\\dev32\\install\\zlib\\lib\\zlib.lib" CACHE STRING "zlib library (binary)") - SET(ZLIB_LIBRARY optimized "C:\\dev32\\install\\zlib\\lib\\zlib.lib" debug "C:\\dev32\\install\\zlib\\lib\\zlibd.lib" CACHE STRING "zlib library (binary)") - SET( OPENSSL_ROOT_DIR "c:\\dev32\\install\\openssl" CACHE_STRING "open ssl root directory") ENDIF(CMAKE_CL_64) ELSE(MSVC) - SET( OPENSSL_ROOT_DIR "c:\\dev32\\install\\openssl" CACHE_STRING "open ssl root directory") - FIND_PATH(ZLIB_INCLUDE_DIR zlib.h - C:\\dev32\\install\\zlib\\include - C:\\zlib_software\\zlib-1.2.5-install\\include - $ENV{ZLIB_INC_DIR}) - FIND_PATH(ZLIB_LIBRARY libzlib.a - C:\\zlib_software\\zlib-1.2.5-install\\lib) - IF (NOT ZLIB_FOUND) - FIND_PATH(ZLIB_LIBRARY zlib.lib - C:\\dev32\\install\\zlib\\lib) - ENDIF(NOT ZLIB_FOUND) - IF (NOT ZLIB_FOUND) - FIND_PATH(ZLIB_LIBRARY libz.a - $ENV{ZLIB_LIB_DIR}) - ENDIF(NOT ZLIB_FOUND) - - ### SET( ZLIB_INCLUDE_DIR "C:\\dev32\\install\\zlib\\include" CACHE STRING "zlib include directory (headers)") - ### SET( ZLIB_LIBRARY "C:\\dev32\\install\\zlib\\lib\\zlib.lib" CACHE STRING "zlib library (binary)") ENDIF(MSVC) ELSE(APPLE) - MESSAGE(FATAL_ERROR "Unrecognized operating system " ${CMAKE_SYSTEM_NAME}) + MESSAGE(SEND_ERROR "Unrecognized operating system " ${CMAKE_SYSTEM_NAME}) ENDIF(APPLE) - - -#cmake_policy(SET CMP0015 OLD) - -# -# Shows compilation command when true -# -SET(CMAKE_VERBOSE_MAKEFILE TRUE CACHE BOOL "cause all build commands to be displayed") - -# -# Allow support for C11X compiler -# -SET (WORKBENCH_C11X FALSE) -##SET (WORKBENCH_C11X TRUE) -IF ("$ENV{WORKBENCH_CONFIGURE_C11X}" STREQUAL "YES") - SET (WORKBENCH_C11X TRUE) - MESSAGE("Configuring Workbench build with C11X enabled.") -ENDIF ("$ENV{WORKBENCH_CONFIGURE_C11X}" STREQUAL "YES") - - -# -# Set flags for C11 compiler -# Only set for C++ compiler -# C11x options are not recognized by C compiler -# -SET (CLANG_11X_FLAGS "") -SET (GNU_11X_FLAGS "") -SET (INTEL_11X_FLAGS "") -IF (WORKBENCH_C11X) - ADD_DEFINITIONS("-DWORKBENCH_HAVE_C11X") - SET (CLANG_11X_FLAGS "-std=c++11 -stdlib=libstdc++ -Wno-error=c++11-narrowing") - ####SET (CLANG_11X_FLAGS "-std=c++11 -stdlib=libc++ -Wno-error=c++11-narrowing") - SET (GNU_11X_FLAGS "-std=c++11 -Wno-error=c++11-narrowing") - SET (INTEL_11X_FLAGS "-std=c++11 -Wno-error=c++11-narrowing") -ENDIF (WORKBENCH_C11X) - # # Intel compiler # IF (${CMAKE_CXX_COMPILER} MATCHES "^.*icpc$") ADD_DEFINITIONS("-W -Wall -Werror=return-type -Werror=switch -Wunused-parameter") - SET(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} ${INTEL_11X_FLAGS}") ENDIF (${CMAKE_CXX_COMPILER} MATCHES "^.*icpc$") # @@ -129,15 +87,7 @@ IF (${CMAKE_CXX_COMPILER} MATCHES "^.*clang2\\+\\+.*") SET(CLANG_FLAG TRUE) ENDIF (${CMAKE_CXX_COMPILER} MATCHES "^.*clang2\\+\\+.*") IF (CLANG_FLAG) - MESSAGE("USING CLANG COMPILER ${CMAKE_CXX_COMPILER}") ADD_DEFINITIONS("-W -Wall -Werror=return-type -Werror=switch -Wunused-parameter -Wno-deprecated-declarations") - SET(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} ${CLANG_11X_FLAGS}") - ##SET(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} ${CLANG_11X_FLAGS} -W -Wall -Werror=return-type -Werror=switch -Wunused-parameter") - ##SET(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} ${CLANG_11X_FLAGS} -W -Wall -Werror=return-type -Werror=switch -Wunused-parameter" CACHE STRING "C++ compiler options" FORCE) - - IF (WORKBENCH_C11X) - ####set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -std=c++11 -stdlib=libc++") - ENDIF (WORKBENCH_C11X) ENDIF (CLANG_FLAG) @@ -146,48 +96,31 @@ ENDIF (CLANG_FLAG) # statements that do not handle all of the enumerated types # are treated as an error. Also, all warnings. # -###IF (CMAKE_COMPILER_IS_GNUCXX) IF (NOT MSVC) -## SET(CMAKE_CXX_FLAGS "-W -Wall -Werror=return-type -Werror=switch ${CMAKE_CXX_FLAGS}" CACHE STRING "c++ compiler specific options") -## SET(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -W -Wall -Werror=return-type -Werror=switch -std=c++0x") -##SET(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++0x") -if (CMAKE_COMPILER_IS_GNUCC) - execute_process(COMMAND ${CMAKE_C_COMPILER} -dumpversion - OUTPUT_VARIABLE GCC_VERSION) - string(REGEX MATCHALL "[0-9]+" GCC_VERSION_COMPONENTS ${GCC_VERSION}) - list(GET GCC_VERSION_COMPONENTS 0 GCC_MAJOR) - list(GET GCC_VERSION_COMPONENTS 1 GCC_MINOR) - message("gcc major minor version numbers are: " ${GCC_MAJOR},${GCC_MINOR}) - if(${GCC_VERSION} VERSION_LESS "4.2") - #SET(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -W -Wall" CACHE STRING "c++ compiler specific options") - ADD_DEFINITIONS(-W -Wall) - else() - #SET(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -W -Wall -Werror=return-type -Werror=switch" CACHE STRING "c++ compiler specific options") - ADD_DEFINITIONS(-W -Wall -Werror=return-type -Werror=switch -Wunused-parameter) - SET(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} ${GNU_11X_FLAGS}") - endif() - - if (${GCC_VERSION} VERSION_LESS "4.9") - # there is no greater than or equal in CMake - else() - ADD_DEFINITIONS(-Wno-narrowing -Wno-unused-local-typedefs) + if (CMAKE_COMPILER_IS_GNUCC) + execute_process(COMMAND ${CMAKE_C_COMPILER} -dumpversion + OUTPUT_VARIABLE GCC_VERSION) + message("gcc version is: " ${GCC_VERSION}) + if(${GCC_VERSION} VERSION_LESS "4.2") + ADD_DEFINITIONS(-W -Wall) + else() + ADD_DEFINITIONS(-W -Wall -Werror=return-type -Werror=switch -Wunused-parameter) + endif() + + if (${GCC_VERSION} VERSION_LESS "4.9") + # there is no greater than or equal in CMake + else() + ADD_DEFINITIONS(-Wno-narrowing -Wno-unused-local-typedefs) + endif() endif() - -# execute_process(COMMAND uname -n OUTPUT_VARIABLE MACHINE_NAME) -# message("MACHINE_NAME: ${MACHINE_NAME}") -# if (${MACHINE_NAME} MATCHES "linuxbuild") -# message("is linuxbuild") -# SET(CMAKE_EXE_LINKER_FLAGS "-Wl,-E" ${CMAKE_EXE_LINKER_FLAGS}) -# endif() - -endif() ENDIF (NOT MSVC) +########################################################################################## # # If GNU compiler, use SIMD-based dot computation, if possible # -if (CMAKE_COMPILER_IS_GNUCC) - +SET(SIMD_RESULT "Search for SIMD disabled for compiler on this system.") +if (CMAKE_COMPILER_IS_GNUCC OR CLANG_FLAG) # # Define flag to avoid trying to compile SIMD stuff (coded for x86 only) # @@ -198,62 +131,180 @@ if (CMAKE_COMPILER_IS_GNUCC) # IF (WORKBENCH_USE_SIMD) TRY_COMPILE(CPUINFO_COMPILES ${CMAKE_CURRENT_BINARY_DIR}/cpuinfo_compile_test ${CMAKE_CURRENT_SOURCE_DIR}/kloewe/cpuinfo cpuinfo) - MESSAGE("CPUINFO_COMPILES: ${CPUINFO_COMPILES}") + + # + # Add the necessary definition and include directory to enable the + # SIMD-based dot product implementations + # + IF (CPUINFO_COMPILES) + ADD_DEFINITIONS(-DCARET_DOTFCN) + INCLUDE_DIRECTORIES(${CMAKE_SOURCE_DIR}/kloewe/dot/src) + SET(SIMD_RESULT "Enabled") + ELSE() + SET(SIMD_RESULT "Failed when compiling with SIMD") + ENDIF() + ELSE (WORKBENCH_USE_SIMD) + SET(SIMD_RESULT "Search for SIMD disabled, WORKBENCH_USE_SIMD=FALSE") ENDIF (WORKBENCH_USE_SIMD) - - # - # Add the necessary definition and include directory to enable the SIMD-based dot product implementations - # - if (WORKBENCH_USE_SIMD AND CPUINFO_COMPILES) - ADD_DEFINITIONS(-DCARET_DOTFCN) - INCLUDE_DIRECTORIES(${CMAKE_SOURCE_DIR}/kloewe/dot/src) - endif() -endif() - -# -# will set name of XCode project: -# disabled at this time -# -##PROJECT(BorderOptProject C CXX) - +endif(CMAKE_COMPILER_IS_GNUCC OR CLANG_FLAG) + +#TSC: we require c++11, so it needs to be in the compile flags - in older cmake, this needs to be done manually +IF (${CMAKE_VERSION} VERSION_LESS "3.1") + IF (CMAKE_COMPILER_IS_GNUCC) + include(CheckCXXCompilerFlag) + CHECK_CXX_COMPILER_FLAG("-std=c++11" GCC_STD11) + IF (${GCC_STD11}) + SET(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++11") + ELSE (${GCC_STD11}) + SET(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++0x") + ENDIF (${GCC_STD11}) + ENDIF (CMAKE_COMPILER_IS_GNUCC) +ELSE (${CMAKE_VERSION} VERSION_LESS "3.1") + SET(CMAKE_CXX_STANDARD 11) + SET(CMAKE_CXX_STANDARD_REQUIRED TRUE) +ENDIF (${CMAKE_VERSION} VERSION_LESS "3.1") + +########################################################################################## +# +# OpenSSL +# +SET(OPENSSL_RESULT "Not Found") FIND_PACKAGE(OpenSSL) IF(OPENSSL_FOUND) INCLUDE_DIRECTORIES(${OPENSSL_INCLUDE_DIR}) - MESSAGE("OpenSSL version=${OPENSSL_VERSION}") - MESSAGE("OpenSSL include=${OPENSSL_INCLUDE_DIR}") - MESSAGE("OpenSSL libraries=${OPENSSL_LIBRARIES}") + SET(OPENSSL_RESULT "${OPENSSL_VERSION} ${OPENSSL_LIBRARIES}") ELSE (OPENSSL_FOUND) - MESSAGE("OpenSSL NOT FOUND") + MESSAGE("OpenSSL NOT FOUND. May need to define OPENSSL_ROOT_DIR when running CMake.") + MESSAGE(" cmake -DOPENSSL_ROOT_DIR= \n") + MESSAGE("See: https://cmake.org/cmake/help/v3.5/module/FindOpenSSL.html") ENDIF(OPENSSL_FOUND) +########################################################################################## # -# Must have QT 4.8 or later +# Must have QT 4.8 or QT 5, recommended 5.6 or later +# https://cmake.org/cmake/help/v3.5/manual/cmake-packages.7.html#manual:cmake-packages(7) +# Modules List: http://doc.qt.io/qt-5/qtmodules.html # -FIND_PACKAGE(Qt4 4.8 REQUIRED) -IF(QT4_FOUND) -ELSE(QT4_FOUND) - MESSAGE(FATAL_ERROR "QT4 not found") -ENDIF(QT4_FOUND) - -# -# QT include files -# -INCLUDE(${QT_USE_FILE}) +SET(QT_RESULT "Not Found") +SET(CARET_QT5_LINK "") +IF (WORKBENCH_USE_QT5) + # + # The OpenGL module is deprecated in Qt5 and + # is needed only if QGLWidget is used for + # the OpenGL graphics. + # + IF (NOT WORKBENCH_USE_QT5_QOPENGL_WIDGET) + SET(WB_QT_OPENGL_MODULE "OpenGL") + ENDIF () -# -# Try to find Qwt, utherwise use bundle -# + FIND_PACKAGE(Qt5 REQUIRED COMPONENTS Concurrent Core Gui Network ${WB_QT_OPENGL_MODULE} PrintSupport Test Widgets Xml) + IF (WORKBENCH_USE_QT5_QOPENGL_WIDGET) + # + # QGLWidget is deprecated in Qt 5 and is replaced with QOpenGLWidget + # + # If WORKBENCH_USE_QT5_QOPENGL_WIDGET is defined, QOpenGLWidget is used + # for OpenGL. Otherwise, QGLWidget is used. + # + add_definitions(-DWORKBENCH_USE_QT5_QOPENGL_WIDGET) + ELSE (WORKBENCH_USE_QT5_QOPENGL_WIDGET) + remove_definitions(-DWORKBENCH_USE_QT5_QOPENGL_WIDGET) + ENDIF (WORKBENCH_USE_QT5_QOPENGL_WIDGET) + + IF (WORKBENCH_QT5_DISABLE_DEPRECATED) + # Causes Qt to remove declarations for deprecated functionality + add_definitions(-DQT_DISABLE_DEPRECATED_BEFORE=0x050700) + # Enable usage of replacements for Qt deprecated functionality in Workbench source code + add_definitions(-DWORKBENCH_REPLACE_QT_DEPRECATED) + ENDIF (WORKBENCH_QT5_DISABLE_DEPRECATED) + + #small hack to get the qt5 compile flag requirements into libraries + SET(CARET_QT5_LINK "Qt5::Core") + + SET(QT_RESULT "${Qt5_VERSION}") +ELSE () + FIND_PACKAGE(Qt4 4.8 REQUIRED) + IF(QT4_FOUND) + INCLUDE(${QT_USE_FILE}) + SET(QT_RESULT "${QTVERSION}") + ELSE(QT4_FOUND) + MESSAGE(SEND_ERROR "QT4 not found") + ENDIF(QT4_FOUND) +ENDIF () + +########################################################################################## +# +# Try to find Qwt, otherwise use bundle +# +SET(QWT_RESULT "Not Found") IF (NOT WIN32) PKG_CHECK_MODULES(Qwt qwt) ENDIF (NOT WIN32) IF (Qwt_FOUND) - MESSAGE("Qwt library found") - MESSAGE(" INCLUDES ${Qwt_INCLUDE_DIRS}") - MESSAGE(" LIBS ${Qwt_LIBRARIES}") + SET(QWT_RESULT "Using Qwt Library ${Qwt_LIBRARIES}") ELSE (Qwt_FOUND) - MESSAGE("Qwt library not found, using bundled") + SET(QWT_RESULT "In Workbench Source Code") ENDIF (Qwt_FOUND) +########################################################################################## +# +# Need OpenGL +# +SET(OPENGL_RESULT "") +FIND_PACKAGE(OpenGL REQUIRED) +IF (OPENGL_FOUND) + # + # Need help finding includes on Apple + # + IF (APPLE) + # When searching for the include directory, find the location + # for the OpenGL framework rather than an individual header file. + FIND_PATH(OPENGL_INCLUDE_DIR OpenGL.framework + /System/Library/Frameworks + /Library/Frameworks + ~/Library/Frameworks) + ENDIF (APPLE) + # + # OpenGL Include Directory + # + INCLUDE_DIRECTORIES(${OPENGL_INCLUDE_DIR}) + MESSAGE("OpenGL Include Directory: ${OPENGL_INCLUDE_DIR}") + MESSAGE("OpenGL Libraries: ${OPENGL_LIBRARIES}") + SET(OPENGL_RESULT ${OPENGL_LIBRARIES}) +ELSE (OPENGL_FOUND) + MESSAGE(SEND_ERROR "OpenGL Libraries were not found") +ENDIF (OPENGL_FOUND) + +########################################################################################## +# +# Look for GLEW but only on Windows +# +SET(GLEW_RESULT "No") +IF (WIN32) + FIND_PACKAGE(GLEW) +ENDIF () + +IF (GLEW_FOUND) + SET(GLEW_RESULT "${GLEW_INCLUDE_DIRS} ${GLEW_LIBRARIES}") + MESSAGE("GLEW Include Dirs: ${GLEW_INCLUDE_DIRS}") + MESSAGE("GLEW Libraries: ${GLEW_LIBRARIES}") + STRING(FIND ${GLEW_LIBRARIES} "glew32s.lib" GLEW_STATIC_LIB_NAME_INDEX) + ADD_DEFINITIONS(-DHAVE_GLEW) + IF (GLEW_STATIC_LIB_NAME_INDEX GREATER -1) + ADD_DEFINITIONS(-DHAVE_GLEW_STATIC_LIB) + MESSAGE("GLEW Static Library Found") + ENDIF() + INCLUDE_DIRECTORIES(${GLEW_INCLUDE_DIRS}) +ELSE() + SET(GLEW_LIBRARIES "") + SET(GLEW_INCLUDE_DIRS "") + IF (WIN32) + MESSAGE(SEND_ERROR "GLEW not found and is required on Windows for finding OpenGL functions.\n" + " Setting CMAKE_PREFIX_PATH to the GLEW installation directory when\n" + " running CMAKE may help find GLEW.\n") + ENDIF() +ENDIF() + +########################################################################################## # # The Find OpenMP package may not work on all systems and the user may # furnish the paths to the OpenMP files by using environment variables. @@ -290,6 +341,7 @@ ENDIF (NOT OPENMP_FOUND) # # If OpenMP is found, may need to set compiler and linker flags # +SET(OPENMP_RESULT "Not Found") IF (OPENMP_FOUND) # add definitions will add the flag to the linker and resource compilers, which don't understand the openmp option SET(CMAKE_CXX_FLAGS "${OpenMP_CXX_FLAGS} ${CMAKE_CXX_FLAGS}") @@ -303,6 +355,7 @@ IF (OPENMP_FOUND) SET (CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -static-intel") ADD_DEFINITIONS("-static-intel") ENDIF() + SET(OPENMP_RESULT "Yes") ELSE (OPENMP_FOUND) MESSAGE(WARNING "OpenMP was not found") @@ -316,28 +369,33 @@ ELSE (OPENMP_FOUND) ENDIF (CLANG_FLAG) ENDIF(OPENMP_FOUND) -MESSAGE("\nC++ flags ${CMAKE_CXX_FLAGS}\n") - +########################################################################################## # # MUST have ZLIB # +SET(ZLIB_RESULT "Not Found") FIND_PACKAGE(ZLIB) IF ( ZLIB_FOUND ) INCLUDE_DIRECTORIES(${ZLIB_INCLUDE_DIRS}) + SET(ZLIB_RESULT ${ZLIB_VERSION_STRING}) ELSE (ZLIB_FOUND) - MESSAGE(FATAL_ERROR "ZLIB was not found") + MESSAGE(SEND_ERROR "ZLIB was not found. It may be necessary to define " + "the variable ZLIB_ROOT when running cmake so that " + "the FindZLIB module is able to find ZLIB.\n" + " cmake -DZLIB_ROOT= \n" + "See HINTS at " + "https://cmake.org/cmake/help/v3.5/module/FindZLIB.html") ENDIF (ZLIB_FOUND) +########################################################################################## # # Try to find QuaZip, otherwise use bundled # -FIND_PACKAGE(QuaZip) +SET(QUAZIP_RESULT "Not Found") +FIND_PACKAGE(QuaZip QUIET) IF (QUAZIP_FOUND) - MESSAGE("QuaZip library found") - MESSAGE(" INCLUDES ${QUAZIP_INCLUDE_DIRS}") - MESSAGE(" LIBS ${QUAZIP_LIBRARIES}") + SET(QUAZIP_RESULT "Using QuaZip Library ${QUAZIP_LIBRARIES}") ELSE (QUAZIP_FOUND) - MESSAGE("QuaZip library not found, using bundled") # # Quazip needs this defined here for static linking on windows # @@ -345,55 +403,45 @@ ELSE (QUAZIP_FOUND) IF(MSVC) ADD_DEFINITIONS(-DQUAZIP_STATIC) ENDIF(MSVC) - ENDIF(WIN32) + ENDIF(WIN32) + SET(QUAZIP_RESULT "In Workbench Source Code") ENDIF (QUAZIP_FOUND) +########################################################################################## # # Find FreeType # +SET(FREETYPE_RESULT "Not Found") +SET(FTGL_RESULT "Must have FreeType to use FTGL") SET(FTGL_FONT_MODULE_FOR_LINKING "") - +IF (DEFINED WORKBENCH_FREETYPE_DIR) + # FindFreeType looks for environment variable FREETYPE_DIR as a hint + SET(ENV{FREETYPE_DIR} ${WORKBENCH_FREETYPE_DIR}) +ENDIF () FIND_PACKAGE(Freetype) IF (FREETYPE_FOUND) - MESSAGE("FreeType library found") - MESSAGE(" INCLUDES ${FREETYPE_INCLUDE_DIRS}") - MESSAGE(" INCLUDES_FT2_BUILD ${FREETYPE_INCLUDE_DIR_ft2build}") - MESSAGE(" INCLUDES_FT2 ${FREETYPE_INCLUDE_DIR_freetype2}") - MESSAGE(" LIBS ${FREETYPE_LIBRARIES}") - IF (NOT WIN32) PKG_CHECK_MODULES(FTGL ftgl) ENDIF (NOT WIN32) IF (FTGL_FOUND) - MESSAGE("FTGL library found") - MESSAGE(" INCLUDES ${FTGL_INCLUDE_DIRS}") - MESSAGE(" LIBS ${FTGL_LIBRARIES}") + SET(FTGL_RESULT "Using FTGL Library ${FTGL_LIBRARIES}") ELSE (FTGL_FOUND) - MESSAGE("FTGL library not found, using bundled") + SET(FTGL_RESULT "In Workbench Source Code") ENDIF (FTGL_FOUND) ADD_DEFINITIONS(-DHAVE_FREETYPE) + SET(FREETYPE_RESULT "${FREETYPE_VERSION_STRING} ${FREETYPE_LIBRARIES}") ELSE (FREETYPE_FOUND) SET (FREETYPE_LIBRARY "") SET (FREETYPE_LIBRARIES "") MESSAGE("FreeType library NOT found") - MESSAGE(" The environment variable FREETYPE_DIR can be set to the") - MESSAGE(" directory containing FreeType include and lib.") - MESSAGE("") - MESSAGE(" ") - MESSAGE(" On Windows (and possibly other systems) it may be necessary to set") - MESSAGE(" DFREETYPE_INCLUDE_DIR_freetype2") - MESSAGE(" /FreeType-X.Y.Z/include/freetype2") - MESSAGE(" DFREETYPE_INCLUDE_DIR_ft2build") - MESSAGE(" /FreeType-X.Y.Z/include") - MESSAGE(" DFREETYPE_LIBRARY") - MESSAGE(" /FreeType-X.Y.Z/lib/freetype.lib") - MESSAGE(" ") - MESSAGE(" These variable can be set when running cmake. For example:") - MESSAGE(" cmake -DFREETYPE_INCLUDE_DIR_freetype2=/FreeType-X.Y.Z/include/freetype2") - MESSAGE(" ") + MESSAGE(" Define the PATH to FreeType when running CMake") + MESSAGE(" -DWORKBENCH_FREETYPE_DIR=") + MESSAGE(" Where is the directory containing FreeType's") + MESSAGE(" 'include' and 'lib' directories.") ENDIF (FREETYPE_FOUND) +########################################################################################## # # Fixes issue with XCode and newer version of CMake. # It prevents the ZERO_CHECK dependency from running @@ -403,11 +451,12 @@ ENDIF (FREETYPE_FOUND) IF (APPLE) SET (CMAKE_SUPPRESS_REGENERATION TRUE) ENDIF (APPLE) -#============================================================================= + +########################################################################################## # -# Test for offscreen mesa (optional library) -# If found, set some variables. Since, Mesa is only used for -# command line +# Test for offscreen MESA (optional library) +# Mesa is used by wb_command's -show-scene. +# If Mesa is not found, the "-show-scene" command will not be available. # SET(OSMESA_FOUND FALSE) SET(OSMESA_DEFINITION "") @@ -416,46 +465,32 @@ SET(OSMESA_GL_LIBRARY "") SET(OSMESA_GLU_LIBRARY "") SET(OSMESA_INCLUDE_DIRECTORY "") -MESSAGE("OSMESA_DIR: $ENV{OSMESA_DIR}") -IF (EXISTS $ENV{OSMESA_DIR}) - IF (EXISTS $ENV{OSMESA_DIR}/include/GL/osmesa.h) - MESSAGE("Have Mesa Include Directory") +SET(OSMESA_RESULT "Not found") +IF (EXISTS ${WORKBENCH_MESA_DIR}) + IF (EXISTS ${WORKBENCH_MESA_DIR}/include/GL/osmesa.h) FIND_LIBRARY(OSMESA_LIBRARY_FOUND - NAMES OSMesa - HINTS $ENV{OSMESA_DIR}/lib) + NAMES OSMesa OSMESA32 + HINTS ${WORKBENCH_MESA_DIR}/lib) FIND_LIBRARY(OSMESA_GL_LIBRARY_FOUND - NAMES GL - HINTS $ENV{OSMESA_DIR}/lib) + NAMES GL OPENGL32 + HINTS ${WORKBENCH_MESA_DIR}/lib) FIND_LIBRARY(OSMESA_GLU_LIBRARY_FOUND - NAMES GLU - HINTS $ENV{OSMESA_DIR}/lib) - MESSAGE("OSMesa lib: " ${OSMESA_LIBRARY}) + NAMES GLU GLU32 + HINTS ${WORKBENCH_MESA_DIR}/lib) IF (EXISTS ${OSMESA_LIBRARY_FOUND} AND EXISTS ${OSMESA_GL_LIBRARY_FOUND} AND EXISTS ${OSMESA_GLU_LIBRARY_FOUND}) SET(OSMESA_DEFINITION -DHAVE_OSMESA) SET(OSMESA_OFFSCREEN_LIBRARY ${OSMESA_LIBRARY_FOUND}) SET(OSMESA_GL_LIBRARY ${OSMESA_GL_LIBRARY_FOUND}) SET(OSMESA_GLU_LIBRARY ${OSMESA_GLU_LIBRARY_FOUND}) - SET(OSMESA_INCLUDE_DIRECTORY $ENV{OSMESA_DIR}/include) + SET(OSMESA_INCLUDE_DIRECTORY ${WORKBENCH_MESA_DIR}/include) SET(OSMESA_FOUND TRUE) - MESSAGE("Offscreen Mesa Library was found") - MESSAGE(" Definition: ${OSMESA_DEFINITION}") - MESSAGE(" Include: ${OSMESA_INCLUDE_DIRECTORY}") - MESSAGE(" Libraries: ${OSMESA_OFFSCREEN_LIBRARY}") - MESSAGE(" Libraries: ${OSMESA_GL_LIBRARY}") - MESSAGE(" Libraries: ${OSMESA_GLU_LIBRARY}") + SET(OSMESA_RESULT ${OSMESA_OFFSCREEN_LIBRARY}) ENDIF (EXISTS ${OSMESA_LIBRARY_FOUND} AND EXISTS ${OSMESA_GL_LIBRARY_FOUND} AND EXISTS ${OSMESA_GLU_LIBRARY_FOUND}) - ENDIF (EXISTS $ENV{OSMESA_DIR}/include/GL/osmesa.h) -ENDIF (EXISTS $ENV{OSMESA_DIR}) - -#============================================================================= -MESSAGE("") -MESSAGE("Compiler: ${CMAKE_CXX_COMPILER}") -MESSAGE("Compiler Version: ${CMAKE_CXX_COMPILER_VERSION}") -MESSAGE("") -#============================================================================= - + ENDIF (EXISTS ${WORKBENCH_MESA_DIR}/include/GL/osmesa.h) +ENDIF (EXISTS ${WORKBENCH_MESA_DIR}) +########################################################################################## # # All subdirectories that will be configured for building # @@ -481,6 +516,7 @@ ADD_SUBDIRECTORY ( OperationsBase ) ADD_SUBDIRECTORY ( Algorithms ) ADD_SUBDIRECTORY ( Operations ) ADD_SUBDIRECTORY ( Brain ) +ADD_SUBDIRECTORY ( Graphics ) IF (NOT Qwt_FOUND) ADD_SUBDIRECTORY ( Qwt ) ENDIF (NOT Qwt_FOUND) @@ -494,7 +530,73 @@ if (WORKBENCH_USE_SIMD AND CPUINFO_COMPILES) ADD_SUBDIRECTORY ( kloewe/dot ) ENDIF() +########################################################################################## # -#CTest tests +# CTest tests # ENABLE_TESTING() + +########################################################################################## +# +# DEBUGGING ONLY +# +# Some find modules do not always document all of their variables. +# If you know the prefix for the find module's variables, +# this code can be used to print the variables by setting +# DEBUG_VARIABLE_PREFIX to the prefix. +# For example, set to "Q" to find all variables beginning from Qt +# or to "OSMESA" to print all OSMESA variables. +# +SET(DEBUG_VARIABLE_PREFIX "") +IF(NOT ("${DEBUG_VARIABLE_PREFIX}" STREQUAL "")) + getListOfVarsStartingWith(${DEBUG_VARIABLE_PREFIX} allVars) + foreach( debugVar IN LISTS allVars ) + message(${MSG_TYPE} ${MSG_INDENT} "${debugVar}=${${debugVar}}" ) + endforeach() +ENDIF() + +########################################################################################## +# +# Print Summary +# +IF(WB_CMAKE_VERBOSE_OUTPUT_FLAG) + SET(MSG_MODE "") #Optional + SET(MSG_INDENT " ") + MESSAGE(${MSG_MODE} "************* SUMMARY FROM RUNNING CMake ********************") + FIND_PROGRAM(QMAKE_PROGRAM "qmake" NAMES "qmake.exe") + + MESSAGE(${MSG_MODE} "TOOLS:") + MESSAGE(${MSG_MODE} ${MSG_INDENT} "CMake Build Type: ${CMAKE_BUILD_TYPE}") + MESSAGE(${MSG_MODE} ${MSG_INDENT} "CMake Version: ${CMAKE_VERSION}") + MESSAGE(${MSG_MODE} ${MSG_INDENT} "CXX Compiler ${CMAKE_CXX_COMPILER_ID}") + MESSAGE(${MSG_MODE} ${MSG_INDENT} "CXX Compiler Version: ${CMAKE_CXX_COMPILER_VERSION}") + MESSAGE(${MSG_MODE} ${MSG_INDENT} "CXX Compiler Flags: ${CMAKE_CXX_FLAGS}") + MESSAGE(${MSG_MODE} ${MSG_INDENT} "CXX Compiler Path: ${CMAKE_CXX_COMPILER}") + MESSAGE(${MSG_MODE} ${MSG_INDENT} "QMake Program: ${QMAKE_PROGRAM}") + + MESSAGE(${MSG_MODE} "WORKBENCH VARIABLES:") + getListOfVarsStartingWith("WORKBENCH" allVars) + foreach( var IN LISTS allVars ) + get_property(docString CACHE ${var} PROPERTY HELPSTRING) + message(${MSG_MODE} ${MSG_INDENT} "${var}=${${var}}" ) + + STRING(REPLACE "" "\n${MSG_INDENT}${MSG_INDENT}" docPrint ${docString}) + message(${MSG_MODE} ${MSG_INDENT} ${MSG_INDENT} ${docPrint}) + endforeach() + + MESSAGE(${MSG_MODE} "PACKAGES:") + MESSAGE(${MSG_MODE} ${MSG_INDENT} "FTGL: " ${FTGL_RESULT}) + MESSAGE(${MSG_MODE} ${MSG_INDENT} "FreeType: " ${FREETYPE_RESULT}) + MESSAGE(${MSG_MODE} ${MSG_INDENT} "GLEW: " ${GLEW_RESULT}) + MESSAGE(${MSG_MODE} ${MSG_INDENT} "OpenGL: " ${OPENGL_RESULT}) + MESSAGE(${MSG_MODE} ${MSG_INDENT} "OpenMP: " ${OPENMP_RESULT}) + MESSAGE(${MSG_MODE} ${MSG_INDENT} "OpenSSL: " ${OPENSSL_RESULT}) + MESSAGE(${MSG_MODE} ${MSG_INDENT} "OSMESA: " ${OSMESA_RESULT}) + MESSAGE(${MSG_MODE} ${MSG_INDENT} "Qt: " ${QT_RESULT}) + MESSAGE(${MSG_MODE} ${MSG_INDENT} "QuaZip: " ${QUAZIP_RESULT}) + MESSAGE(${MSG_MODE} ${MSG_INDENT} "Qwt: " ${QWT_RESULT}) + MESSAGE(${MSG_MODE} ${MSG_INDENT} "SIMD: " ${SIMD_RESULT}) + MESSAGE(${MSG_MODE} ${MSG_INDENT} "ZLIB: " ${ZLIB_RESULT}) + + MESSAGE(${MSG_MODE} "************* END SUMMARY FROM RUNNING CMake *****************") +ENDIF(WB_CMAKE_VERBOSE_OUTPUT_FLAG) diff --git a/src/Charting/CMakeLists.txt b/src/Charting/CMakeLists.txt index 253393b354dd062ef6f0cc27826eb31572d070fb..590b4d2b2af6d6a6884cd7f9693c343a59170cf6 100644 --- a/src/Charting/CMakeLists.txt +++ b/src/Charting/CMakeLists.txt @@ -4,6 +4,13 @@ # PROJECT(Charting) +# +# Qt include files +# +if(Qt5_FOUND) + include_directories(${Qt5Core_INCLUDE_DIRS}) +endif() + # # Create a library # @@ -17,7 +24,6 @@ ChartData.h ChartDataCartesian.h ChartDataSource.h ChartDataSourceModeEnum.h -ChartDataTypeEnum.h ChartMatrixDisplayProperties.h ChartMatrixLoadingDimensionEnum.h ChartMatrixScaleModeEnum.h @@ -29,7 +35,29 @@ ChartModelTimeSeries.h ChartPoint.h ChartScaleAutoRanging.h ChartSelectionModeEnum.h +ChartOneDataTypeEnum.h +ChartingVersionEnum.h +ChartTwoAxisScaleRangeModeEnum.h +ChartTwoCartesianAxis.h +ChartTwoCompoundDataType.h +ChartTwoDataCartesian.h +ChartTwoDataTypeEnum.h +ChartTwoHistogramContentTypeEnum.h +ChartTwoLineSeriesContentTypeEnum.h +ChartTwoMatrixContentTypeEnum.h +ChartTwoMatrixDisplayProperties.h +ChartTwoMatrixLoadingDimensionEnum.h +ChartTwoMatrixTriangularViewingModeEnum.h +ChartTwoNumericSubdivisionsModeEnum.h +ChartTwoLineSeriesHistory.h +ChartTwoTitle.h +EventChartTwoAttributesChanged.h +EventChartTwoAxisGetDataRange.h +EventChartTwoLoadLineSeriesData.h +MapFileDataSelector.h + +ChartTwoAxisScaleRangeModeEnum.cxx ChartAxis.cxx ChartAxisCartesian.cxx ChartAxisLocationEnum.cxx @@ -39,7 +67,6 @@ ChartData.cxx ChartDataCartesian.cxx ChartDataSource.cxx ChartDataSourceModeEnum.cxx -ChartDataTypeEnum.cxx ChartMatrixDisplayProperties.cxx ChartMatrixLoadingDimensionEnum.cxx ChartMatrixScaleModeEnum.cxx @@ -51,8 +78,30 @@ ChartModelTimeSeries.cxx ChartPoint.cxx ChartScaleAutoRanging.cxx ChartSelectionModeEnum.cxx +ChartOneDataTypeEnum.cxx +ChartingVersionEnum.cxx + +ChartTwoCartesianAxis.cxx +ChartTwoCompoundDataType.cxx +ChartTwoDataCartesian.cxx +ChartTwoDataTypeEnum.cxx +ChartTwoHistogramContentTypeEnum.cxx +ChartTwoLineSeriesContentTypeEnum.cxx +ChartTwoMatrixContentTypeEnum.cxx +ChartTwoMatrixDisplayProperties.cxx +ChartTwoMatrixLoadingDimensionEnum.cxx +ChartTwoMatrixTriangularViewingModeEnum.cxx +ChartTwoNumericSubdivisionsModeEnum.cxx +ChartTwoLineSeriesHistory.cxx +ChartTwoTitle.cxx +EventChartTwoAttributesChanged.cxx +EventChartTwoAxisGetDataRange.cxx +EventChartTwoLoadLineSeriesData.cxx +MapFileDataSelector.cxx ) +TARGET_LINK_LIBRARIES(Charting ${CARET_QT5_LINK}) + # # Include directories # @@ -60,6 +109,7 @@ INCLUDE_DIRECTORIES( ${CMAKE_SOURCE_DIR}/Annotations ${CMAKE_SOURCE_DIR}/Charting ${CMAKE_SOURCE_DIR}/Common +${CMAKE_SOURCE_DIR}/Graphics ${CMAKE_SOURCE_DIR}/Scenes ) diff --git a/src/Charting/ChartAxisLocationEnum.cxx b/src/Charting/ChartAxisLocationEnum.cxx index 0de4a227e0cda0ca3762c5f122428a358539eb87..abd51637abd88db4fb8bad8a1bf9f5e250cf8b76 100644 --- a/src/Charting/ChartAxisLocationEnum.cxx +++ b/src/Charting/ChartAxisLocationEnum.cxx @@ -379,3 +379,33 @@ ChartAxisLocationEnum::getAllGuiNames(std::vector& allGuiNames, const b } } +/** + * Get the opposite axis for the given axis (left <--> right; top <--> bottom). + * + * @param axis + * The axis. + * @return + * Opposite axis. + */ +ChartAxisLocationEnum::Enum +ChartAxisLocationEnum::getOppositeAxis(const ChartAxisLocationEnum::Enum axis) +{ + ChartAxisLocationEnum::Enum oppositeAxis = ChartAxisLocationEnum::CHART_AXIS_LOCATION_BOTTOM; + switch (axis) { + case ChartAxisLocationEnum::CHART_AXIS_LOCATION_BOTTOM: + oppositeAxis = ChartAxisLocationEnum::CHART_AXIS_LOCATION_TOP; + break; + case ChartAxisLocationEnum::CHART_AXIS_LOCATION_LEFT: + oppositeAxis = ChartAxisLocationEnum::CHART_AXIS_LOCATION_RIGHT; + break; + case ChartAxisLocationEnum::CHART_AXIS_LOCATION_RIGHT: + oppositeAxis = ChartAxisLocationEnum::CHART_AXIS_LOCATION_LEFT; + break; + case ChartAxisLocationEnum::CHART_AXIS_LOCATION_TOP: + oppositeAxis = ChartAxisLocationEnum::CHART_AXIS_LOCATION_BOTTOM; + break; + } + return oppositeAxis; +} + + diff --git a/src/Charting/ChartAxisLocationEnum.h b/src/Charting/ChartAxisLocationEnum.h index f6c1b0673920f2e51806341e560e388d32642c64..f565c173775d341d1be2b2c0db5cf46e76684627 100644 --- a/src/Charting/ChartAxisLocationEnum.h +++ b/src/Charting/ChartAxisLocationEnum.h @@ -66,6 +66,8 @@ public: static void getAllGuiNames(std::vector& allGuiNames, const bool isSorted); + static ChartAxisLocationEnum::Enum getOppositeAxis(const ChartAxisLocationEnum::Enum axis); + private: ChartAxisLocationEnum(const Enum enumValue, const AString& name, diff --git a/src/Charting/ChartData.cxx b/src/Charting/ChartData.cxx index 98b37278dae4412804bf9060227c3ca1bed4ed0f..4f2281e6abff0fbd6ecae8975ebb726747540642 100644 --- a/src/Charting/ChartData.cxx +++ b/src/Charting/ChartData.cxx @@ -46,7 +46,7 @@ using namespace caret; * @param chartDataType * Type of chart model. */ -ChartData::ChartData(const ChartDataTypeEnum::Enum chartDataType) +ChartData::ChartData(const ChartOneDataTypeEnum::Enum chartDataType) : CaretObject(), SceneableInterface(), m_chartDataType(chartDataType) @@ -143,30 +143,30 @@ ChartData::copyHelperChartData(const ChartData& obj) * Pointer to new instance. */ ChartData* -ChartData::newChartDataForChartDataType(const ChartDataTypeEnum::Enum chartDataType) +ChartData::newChartDataForChartDataType(const ChartOneDataTypeEnum::Enum chartDataType) { ChartData* chartData = NULL; switch (chartDataType) { - case ChartDataTypeEnum::CHART_DATA_TYPE_INVALID: + case ChartOneDataTypeEnum::CHART_DATA_TYPE_INVALID: CaretAssert(0); break; - case ChartDataTypeEnum::CHART_DATA_TYPE_MATRIX_LAYER: + case ChartOneDataTypeEnum::CHART_DATA_TYPE_MATRIX_LAYER: CaretAssert(0); break; - case ChartDataTypeEnum::CHART_DATA_TYPE_MATRIX_SERIES: + case ChartOneDataTypeEnum::CHART_DATA_TYPE_MATRIX_SERIES: break; - case ChartDataTypeEnum::CHART_DATA_TYPE_LINE_DATA_SERIES: + case ChartOneDataTypeEnum::CHART_DATA_TYPE_LINE_DATA_SERIES: chartData = new ChartDataCartesian(chartDataType, ChartAxisUnitsEnum::CHART_AXIS_UNITS_NONE, ChartAxisUnitsEnum::CHART_AXIS_UNITS_NONE); break; - case ChartDataTypeEnum::CHART_DATA_TYPE_LINE_FREQUENCY_SERIES: + case ChartOneDataTypeEnum::CHART_DATA_TYPE_LINE_FREQUENCY_SERIES: chartData = new ChartDataCartesian(chartDataType, ChartAxisUnitsEnum::CHART_AXIS_UNITS_NONE, ChartAxisUnitsEnum::CHART_AXIS_UNITS_TIME_SECONDS); break; - case ChartDataTypeEnum::CHART_DATA_TYPE_LINE_TIME_SERIES: + case ChartOneDataTypeEnum::CHART_DATA_TYPE_LINE_TIME_SERIES: chartData = new ChartDataCartesian(chartDataType, ChartAxisUnitsEnum::CHART_AXIS_UNITS_NONE, ChartAxisUnitsEnum::CHART_AXIS_UNITS_TIME_SECONDS); @@ -233,7 +233,7 @@ ChartData::restoreFromScene(const SceneAttributes* sceneAttributes, /** * @return The chart data model type. */ -ChartDataTypeEnum::Enum +ChartOneDataTypeEnum::Enum ChartData::getChartDataType() const { return m_chartDataType; diff --git a/src/Charting/ChartData.h b/src/Charting/ChartData.h index 8598ca6ec6d59cb9b20c4c4a3921a8d57ec8524f..25fc3ac355846cc87b7eaf3ba23804ccace443bc 100644 --- a/src/Charting/ChartData.h +++ b/src/Charting/ChartData.h @@ -23,7 +23,7 @@ #include "BrainConstants.h" #include "CaretObject.h" -#include "ChartDataTypeEnum.h" +#include "ChartOneDataTypeEnum.h" #include "SceneableInterface.h" @@ -35,7 +35,7 @@ namespace caret { class ChartData : public CaretObject, public SceneableInterface { public: - static ChartData* newChartDataForChartDataType(const ChartDataTypeEnum::Enum chartDataType); + static ChartData* newChartDataForChartDataType(const ChartOneDataTypeEnum::Enum chartDataType); virtual ~ChartData(); @@ -50,7 +50,7 @@ namespace caret { */ virtual ChartData* clone() const = 0; - ChartDataTypeEnum::Enum getChartDataType() const; + ChartOneDataTypeEnum::Enum getChartDataType() const; const ChartDataSource* getChartDataSource() const; @@ -80,7 +80,7 @@ namespace caret { virtual AString toString() const; protected: - ChartData(const ChartDataTypeEnum::Enum chartDataType); + ChartData(const ChartOneDataTypeEnum::Enum chartDataType); ChartData(const ChartData& obj); @@ -124,7 +124,7 @@ namespace caret { SceneClassAssistant* m_sceneAssistant; - ChartDataTypeEnum::Enum m_chartDataType; + ChartOneDataTypeEnum::Enum m_chartDataType; ChartDataSource* m_chartDataSource; diff --git a/src/Charting/ChartDataCartesian.cxx b/src/Charting/ChartDataCartesian.cxx index 744576fdf7903083df2aeff421e99cc4f2779a56..f18338d3beb7e1cc98ccba2abaa220e20972f43c 100644 --- a/src/Charting/ChartDataCartesian.cxx +++ b/src/Charting/ChartDataCartesian.cxx @@ -52,7 +52,7 @@ using namespace caret; * @param dataAxisUnitsY * Data units for Y-axis. */ -ChartDataCartesian::ChartDataCartesian(const ChartDataTypeEnum::Enum chartDataType, +ChartDataCartesian::ChartDataCartesian(const ChartOneDataTypeEnum::Enum chartDataType, const ChartAxisUnitsEnum::Enum dataAxisUnitsX, const ChartAxisUnitsEnum::Enum dataAxisUnitsY) : ChartData(chartDataType), diff --git a/src/Charting/ChartDataCartesian.h b/src/Charting/ChartDataCartesian.h index aa29c37813048111a223c91834df9ed0acbe0c76..f61ff9f8e0c7c20b72a88af3fa05cd6f3469ffdc 100644 --- a/src/Charting/ChartDataCartesian.h +++ b/src/Charting/ChartDataCartesian.h @@ -34,7 +34,7 @@ namespace caret { class ChartDataCartesian : public ChartData { public: - ChartDataCartesian(const ChartDataTypeEnum::Enum chartDataType, + ChartDataCartesian(const ChartOneDataTypeEnum::Enum chartDataType, const ChartAxisUnitsEnum::Enum dataAxisUnitsX, const ChartAxisUnitsEnum::Enum dataAxisUnitsY); diff --git a/src/Charting/ChartDataSource.cxx b/src/Charting/ChartDataSource.cxx index a399e6a6fa5018d21c2e2c9854c13c572d4e5ab2..c2f7014c85feacf87b15d16540a061573a10fbd3 100644 --- a/src/Charting/ChartDataSource.cxx +++ b/src/Charting/ChartDataSource.cxx @@ -120,6 +120,137 @@ ChartDataSource::operator=(const ChartDataSource& obj) return *this; } +/** + * Equality operator. + * + * @param rhs + * Other item for comparison. + */ +bool +ChartDataSource::operator==(const ChartDataSource& rhs) const +{ + if (this == &rhs) { + return true; + } + + if (m_chartableFileName != rhs.m_chartableFileName) { + return false; + } + + switch (m_dataSourceMode) { + case ChartDataSourceModeEnum::CHART_DATA_SOURCE_MODE_FILE_ROW: + if (m_fileRowIndex == rhs.m_fileRowIndex) { + return true; + } + break; + case ChartDataSourceModeEnum::CHART_DATA_SOURCE_MODE_INVALID: + break; + case ChartDataSourceModeEnum::CHART_DATA_SOURCE_MODE_SURFACE_NODE_INDEX: + if ((m_surfaceStructureName == rhs.m_surfaceStructureName) + && (m_surfaceNumberOfNodes == rhs.m_surfaceNumberOfNodes) + && (m_nodeIndex == rhs.m_nodeIndex)) { + return true; + } + break; + case ChartDataSourceModeEnum::CHART_DATA_SOURCE_MODE_SURFACE_NODE_INDICES_AVERAGE: + if ((m_surfaceStructureName == rhs.m_surfaceStructureName) + && (m_surfaceNumberOfNodes == rhs.m_surfaceNumberOfNodes) + && (m_nodeIndicesAverage == rhs.m_nodeIndicesAverage)) { + return true; + } + case ChartDataSourceModeEnum::CHART_DATA_SOURCE_MODE_VOXEL_IJK: + if ((m_voxelXYZ[0] == rhs.m_voxelXYZ[0]) + && (m_voxelXYZ[1] == rhs.m_voxelXYZ[1]) + && (m_voxelXYZ[2] == rhs.m_voxelXYZ[2])) { + return true; + } + break; + } + + return false; +} + +/** + * Equality operator. + * + * @param rhs + * Other item for comparison. + */ +bool +ChartDataSource::operator<(const ChartDataSource& rhs) const +{ + if (this == &rhs) { + return false; + } + + if (m_chartableFileName != rhs.m_chartableFileName) { + return false; + } + + switch (m_dataSourceMode) { + case ChartDataSourceModeEnum::CHART_DATA_SOURCE_MODE_FILE_ROW: + return (m_fileRowIndex < rhs.m_fileRowIndex); + break; + case ChartDataSourceModeEnum::CHART_DATA_SOURCE_MODE_INVALID: + break; + case ChartDataSourceModeEnum::CHART_DATA_SOURCE_MODE_SURFACE_NODE_INDEX: + if (m_surfaceStructureName < rhs.m_surfaceStructureName) { + return true; + } + if (m_surfaceNumberOfNodes < rhs.m_surfaceNumberOfNodes) { + return true; + } + if (m_nodeIndex < rhs.m_nodeIndex) { + return true; + } + break; + case ChartDataSourceModeEnum::CHART_DATA_SOURCE_MODE_SURFACE_NODE_INDICES_AVERAGE: + if (m_surfaceStructureName < rhs.m_surfaceStructureName) { + return true; + } + if (m_surfaceNumberOfNodes < rhs.m_surfaceNumberOfNodes) { + return true; + } + if (m_nodeIndicesAverage.size() < rhs.m_nodeIndicesAverage.size()) { + return true; + } + else if (m_nodeIndicesAverage.size() > rhs.m_nodeIndicesAverage.size()) { + return false; + } + else if (m_nodeIndicesAverage.size() == rhs.m_nodeIndicesAverage.size()) { + std::vector copyMe = m_nodeIndicesAverage; + std::sort(copyMe.begin(), copyMe.end()); + std::vector copyRhs = rhs.m_nodeIndicesAverage; + std::sort(copyRhs.begin(), copyRhs.end()); + + const int32_t numItems = static_cast(copyMe.size()); + for (int32_t i = 0; i < numItems; i++) { + CaretAssertVectorIndex(copyMe, i); + CaretAssertVectorIndex(copyRhs, i); + if (copyMe[i] < copyRhs[i]) { + return true; + } + else if (copyMe[i] > copyRhs[i]) { + return false; + } + } + } + case ChartDataSourceModeEnum::CHART_DATA_SOURCE_MODE_VOXEL_IJK: + if (m_voxelXYZ[0] < rhs.m_voxelXYZ[0]) { + return true; + } + if (m_voxelXYZ[1] < rhs.m_voxelXYZ[1]) { + return true; + } + if (m_voxelXYZ[2] < rhs.m_voxelXYZ[2]) { + return true; + } + break; + } + + return false; +} + /** * Copy the given data source to me. * diff --git a/src/Charting/ChartDataSource.h b/src/Charting/ChartDataSource.h index 8370d2c627ca29823fc7d2cedbe16da57db1f3ba..dc8cdbc427b3b25b9f5ba8ac7d170f885da16f6d 100644 --- a/src/Charting/ChartDataSource.h +++ b/src/Charting/ChartDataSource.h @@ -41,6 +41,10 @@ namespace caret { ChartDataSource& operator=(const ChartDataSource&); + bool operator==(const ChartDataSource&) const; + + bool operator<(const ChartDataSource&) const; + void copy(const ChartDataSource* copyFrom); virtual SceneClass* saveToScene(const SceneAttributes* sceneAttributes, diff --git a/src/Charting/ChartModel.cxx b/src/Charting/ChartModel.cxx index c460aeed93ab066e5a01c586d72bb0f22ad360fc..517b3ed1c6bb1216d97e818ef9650a3a9541119d 100644 --- a/src/Charting/ChartModel.cxx +++ b/src/Charting/ChartModel.cxx @@ -55,7 +55,7 @@ using namespace caret; * @param chartSelectionMode * The selection mode. */ -ChartModel::ChartModel(const ChartDataTypeEnum::Enum chartDataType, +ChartModel::ChartModel(const ChartOneDataTypeEnum::Enum chartDataType, const ChartSelectionModeEnum::Enum chartSelectionMode) : CaretObject(), SceneableInterface(), @@ -244,7 +244,7 @@ ChartModel::copyHelperChartModel(const ChartModel& obj) /** * @return The chart data type. */ -ChartDataTypeEnum::Enum +ChartOneDataTypeEnum::Enum ChartModel::getChartDataType() const { return m_chartDataType; diff --git a/src/Charting/ChartModel.h b/src/Charting/ChartModel.h index 80b611ce51d4c0ddf7e7601361a824669e89c0c1..5e2bdb362a5e2bb7ab3fdc62026ffd6e35f5347a 100644 --- a/src/Charting/ChartModel.h +++ b/src/Charting/ChartModel.h @@ -26,7 +26,7 @@ #include #include "CaretObject.h" -#include "ChartDataTypeEnum.h" +#include "ChartOneDataTypeEnum.h" #include "ChartSelectionModeEnum.h" #include "SceneableInterface.h" @@ -40,7 +40,7 @@ namespace caret { class ChartModel : public CaretObject, public SceneableInterface { public: - ChartModel(const ChartDataTypeEnum::Enum chartDataType, + ChartModel(const ChartOneDataTypeEnum::Enum chartDataType, const ChartSelectionModeEnum::Enum chartSelectionMode); ChartModel(const ChartModel&); @@ -51,7 +51,7 @@ namespace caret { void removeChartData(); - ChartDataTypeEnum::Enum getChartDataType() const; + ChartOneDataTypeEnum::Enum getChartDataType() const; ChartSelectionModeEnum::Enum getChartSelectionMode() const; @@ -160,7 +160,7 @@ namespace caret { void updateUsingMaximumNumberOfChartDatasToDisplay(); - ChartDataTypeEnum::Enum m_chartDataType; + ChartOneDataTypeEnum::Enum m_chartDataType; ChartSelectionModeEnum::Enum m_chartSelectionMode; diff --git a/src/Charting/ChartModelCartesian.cxx b/src/Charting/ChartModelCartesian.cxx index 31e05ca3cbba35222cfea2509a22b04a8b6a630d..7ec8d011e60fc2bf5baa4fd3b304a67b3374c073 100644 --- a/src/Charting/ChartModelCartesian.cxx +++ b/src/Charting/ChartModelCartesian.cxx @@ -54,7 +54,7 @@ using namespace caret; * @param dataAxisUnitsY * Units for the Y-axis. */ -ChartModelCartesian::ChartModelCartesian(const ChartDataTypeEnum::Enum chartDataType, +ChartModelCartesian::ChartModelCartesian(const ChartOneDataTypeEnum::Enum chartDataType, const ChartAxisUnitsEnum::Enum dataAxisUnitsX, const ChartAxisUnitsEnum::Enum dataAxisUnitsY) : ChartModel(chartDataType, @@ -170,7 +170,7 @@ ChartModelCartesian::getAverageChartDataForDisplay(const int32_t tabIndex) const std::vector xValue; std::vector ySum; int64_t averageCounter = 0; - ChartDataTypeEnum::Enum firstChartDataType = ChartDataTypeEnum::CHART_DATA_TYPE_INVALID; + ChartOneDataTypeEnum::Enum firstChartDataType = ChartOneDataTypeEnum::CHART_DATA_TYPE_INVALID; bool firstFlag = true; for (std::vector::const_iterator iter = allData.begin(); diff --git a/src/Charting/ChartModelCartesian.h b/src/Charting/ChartModelCartesian.h index 96625f86c5843c5f99817218a05e153fa04422dd..6d2b3a13677067150221479286ba94e6aac59ed2 100644 --- a/src/Charting/ChartModelCartesian.h +++ b/src/Charting/ChartModelCartesian.h @@ -32,7 +32,7 @@ namespace caret { class ChartModelCartesian : public ChartModel { public: - ChartModelCartesian(const ChartDataTypeEnum::Enum chartDataType, + ChartModelCartesian(const ChartOneDataTypeEnum::Enum chartDataType, const ChartAxisUnitsEnum::Enum dataAxisUnitsX, const ChartAxisUnitsEnum::Enum dataAxisUnitsY); diff --git a/src/Charting/ChartModelDataSeries.cxx b/src/Charting/ChartModelDataSeries.cxx index c39c555ed8f5d58cd156367f4087285b57b0f70b..7c4774bf5978776a81a30f638f5885015791a249 100644 --- a/src/Charting/ChartModelDataSeries.cxx +++ b/src/Charting/ChartModelDataSeries.cxx @@ -38,7 +38,7 @@ using namespace caret; * Constructor. */ ChartModelDataSeries::ChartModelDataSeries() -: ChartModelCartesian(ChartDataTypeEnum::CHART_DATA_TYPE_LINE_DATA_SERIES, +: ChartModelCartesian(ChartOneDataTypeEnum::CHART_DATA_TYPE_LINE_DATA_SERIES, ChartAxisUnitsEnum::CHART_AXIS_UNITS_NONE, ChartAxisUnitsEnum::CHART_AXIS_UNITS_NONE) { diff --git a/src/Charting/ChartModelFrequencySeries.cxx b/src/Charting/ChartModelFrequencySeries.cxx index 27b1e2b58255a629cb6fc4cea0c56d000ac57e45..8532548fb15bbaae67c0cf29d95d118fbc739192 100644 --- a/src/Charting/ChartModelFrequencySeries.cxx +++ b/src/Charting/ChartModelFrequencySeries.cxx @@ -38,7 +38,7 @@ using namespace caret; * Constructor. */ ChartModelFrequencySeries::ChartModelFrequencySeries() -: ChartModelCartesian(ChartDataTypeEnum::CHART_DATA_TYPE_LINE_FREQUENCY_SERIES, +: ChartModelCartesian(ChartOneDataTypeEnum::CHART_DATA_TYPE_LINE_FREQUENCY_SERIES, ChartAxisUnitsEnum::CHART_AXIS_UNITS_FREQUENCY_HERTZ, ChartAxisUnitsEnum::CHART_AXIS_UNITS_NONE) { diff --git a/src/Charting/ChartModelTimeSeries.cxx b/src/Charting/ChartModelTimeSeries.cxx index 435055b94003aad79f7a1ef783e6b9cdc38238f5..2f16104e4bc3cc27fc731808a3442b33d8efcd3d 100644 --- a/src/Charting/ChartModelTimeSeries.cxx +++ b/src/Charting/ChartModelTimeSeries.cxx @@ -38,7 +38,7 @@ using namespace caret; * Constructor. */ ChartModelTimeSeries::ChartModelTimeSeries() -: ChartModelCartesian(ChartDataTypeEnum::CHART_DATA_TYPE_LINE_TIME_SERIES, +: ChartModelCartesian(ChartOneDataTypeEnum::CHART_DATA_TYPE_LINE_TIME_SERIES, ChartAxisUnitsEnum::CHART_AXIS_UNITS_TIME_SECONDS, ChartAxisUnitsEnum::CHART_AXIS_UNITS_NONE) { diff --git a/src/Charting/ChartDataTypeEnum.cxx b/src/Charting/ChartOneDataTypeEnum.cxx similarity index 71% rename from src/Charting/ChartDataTypeEnum.cxx rename to src/Charting/ChartOneDataTypeEnum.cxx index 48faa16af3e5e9e3cee4c2649243f0e1a31b9a03..8a178118553a5107dad4cf0c3bed97f90ee2a2e3 100644 --- a/src/Charting/ChartDataTypeEnum.cxx +++ b/src/Charting/ChartOneDataTypeEnum.cxx @@ -20,9 +20,9 @@ /*LICENSE_END*/ #include -#define __CHART_DATA_TYPE_ENUM_DECLARE__ -#include "ChartDataTypeEnum.h" -#undef __CHART_DATA_TYPE_ENUM_DECLARE__ +#define __CHART_VERSION_ONE_DATA_TYPE_ENUM_DECLARE__ +#include "ChartOneDataTypeEnum.h" +#undef __CHART_VERSION_ONE_DATA_TYPE_ENUM_DECLARE__ #include "CaretAssert.h" @@ -30,8 +30,8 @@ using namespace caret; /** - * \class caret::ChartDataTypeEnum - * \brief Enumerated type for type of chart data. + * \class caret::ChartOneDataTypeEnum + * \brief Enumerated type for type of VERSION ONE chart data. * * Using this enumerated type in the GUI with an EnumComboBoxTemplate * @@ -49,21 +49,21 @@ using namespace caret; * Implementation File (.cxx) * Include the header files * #include "EnumComboBoxTemplate.h" - * #include "ChartDataTypeEnum.h" + * #include "ChartOneDataTypeEnum.h" * * Instatiate: * m_chartDataTypeEnumComboBox = new EnumComboBoxTemplate(this); - * m_chartDataTypeEnumComboBox->setup(); + * m_chartDataTypeEnumComboBox->setup(); * * Get notified when the user changes the selection: * QObject::connect(m_chartDataTypeEnumComboBox, SIGNAL(itemActivated()), * this, SLOT(chartDataTypeEnumComboBoxItemActivated())); * * Update the selection: - * m_chartDataTypeEnumComboBox->setSelectedItem(NEW_VALUE); + * m_chartDataTypeEnumComboBox->setSelectedItem(NEW_VALUE); * * Read the selection: - * const ChartDataTypeEnum::Enum VARIABLE = m_chartDataTypeEnumComboBox->getSelectedItem(); + * const ChartOneDataTypeEnum::Enum VARIABLE = m_chartDataTypeEnumComboBox->getSelectedItem(); * */ @@ -78,7 +78,7 @@ using namespace caret; * @param guiName * User-friendly name for use in user-interface. */ -ChartDataTypeEnum::ChartDataTypeEnum(const Enum enumValue, +ChartOneDataTypeEnum::ChartOneDataTypeEnum(const Enum enumValue, const AString& name, const AString& guiName) { @@ -91,7 +91,7 @@ ChartDataTypeEnum::ChartDataTypeEnum(const Enum enumValue, /** * Destructor. */ -ChartDataTypeEnum::~ChartDataTypeEnum() +ChartOneDataTypeEnum::~ChartOneDataTypeEnum() { } @@ -99,34 +99,34 @@ ChartDataTypeEnum::~ChartDataTypeEnum() * Initialize the enumerated metadata. */ void -ChartDataTypeEnum::initialize() +ChartOneDataTypeEnum::initialize() { if (initializedFlag) { return; } initializedFlag = true; - enumData.push_back(ChartDataTypeEnum(CHART_DATA_TYPE_INVALID, + enumData.push_back(ChartOneDataTypeEnum(CHART_DATA_TYPE_INVALID, "CHART_DATA_TYPE_INVALID", "Invalid")); - enumData.push_back(ChartDataTypeEnum(CHART_DATA_TYPE_LINE_DATA_SERIES, + enumData.push_back(ChartOneDataTypeEnum(CHART_DATA_TYPE_LINE_DATA_SERIES, "CHART_DATA_TYPE_LINE_DATA_SERIES", "Data Series")); - enumData.push_back(ChartDataTypeEnum(CHART_DATA_TYPE_LINE_FREQUENCY_SERIES, + enumData.push_back(ChartOneDataTypeEnum(CHART_DATA_TYPE_LINE_FREQUENCY_SERIES, "CHART_DATA_TYPE_LINE_FREQUENCY_SERIES", "Frequency Series")); - enumData.push_back(ChartDataTypeEnum(CHART_DATA_TYPE_LINE_TIME_SERIES, + enumData.push_back(ChartOneDataTypeEnum(CHART_DATA_TYPE_LINE_TIME_SERIES, "CHART_DATA_TYPE_LINE_TIME_SERIES", "Time Series")); - enumData.push_back(ChartDataTypeEnum(CHART_DATA_TYPE_MATRIX_LAYER, + enumData.push_back(ChartOneDataTypeEnum(CHART_DATA_TYPE_MATRIX_LAYER, "CHART_DATA_TYPE_MATRIX_LAYER", "Matrix - Layer")); - enumData.push_back(ChartDataTypeEnum(CHART_DATA_TYPE_MATRIX_SERIES, + enumData.push_back(ChartOneDataTypeEnum(CHART_DATA_TYPE_MATRIX_SERIES, "CHART_DATA_TYPE_MATRIX_SERIES", "Matrix - Series")); } @@ -138,14 +138,14 @@ ChartDataTypeEnum::initialize() * @return Pointer to data for this enumerated type * or NULL if no data for type or if type is invalid. */ -const ChartDataTypeEnum* -ChartDataTypeEnum::findData(const Enum enumValue) +const ChartOneDataTypeEnum* +ChartOneDataTypeEnum::findData(const Enum enumValue) { if (initializedFlag == false) initialize(); size_t num = enumData.size(); for (size_t i = 0; i < num; i++) { - const ChartDataTypeEnum* d = &enumData[i]; + const ChartOneDataTypeEnum* d = &enumData[i]; if (d->enumValue == enumValue) { return d; } @@ -162,10 +162,10 @@ ChartDataTypeEnum::findData(const Enum enumValue) * String representing enumerated value. */ AString -ChartDataTypeEnum::toName(Enum enumValue) { +ChartOneDataTypeEnum::toName(Enum enumValue) { if (initializedFlag == false) initialize(); - const ChartDataTypeEnum* enumInstance = findData(enumValue); + const ChartOneDataTypeEnum* enumInstance = findData(enumValue); return enumInstance->name; } @@ -179,8 +179,8 @@ ChartDataTypeEnum::toName(Enum enumValue) { * @return * Enumerated value. */ -ChartDataTypeEnum::Enum -ChartDataTypeEnum::fromName(const AString& nameIn, bool* isValidOut) +ChartOneDataTypeEnum::Enum +ChartOneDataTypeEnum::fromName(const AString& nameIn, bool* isValidOut) { if (initializedFlag == false) initialize(); @@ -199,12 +199,12 @@ ChartDataTypeEnum::fromName(const AString& nameIn, bool* isValidOut) } bool validFlag = false; - Enum enumValue = ChartDataTypeEnum::enumData[0].enumValue; + Enum enumValue = ChartOneDataTypeEnum::enumData[0].enumValue; - for (std::vector::iterator iter = enumData.begin(); + for (std::vector::iterator iter = enumData.begin(); iter != enumData.end(); iter++) { - const ChartDataTypeEnum& d = *iter; + const ChartOneDataTypeEnum& d = *iter; if (d.name == name) { enumValue = d.enumValue; validFlag = true; @@ -216,7 +216,7 @@ ChartDataTypeEnum::fromName(const AString& nameIn, bool* isValidOut) *isValidOut = validFlag; } else if (validFlag == false) { - CaretAssertMessage(0, AString("Name " + name + "failed to match enumerated value for type ChartDataTypeEnum")); + CaretAssertMessage(0, AString("Name " + name + "failed to match enumerated value for type ChartOneDataTypeEnum")); } return enumValue; } @@ -229,10 +229,10 @@ ChartDataTypeEnum::fromName(const AString& nameIn, bool* isValidOut) * String representing enumerated value. */ AString -ChartDataTypeEnum::toGuiName(Enum enumValue) { +ChartOneDataTypeEnum::toGuiName(Enum enumValue) { if (initializedFlag == false) initialize(); - const ChartDataTypeEnum* enumInstance = findData(enumValue); + const ChartOneDataTypeEnum* enumInstance = findData(enumValue); return enumInstance->guiName; } @@ -246,8 +246,8 @@ ChartDataTypeEnum::toGuiName(Enum enumValue) { * @return * Enumerated value. */ -ChartDataTypeEnum::Enum -ChartDataTypeEnum::fromGuiName(const AString& guiNameIn, bool* isValidOut) +ChartOneDataTypeEnum::Enum +ChartOneDataTypeEnum::fromGuiName(const AString& guiNameIn, bool* isValidOut) { if (initializedFlag == false) initialize(); @@ -257,12 +257,12 @@ ChartDataTypeEnum::fromGuiName(const AString& guiNameIn, bool* isValidOut) } bool validFlag = false; - Enum enumValue = ChartDataTypeEnum::enumData[0].enumValue; + Enum enumValue = ChartOneDataTypeEnum::enumData[0].enumValue; - for (std::vector::iterator iter = enumData.begin(); + for (std::vector::iterator iter = enumData.begin(); iter != enumData.end(); iter++) { - const ChartDataTypeEnum& d = *iter; + const ChartOneDataTypeEnum& d = *iter; if (d.guiName == guiName) { enumValue = d.enumValue; validFlag = true; @@ -274,7 +274,7 @@ ChartDataTypeEnum::fromGuiName(const AString& guiNameIn, bool* isValidOut) *isValidOut = validFlag; } else if (validFlag == false) { - CaretAssertMessage(0, AString("guiName " + guiName + "failed to match enumerated value for type ChartDataTypeEnum")); + CaretAssertMessage(0, AString("guiName " + guiName + "failed to match enumerated value for type ChartOneDataTypeEnum")); } return enumValue; } @@ -286,10 +286,10 @@ ChartDataTypeEnum::fromGuiName(const AString& guiNameIn, bool* isValidOut) * Integer code for data type. */ int32_t -ChartDataTypeEnum::toIntegerCode(Enum enumValue) +ChartOneDataTypeEnum::toIntegerCode(Enum enumValue) { if (initializedFlag == false) initialize(); - const ChartDataTypeEnum* enumInstance = findData(enumValue); + const ChartOneDataTypeEnum* enumInstance = findData(enumValue); return enumInstance->integerCode; } @@ -304,18 +304,18 @@ ChartDataTypeEnum::toIntegerCode(Enum enumValue) * @return * Enum for integer code. */ -ChartDataTypeEnum::Enum -ChartDataTypeEnum::fromIntegerCode(const int32_t integerCode, bool* isValidOut) +ChartOneDataTypeEnum::Enum +ChartOneDataTypeEnum::fromIntegerCode(const int32_t integerCode, bool* isValidOut) { if (initializedFlag == false) initialize(); bool validFlag = false; - Enum enumValue = ChartDataTypeEnum::enumData[0].enumValue; + Enum enumValue = ChartOneDataTypeEnum::enumData[0].enumValue; - for (std::vector::iterator iter = enumData.begin(); + for (std::vector::iterator iter = enumData.begin(); iter != enumData.end(); iter++) { - const ChartDataTypeEnum& enumInstance = *iter; + const ChartOneDataTypeEnum& enumInstance = *iter; if (enumInstance.integerCode == integerCode) { enumValue = enumInstance.enumValue; validFlag = true; @@ -327,7 +327,7 @@ ChartDataTypeEnum::fromIntegerCode(const int32_t integerCode, bool* isValidOut) *isValidOut = validFlag; } else if (validFlag == false) { - CaretAssertMessage(0, AString("Integer code " + AString::number(integerCode) + "failed to match enumerated value for type ChartDataTypeEnum")); + CaretAssertMessage(0, AString("Integer code " + AString::number(integerCode) + "failed to match enumerated value for type ChartOneDataTypeEnum")); } return enumValue; } @@ -340,13 +340,13 @@ ChartDataTypeEnum::fromIntegerCode(const int32_t integerCode, bool* isValidOut) * A vector that is OUTPUT containing all of the enumerated values. */ void -ChartDataTypeEnum::getAllEnums(std::vector& allEnums) +ChartOneDataTypeEnum::getAllEnums(std::vector& allEnums) { if (initializedFlag == false) initialize(); allEnums.clear(); - for (std::vector::iterator iter = enumData.begin(); + for (std::vector::iterator iter = enumData.begin(); iter != enumData.end(); iter++) { allEnums.push_back(iter->enumValue); @@ -362,16 +362,16 @@ ChartDataTypeEnum::getAllEnums(std::vector& allEnums) * If true, the names are sorted in alphabetical order. */ void -ChartDataTypeEnum::getAllNames(std::vector& allNames, const bool isSorted) +ChartOneDataTypeEnum::getAllNames(std::vector& allNames, const bool isSorted) { if (initializedFlag == false) initialize(); allNames.clear(); - for (std::vector::iterator iter = enumData.begin(); + for (std::vector::iterator iter = enumData.begin(); iter != enumData.end(); iter++) { - allNames.push_back(ChartDataTypeEnum::toName(iter->enumValue)); + allNames.push_back(ChartOneDataTypeEnum::toName(iter->enumValue)); } if (isSorted) { @@ -388,16 +388,16 @@ ChartDataTypeEnum::getAllNames(std::vector& allNames, const bool isSort * If true, the names are sorted in alphabetical order. */ void -ChartDataTypeEnum::getAllGuiNames(std::vector& allGuiNames, const bool isSorted) +ChartOneDataTypeEnum::getAllGuiNames(std::vector& allGuiNames, const bool isSorted) { if (initializedFlag == false) initialize(); allGuiNames.clear(); - for (std::vector::iterator iter = enumData.begin(); + for (std::vector::iterator iter = enumData.begin(); iter != enumData.end(); iter++) { - allGuiNames.push_back(ChartDataTypeEnum::toGuiName(iter->enumValue)); + allGuiNames.push_back(ChartOneDataTypeEnum::toGuiName(iter->enumValue)); } if (isSorted) { diff --git a/src/Charting/ChartDataTypeEnum.h b/src/Charting/ChartOneDataTypeEnum.h similarity index 81% rename from src/Charting/ChartDataTypeEnum.h rename to src/Charting/ChartOneDataTypeEnum.h index 11e32bec964dd98ff46bc52bb89ffccfe56e5b82..7eb40fe04384754359cc3a221393bf22ead8b010 100644 --- a/src/Charting/ChartDataTypeEnum.h +++ b/src/Charting/ChartOneDataTypeEnum.h @@ -1,5 +1,5 @@ -#ifndef __CHART_DATA_TYPE_ENUM_H__ -#define __CHART_DATA_TYPE_ENUM_H__ +#ifndef __CHART_VERSION_ONE_DATA_TYPE_ENUM_H__ +#define __CHART_VERSION_ONE_DATA_TYPE_ENUM_H__ /*LICENSE_START*/ /* @@ -28,7 +28,7 @@ namespace caret { -class ChartDataTypeEnum { +class ChartOneDataTypeEnum { public: /** @@ -50,7 +50,7 @@ public: }; - ~ChartDataTypeEnum(); + ~ChartOneDataTypeEnum(); static AString toName(Enum enumValue); @@ -71,14 +71,14 @@ public: static void getAllGuiNames(std::vector& allGuiNames, const bool isSorted); private: - ChartDataTypeEnum(const Enum enumValue, + ChartOneDataTypeEnum(const Enum enumValue, const AString& name, const AString& guiName); - static const ChartDataTypeEnum* findData(const Enum enumValue); + static const ChartOneDataTypeEnum* findData(const Enum enumValue); /** Holds all instance of enum values and associated metadata */ - static std::vector enumData; + static std::vector enumData; /** Initialize instances that contain the enum values and metadata */ static void initialize(); @@ -102,11 +102,11 @@ private: AString guiName; }; -#ifdef __CHART_DATA_TYPE_ENUM_DECLARE__ -std::vector ChartDataTypeEnum::enumData; -bool ChartDataTypeEnum::initializedFlag = false; -int32_t ChartDataTypeEnum::integerCodeCounter = 0; -#endif // __CHART_DATA_TYPE_ENUM_DECLARE__ +#ifdef __CHART_VERSION_ONE_DATA_TYPE_ENUM_DECLARE__ +std::vector ChartOneDataTypeEnum::enumData; +bool ChartOneDataTypeEnum::initializedFlag = false; +int32_t ChartOneDataTypeEnum::integerCodeCounter = 0; +#endif // __CHART_VERSION_ONE_DATA_TYPE_ENUM_DECLARE__ } // namespace -#endif //__CHART_DATA_TYPE_ENUM_H__ +#endif //__CHART_VERSION_ONE_DATA_TYPE_ENUM_H__ diff --git a/src/Charting/ChartTwoAxisScaleRangeModeEnum.cxx b/src/Charting/ChartTwoAxisScaleRangeModeEnum.cxx new file mode 100644 index 0000000000000000000000000000000000000000..bcd52ab519abf433759b53e15e66e3f69d151e77 --- /dev/null +++ b/src/Charting/ChartTwoAxisScaleRangeModeEnum.cxx @@ -0,0 +1,385 @@ + +/*LICENSE_START*/ +/* + * Copyright (C) 2017 Washington University School of Medicine + * + * 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 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ +/*LICENSE_END*/ + +#include +#define __CHART_TWO_AXIS_SCALE_RANGE_MODE_ENUM_DECLARE__ +#include "ChartTwoAxisScaleRangeModeEnum.h" +#undef __CHART_TWO_AXIS_SCALE_RANGE_MODE_ENUM_DECLARE__ + +#include "CaretAssert.h" + +using namespace caret; + + +/** + * \class caret::ChartTwoAxisScaleRangeModeEnum + * \brief Chart Two Axis Scale Range Mode. + * + * Using this enumerated type in the GUI with an EnumComboBoxTemplate + * + * Header File (.h) + * Forward declare the data type: + * class EnumComboBoxTemplate; + * + * Declare the member: + * EnumComboBoxTemplate* m_chartTwoAxisScaleRangeModeEnumComboBox; + * + * Declare a slot that is called when user changes selection + * private slots: + * void chartTwoAxisScaleRangeModeEnumComboBoxItemActivated(); + * + * Implementation File (.cxx) + * Include the header files + * #include "EnumComboBoxTemplate.h" + * #include "ChartTwoAxisScaleRangeModeEnum.h" + * + * Instatiate: + * m_chartTwoAxisScaleRangeModeEnumComboBox = new EnumComboBoxTemplate(this); + * m_chartTwoAxisScaleRangeModeEnumComboBox->setup(); + * + * Get notified when the user changes the selection: + * QObject::connect(m_chartTwoAxisScaleRangeModeEnumComboBox, SIGNAL(itemActivated()), + * this, SLOT(chartTwoAxisScaleRangeModeEnumComboBoxItemActivated())); + * + * Update the selection: + * m_chartTwoAxisScaleRangeModeEnumComboBox->setSelectedItem(NEW_VALUE); + * + * Read the selection: + * const ChartTwoAxisScaleRangeModeEnum::Enum VARIABLE = m_chartTwoAxisScaleRangeModeEnumComboBox->getSelectedItem(); + * + */ + +/** + * Constructor. + * + * @param enumValue + * An enumerated value. + * @param name + * Name of enumerated value. + * + * @param guiName + * User-friendly name for use in user-interface. + * @param oldName + * An older version of 'name' + */ +ChartTwoAxisScaleRangeModeEnum::ChartTwoAxisScaleRangeModeEnum(const Enum enumValue, + const AString& name, + const AString& guiName, + const AString& oldName) +{ + this->enumValue = enumValue; + this->integerCode = integerCodeCounter++; + this->name = name; + this->guiName = guiName; + this->oldName = oldName; +} + +/** + * Destructor. + */ +ChartTwoAxisScaleRangeModeEnum::~ChartTwoAxisScaleRangeModeEnum() +{ +} + +/** + * Initialize the enumerated metadata. + */ +void +ChartTwoAxisScaleRangeModeEnum::initialize() +{ + if (initializedFlag) { + return; + } + initializedFlag = true; + + enumData.push_back(ChartTwoAxisScaleRangeModeEnum(AUTO, + "AUTO", + "Auto", + "AXIS_DATA_RANGE_AUTO")); + + enumData.push_back(ChartTwoAxisScaleRangeModeEnum(DATA, + "DATA", + "Data", + "AXIS_DATA_RANGE_DATA")); + + enumData.push_back(ChartTwoAxisScaleRangeModeEnum(USER, + "USER", + "User", + "AXIS_DATA_RANGE_USER")); + +} + +/** + * Find the data for and enumerated value. + * @param enumValue + * The enumerated value. + * @return Pointer to data for this enumerated type + * or NULL if no data for type or if type is invalid. + */ +const ChartTwoAxisScaleRangeModeEnum* +ChartTwoAxisScaleRangeModeEnum::findData(const Enum enumValue) +{ + if (initializedFlag == false) initialize(); + + size_t num = enumData.size(); + for (size_t i = 0; i < num; i++) { + const ChartTwoAxisScaleRangeModeEnum* d = &enumData[i]; + if (d->enumValue == enumValue) { + return d; + } + } + + return NULL; +} + +/** + * Get a string representation of the enumerated type. + * @param enumValue + * Enumerated value. + * @return + * String representing enumerated value. + */ +AString +ChartTwoAxisScaleRangeModeEnum::toName(Enum enumValue) { + if (initializedFlag == false) initialize(); + + const ChartTwoAxisScaleRangeModeEnum* enumInstance = findData(enumValue); + return enumInstance->name; +} + +/** + * Get an enumerated value corresponding to its name. + * @param name + * Name of enumerated value. + * @param isValidOut + * If not NULL, it is set indicating that a + * enum value exists for the input name. + * @return + * Enumerated value. + */ +ChartTwoAxisScaleRangeModeEnum::Enum +ChartTwoAxisScaleRangeModeEnum::fromName(const AString& name, bool* isValidOut) +{ + if (initializedFlag == false) initialize(); + + bool validFlag = false; + Enum enumValue = ChartTwoAxisScaleRangeModeEnum::enumData[0].enumValue; + + for (std::vector::iterator iter = enumData.begin(); + iter != enumData.end(); + iter++) { + const ChartTwoAxisScaleRangeModeEnum& d = *iter; + if ((d.name == name) + || (d.oldName == name)) { + enumValue = d.enumValue; + validFlag = true; + break; + } + } + + if (isValidOut != 0) { + *isValidOut = validFlag; + } + else if (validFlag == false) { + CaretAssertMessage(0, AString("Name " + name + "failed to match enumerated value for type ChartTwoAxisScaleRangeModeEnum")); + } + return enumValue; +} + +/** + * Get a GUI string representation of the enumerated type. + * @param enumValue + * Enumerated value. + * @return + * String representing enumerated value. + */ +AString +ChartTwoAxisScaleRangeModeEnum::toGuiName(Enum enumValue) { + if (initializedFlag == false) initialize(); + + const ChartTwoAxisScaleRangeModeEnum* enumInstance = findData(enumValue); + return enumInstance->guiName; +} + +/** + * Get an enumerated value corresponding to its GUI name. + * @param s + * Name of enumerated value. + * @param isValidOut + * If not NULL, it is set indicating that a + * enum value exists for the input name. + * @return + * Enumerated value. + */ +ChartTwoAxisScaleRangeModeEnum::Enum +ChartTwoAxisScaleRangeModeEnum::fromGuiName(const AString& guiName, bool* isValidOut) +{ + if (initializedFlag == false) initialize(); + + bool validFlag = false; + Enum enumValue = ChartTwoAxisScaleRangeModeEnum::enumData[0].enumValue; + + for (std::vector::iterator iter = enumData.begin(); + iter != enumData.end(); + iter++) { + const ChartTwoAxisScaleRangeModeEnum& d = *iter; + if (d.guiName == guiName) { + enumValue = d.enumValue; + validFlag = true; + break; + } + } + + if (isValidOut != 0) { + *isValidOut = validFlag; + } + else if (validFlag == false) { + CaretAssertMessage(0, AString("guiName " + guiName + "failed to match enumerated value for type ChartTwoAxisScaleRangeModeEnum")); + } + return enumValue; +} + +/** + * Get the integer code for a data type. + * + * @return + * Integer code for data type. + */ +int32_t +ChartTwoAxisScaleRangeModeEnum::toIntegerCode(Enum enumValue) +{ + if (initializedFlag == false) initialize(); + const ChartTwoAxisScaleRangeModeEnum* enumInstance = findData(enumValue); + return enumInstance->integerCode; +} + +/** + * Find the data type corresponding to an integer code. + * + * @param integerCode + * Integer code for enum. + * @param isValidOut + * If not NULL, on exit isValidOut will indicate if + * integer code is valid. + * @return + * Enum for integer code. + */ +ChartTwoAxisScaleRangeModeEnum::Enum +ChartTwoAxisScaleRangeModeEnum::fromIntegerCode(const int32_t integerCode, bool* isValidOut) +{ + if (initializedFlag == false) initialize(); + + bool validFlag = false; + Enum enumValue = ChartTwoAxisScaleRangeModeEnum::enumData[0].enumValue; + + for (std::vector::iterator iter = enumData.begin(); + iter != enumData.end(); + iter++) { + const ChartTwoAxisScaleRangeModeEnum& enumInstance = *iter; + if (enumInstance.integerCode == integerCode) { + enumValue = enumInstance.enumValue; + validFlag = true; + break; + } + } + + if (isValidOut != 0) { + *isValidOut = validFlag; + } + else if (validFlag == false) { + CaretAssertMessage(0, AString("Integer code " + AString::number(integerCode) + "failed to match enumerated value for type ChartTwoAxisScaleRangeModeEnum")); + } + return enumValue; +} + +/** + * Get all of the enumerated type values. The values can be used + * as parameters to toXXX() methods to get associated metadata. + * + * @param allEnums + * A vector that is OUTPUT containing all of the enumerated values. + */ +void +ChartTwoAxisScaleRangeModeEnum::getAllEnums(std::vector& allEnums) +{ + if (initializedFlag == false) initialize(); + + allEnums.clear(); + + for (std::vector::iterator iter = enumData.begin(); + iter != enumData.end(); + iter++) { + allEnums.push_back(iter->enumValue); + } +} + +/** + * Get all of the names of the enumerated type values. + * + * @param allNames + * A vector that is OUTPUT containing all of the names of the enumerated values. + * @param isSorted + * If true, the names are sorted in alphabetical order. + */ +void +ChartTwoAxisScaleRangeModeEnum::getAllNames(std::vector& allNames, const bool isSorted) +{ + if (initializedFlag == false) initialize(); + + allNames.clear(); + + for (std::vector::iterator iter = enumData.begin(); + iter != enumData.end(); + iter++) { + allNames.push_back(ChartTwoAxisScaleRangeModeEnum::toName(iter->enumValue)); + } + + if (isSorted) { + std::sort(allNames.begin(), allNames.end()); + } +} + +/** + * Get all of the GUI names of the enumerated type values. + * + * @param allNames + * A vector that is OUTPUT containing all of the GUI names of the enumerated values. + * @param isSorted + * If true, the names are sorted in alphabetical order. + */ +void +ChartTwoAxisScaleRangeModeEnum::getAllGuiNames(std::vector& allGuiNames, const bool isSorted) +{ + if (initializedFlag == false) initialize(); + + allGuiNames.clear(); + + for (std::vector::iterator iter = enumData.begin(); + iter != enumData.end(); + iter++) { + allGuiNames.push_back(ChartTwoAxisScaleRangeModeEnum::toGuiName(iter->enumValue)); + } + + if (isSorted) { + std::sort(allGuiNames.begin(), allGuiNames.end()); + } +} + diff --git a/src/Charting/ChartTwoAxisScaleRangeModeEnum.h b/src/Charting/ChartTwoAxisScaleRangeModeEnum.h new file mode 100644 index 0000000000000000000000000000000000000000..7aa20fcab8189848b08e49afc48d1276a8686007 --- /dev/null +++ b/src/Charting/ChartTwoAxisScaleRangeModeEnum.h @@ -0,0 +1,111 @@ +#ifndef __CHART_TWO_AXIS_SCALE_RANGE_MODE_ENUM_H__ +#define __CHART_TWO_AXIS_SCALE_RANGE_MODE_ENUM_H__ + +/*LICENSE_START*/ +/* + * Copyright (C) 2017 Washington University School of Medicine + * + * 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 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ +/*LICENSE_END*/ + + +#include +#include +#include "AString.h" + +namespace caret { + +class ChartTwoAxisScaleRangeModeEnum { + +public: + /** + * Enumerated values. + */ + enum Enum { + /** Auto scales and pads for nice numeric scale values */ + AUTO, + /** Scales to minimum and maximum of data */ + DATA, + /** Scales to user-specified minimum and maximum */ + /** */ + USER + }; + + + ~ChartTwoAxisScaleRangeModeEnum(); + + static AString toName(Enum enumValue); + + static Enum fromName(const AString& name, bool* isValidOut); + + static AString toGuiName(Enum enumValue); + + static Enum fromGuiName(const AString& guiName, bool* isValidOut); + + static int32_t toIntegerCode(Enum enumValue); + + static Enum fromIntegerCode(const int32_t integerCode, bool* isValidOut); + + static void getAllEnums(std::vector& allEnums); + + static void getAllNames(std::vector& allNames, const bool isSorted); + + static void getAllGuiNames(std::vector& allGuiNames, const bool isSorted); + +private: + ChartTwoAxisScaleRangeModeEnum(const Enum enumValue, + const AString& name, + const AString& guiName, + const AString& oldName); + + static const ChartTwoAxisScaleRangeModeEnum* findData(const Enum enumValue); + + /** Holds all instance of enum values and associated metadata */ + static std::vector enumData; + + /** Initialize instances that contain the enum values and metadata */ + static void initialize(); + + /** Indicates instance of enum values and metadata have been initialized */ + static bool initializedFlag; + + /** Auto generated integer codes */ + static int32_t integerCodeCounter; + + /** The enumerated type value for an instance */ + Enum enumValue; + + /** The integer code associated with an enumerated value */ + int32_t integerCode; + + /** The name, a text string that is identical to the enumerated value */ + AString name; + + /** A user-friendly name that is displayed in the GUI */ + AString guiName; + + /** For older version of 'name' */ + AString oldName; +}; + +#ifdef __CHART_TWO_AXIS_SCALE_RANGE_MODE_ENUM_DECLARE__ +std::vector ChartTwoAxisScaleRangeModeEnum::enumData; +bool ChartTwoAxisScaleRangeModeEnum::initializedFlag = false; +int32_t ChartTwoAxisScaleRangeModeEnum::integerCodeCounter = 0; +#endif // __CHART_TWO_AXIS_SCALE_RANGE_MODE_ENUM_DECLARE__ + +} // namespace +#endif //__CHART_TWO_AXIS_SCALE_RANGE_MODE_ENUM_H__ diff --git a/src/Charting/ChartTwoCartesianAxis.cxx b/src/Charting/ChartTwoCartesianAxis.cxx new file mode 100644 index 0000000000000000000000000000000000000000..9b1af91d7752068ffa8234c560bf9e766ad61c16 --- /dev/null +++ b/src/Charting/ChartTwoCartesianAxis.cxx @@ -0,0 +1,1017 @@ + +/*LICENSE_START*/ +/* + * Copyright (C) 2017 Washington University School of Medicine + * + * 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 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ +/*LICENSE_END*/ + +#define __CHART_TWO_CARTESIAN_AXIS_DECLARE__ +#include "ChartTwoCartesianAxis.h" +#undef __CHART_TWO_CARTESIAN_AXIS_DECLARE__ + +#include "CaretAssert.h" +#include "CaretLogger.h" +#include "ChartScaleAutoRanging.h" +#include "EventChartTwoAxisGetDataRange.h" +#include "EventManager.h" +#include "MathFunctions.h" +#include "NumericTextFormatting.h" +#include "SceneClass.h" +#include "SceneClassAssistant.h" + +using namespace caret; + + + +/** + * \class caret::ChartTwoCartesianAxis + * \brief Chart Two Cartesian Axis Attributes. + * \ingroup Charting + */ + +/** + * Constructor. + * + * @param axisLocation + * Location of the axis (left, right, bottom, top) + * @param parentChartOverlaySet + * Parent of this axis. + */ +ChartTwoCartesianAxis::ChartTwoCartesianAxis(const ChartTwoOverlaySet* parentChartOverlaySet, + const ChartAxisLocationEnum::Enum axisLocation) +: CaretObject(), +SceneableInterface(), +m_parentChartOverlaySet(parentChartOverlaySet), +m_axisLocation(axisLocation) +{ + /* + * Note: Parent chart overlay set is used for sending an event + * to the parent to get the range of data. Note that parent + * chart overlay is in 'Brain' module which we have no access. + */ + CaretAssert(m_parentChartOverlaySet); + + m_showTickmarks = true; + m_showLabel = true; + + m_sceneAssistant = std::unique_ptr(new SceneClassAssistant()); + m_sceneAssistant->add("m_displayedByUser", + &m_displayedByUser); + m_sceneAssistant->add("m_userScaleMinimumValue", + &m_userScaleMinimumValue); + m_sceneAssistant->add("m_userScaleMaximumValue", + &m_userScaleMaximumValue); + m_sceneAssistant->add("m_axisLabelsStepValue", + &m_axisLabelsStepValue); + m_sceneAssistant->add("m_userDigitsRightOfDecimal", + &m_userDigitsRightOfDecimal); + + m_sceneAssistant->add("m_scaleRangeMode", + &m_scaleRangeMode); + m_sceneAssistant->add("m_units", + &m_units); + m_sceneAssistant->add("m_userNumericFormat", + &m_userNumericFormat); + m_sceneAssistant->add("m_numericSubdivsionsMode", + &m_numericSubdivsionsMode); + m_sceneAssistant->add("m_userNumberOfSubdivisions", + &m_userNumberOfSubdivisions); + m_sceneAssistant->add("m_enabledByChart", + &m_enabledByChart); + m_sceneAssistant->add("m_showTickmarks", + &m_showTickmarks); + m_sceneAssistant->add("m_showLabel", + &m_showLabel); + m_sceneAssistant->add("m_titleOverlayIndex", + &m_titleOverlayIndex); + m_sceneAssistant->add("m_labelTextSize", + &m_labelTextSize); + m_sceneAssistant->add("m_numericsTextSize", + &m_numericsTextSize); + m_sceneAssistant->add("m_numericsTextDisplayed", + &m_numericsTextDisplayed); + m_sceneAssistant->add("m_numericsTextRotated", + &m_numericsTextRotated); + m_sceneAssistant->add("m_paddingSize", + &m_paddingSize); +} + +/** + * Destructor. + */ +ChartTwoCartesianAxis::~ChartTwoCartesianAxis() +{ +} + +/** + * Copy constructor. + * @param obj + * Object that is copied. + */ +ChartTwoCartesianAxis::ChartTwoCartesianAxis(const ChartTwoCartesianAxis& obj) +: CaretObject(obj), +SceneableInterface(obj), +m_axisLocation(obj.m_axisLocation) +{ + this->copyHelperChartTwoCartesianAxis(obj); +} + +/** + * Assignment operator. + * @param obj + * Data copied from obj to this. + * @return + * Reference to this object. + */ +ChartTwoCartesianAxis& +ChartTwoCartesianAxis::operator=(const ChartTwoCartesianAxis& obj) +{ + if (this != &obj) { + CaretObject::operator=(obj); + this->copyHelperChartTwoCartesianAxis(obj); + } + return *this; +} + +/** + * Helps with copying an object of this type. + * @param obj + * Object that is copied. + */ +void +ChartTwoCartesianAxis::copyHelperChartTwoCartesianAxis(const ChartTwoCartesianAxis& obj) +{ + CaretAssert(m_axisLocation == obj.m_axisLocation); + + m_titleOverlayIndex = obj.m_titleOverlayIndex; + m_displayedByUser = obj.m_displayedByUser; + m_userScaleMinimumValue = obj.m_userScaleMinimumValue; + m_userScaleMaximumValue = obj.m_userScaleMaximumValue; + m_axisLabelsStepValue = obj.m_axisLabelsStepValue; + m_userDigitsRightOfDecimal = obj.m_userDigitsRightOfDecimal; + m_scaleRangeMode = obj.m_scaleRangeMode; + m_units = obj.m_units; + m_userNumericFormat = obj.m_userNumericFormat; + m_numericSubdivsionsMode = obj.m_numericSubdivsionsMode; + m_userNumberOfSubdivisions = obj.m_userNumberOfSubdivisions; + m_enabledByChart = obj.m_enabledByChart; + m_showTickmarks = obj.m_showTickmarks; + m_showLabel = obj.m_showLabel; + m_labelTextSize = obj.m_labelTextSize; + m_numericsTextSize = obj.m_numericsTextSize; + m_numericsTextDisplayed = obj.m_numericsTextDisplayed; + m_numericsTextRotated = obj.m_numericsTextRotated; + m_paddingSize = obj.m_paddingSize; + limitUserScaleMinMaxToValidRange(); +} + +/** + * @return The axis location (left, right, bottom, top) + */ +ChartAxisLocationEnum::Enum +ChartTwoCartesianAxis::getAxisLocation() const +{ + return m_axisLocation; +} + +/** + * @return display the axis selected by user + */ +bool +ChartTwoCartesianAxis::isDisplayedByUser() const +{ + return m_displayedByUser; +} + +/** + * Set display the axis by user + * @param displayedByUser + * New value for display the axis + */ +void +ChartTwoCartesianAxis::setDisplayedByUser(const bool displayedByUser) +{ + m_displayedByUser = displayedByUser; +} +/** + * Get the range of data for this axis. + * + * @param rangeMinimumOut + * Minimum value allowed. + * @param rangeMaximumOut + * Maximum value allowed. + */ +void +ChartTwoCartesianAxis::getDataRange(float& rangeMinimumOut, + float& rangeMaximumOut) const +{ + EventChartTwoAxisGetDataRange rangeEvent(m_parentChartOverlaySet, + m_axisLocation); + EventManager::get()->sendEvent(rangeEvent.getPointer()); + + if ( ! rangeEvent.getMinimumAndMaximumValues(rangeMinimumOut, + rangeMaximumOut)) { + rangeMinimumOut = 0.0f; + rangeMaximumOut = 0.0f; + } +} + +/** + * Limit the user scale min/max values to be within the valid range. + */ +void +ChartTwoCartesianAxis::limitUserScaleMinMaxToValidRange() +{ +// float minimumValue = 0.0f; +// float maximumValue = 0.0f; +// getDataRange(minimumValue, +// maximumValue); +// +// m_userScaleMinimumValue = MathFunctions::limitRange(m_userScaleMinimumValue, +// minimumValue, +// maximumValue); +// m_userScaleMaximumValue = MathFunctions::limitRange(m_userScaleMaximumValue, +// minimumValue, +// maximumValue); +} + +/** + * @return User's digits right of decimal point. + */ +int32_t +ChartTwoCartesianAxis::getUserDigitsRightOfDecimal() const +{ + return m_userDigitsRightOfDecimal; +} + +/** + * Set user's digits right of decimal point. + * + * @param digitsRightOfDecimal + * Numer of digits right of decimal point. + */ +void +ChartTwoCartesianAxis::setUserDigitsRightOfDecimal(const int32_t digitsRightOfDecimal) +{ + m_userDigitsRightOfDecimal = digitsRightOfDecimal; +} + + +/** + * @return User's number of subdivisions. + */ +int32_t +ChartTwoCartesianAxis::getUserNumberOfSubdivisions() const +{ + return m_userNumberOfSubdivisions; +} + +/** + * Set User's number of subdivisions + * @param numberOfSubdivisions + * New value for Number of subdivisions. + */ +void +ChartTwoCartesianAxis::setUserNumberOfSubdivisions(const int32_t numberOfSubdivisions) +{ + m_userNumberOfSubdivisions = numberOfSubdivisions; +} + +/** + * @return numeric subdivisions mode + */ +ChartTwoNumericSubdivisionsModeEnum::Enum +ChartTwoCartesianAxis::getNumericSubdivsionsMode() const +{ + return m_numericSubdivsionsMode; +} + +/** + * Set numeric subdivisions mode + * + * @param numericSubdivsionsMode + * New value for numeric subdivisions mode + */ +void +ChartTwoCartesianAxis::setNumericSubdivsionsMode(const ChartTwoNumericSubdivisionsModeEnum::Enum numericSubdivsionsMode) +{ + m_numericSubdivsionsMode = numericSubdivsionsMode; +} + +/** + * @return User scale's minimum value + */ +float +ChartTwoCartesianAxis::getUserScaleMinimumValue() const +{ + return m_userScaleMinimumValue; +} + +/** + * @return User scale's maximum value + */ +float +ChartTwoCartesianAxis::getUserScaleMaximumValue() const +{ + return m_userScaleMaximumValue; +} + +/** + * Set User scale's maximum value + * @param userScaleMaximumValue + * New value for User scale's maximum value + */ +void +ChartTwoCartesianAxis::setUserScaleMaximumValue(const float userScaleMaximumValue) +{ + m_userScaleMaximumValue = userScaleMaximumValue; + limitUserScaleMinMaxToValidRange(); +} + +/** + * Set User scale's minimum value + * @param userScaleMinimumValue + * New value for User scale's minimum value + */ +void +ChartTwoCartesianAxis::setUserScaleMinimumValue(const float userScaleMinimumValue) +{ + m_userScaleMinimumValue = userScaleMinimumValue; + limitUserScaleMinMaxToValidRange(); +} + +/** + * @return Is the axis enabled because a chart is using it + */ +bool +ChartTwoCartesianAxis::isEnabledByChart() const +{ + return m_enabledByChart; +} + +/** + * Set the axis enabled because a chart is using it + * + * @param enabled + * New enabled status + */ +void +ChartTwoCartesianAxis::setEnabledByChart(const bool enabled) +{ + m_enabledByChart = enabled; +} + +/** + * @return Show axis tickmarks + */ +bool +ChartTwoCartesianAxis::isShowTickmarks() const +{ + return m_showTickmarks; +} + +/** + * Set Show axis tickmarks + * @param showTickmarks + * New value for Show axis tickmarks + */ +void +ChartTwoCartesianAxis::setShowTickmarks(const bool showTickmarks) +{ + m_showTickmarks = showTickmarks; +} + +/** + * @return show axis label + */ +bool +ChartTwoCartesianAxis::isShowLabel() const +{ + return m_showLabel; +} + +/** + * Set show axis label + * @param showLabel + * New value for show axis label + */ +void +ChartTwoCartesianAxis::setShowLabel(const bool showLabel) +{ + m_showLabel = showLabel; +} + +/** + * @return Scale Range Mode + */ +ChartTwoAxisScaleRangeModeEnum::Enum +ChartTwoCartesianAxis::getScaleRangeMode() const +{ + return m_scaleRangeMode; +} + +/** + * Set Scale Range Mode + * + * @param scaleRangeMode + * New value for Scale Range Mode + */ +void +ChartTwoCartesianAxis::setScaleRangeMode(const ChartTwoAxisScaleRangeModeEnum::Enum scaleRangeMode) +{ + m_scaleRangeMode = scaleRangeMode; +} + +/** + * @return Axis units + */ +CaretUnitsTypeEnum::Enum +ChartTwoCartesianAxis::getUnits() const +{ + return m_units; +} + +/** + * Set Axis units + * + * @param units + * New value for Axis units + */ +void +ChartTwoCartesianAxis::setUnits(const CaretUnitsTypeEnum::Enum units) +{ + m_units = units; +} + +/** + * @return User's umeric format mode + */ +NumericFormatModeEnum::Enum +ChartTwoCartesianAxis::getUserNumericFormat() const +{ + return m_userNumericFormat; +} + +/** + * Set User's Numeric format mode + * + * @param numericFormat + * New value for Numeric format mode + */ +void +ChartTwoCartesianAxis::setUserNumericFormat(const NumericFormatModeEnum::Enum numericFormat) +{ + m_userNumericFormat = numericFormat; +} + +/** + * Get a description of this object's content. + * @return String describing this object's content. + */ +AString +ChartTwoCartesianAxis::toString() const +{ + return "ChartTwoCartesianAxis"; +} + +/** + * @return Index of overlay that supplies the label. + * + * @param maximumNumberOfOverlays + * Maximum number of allowable overlays. + */ +int32_t +ChartTwoCartesianAxis::getLabelOverlayIndex(const int32_t maximumNumberOfOverlays) const +{ + if (m_titleOverlayIndex < 0) { + m_titleOverlayIndex = 0; + } + else if (m_titleOverlayIndex >= maximumNumberOfOverlays) { + m_titleOverlayIndex = 0; + } + + return m_titleOverlayIndex; +} + +/** + * Set the index of the overlay that supplies the title. + * + * @param labelOverlayIndex + * New value for label overlay index. + */ +void +ChartTwoCartesianAxis::setLabelOverlayIndex(const int32_t labelOverlayIndex) +{ + m_titleOverlayIndex = labelOverlayIndex; +} + +/** + * @return size of label text + */ +float +ChartTwoCartesianAxis::getLabelTextSize() const +{ + return m_labelTextSize; +} + +/** + * Set size of label text + * + * @param labelTextSize + * New value for size of label text + */ +void +ChartTwoCartesianAxis::setLabelTextSize(const float labelTextSize) +{ + m_labelTextSize = labelTextSize; +} + +/** + * @return size of numerics text + */ +float +ChartTwoCartesianAxis::getNumericsTextSize() const +{ + return m_numericsTextSize; +} + +/** + * Set size of numerics text + * + * @param numericsTextSize + * New value for size of numerics text + */ +void +ChartTwoCartesianAxis::setNumericsTextSize(const float numericsTextSize) +{ + m_numericsTextSize = numericsTextSize; +} + +/** + * @return display numeric text in scale + */ +bool +ChartTwoCartesianAxis::isNumericsTextDisplayed() const +{ + return m_numericsTextDisplayed; +} + +/** + * Set display numeric text in scale + * + * @param numericsTextDisplayed + * New value for display numeric text in scale + */ +void +ChartTwoCartesianAxis::setNumericsTextDisplayed(const bool numericsTextDisplayed) +{ + m_numericsTextDisplayed = numericsTextDisplayed; +} + +/** + * @return rotate numeric text + */ +bool +ChartTwoCartesianAxis::isNumericsTextRotated() const +{ + return m_numericsTextRotated; +} + +/** + * Set rotate numeric text + * + * @param numericsTextRotated + * New value for rotate numeric text + */ +void +ChartTwoCartesianAxis::setNumericsTextRotated(const bool numericsTextRotated) +{ + m_numericsTextRotated = numericsTextRotated; +} + +/** + * @return size of padding + */ +float +ChartTwoCartesianAxis::getPaddingSize() const +{ + return m_paddingSize; +} + +/** + * Set size of padding + * + * @param paddingSize + * New value for size of padding + */ +void +ChartTwoCartesianAxis::setPaddingSize(const float paddingSize) +{ + m_paddingSize = paddingSize; +} + +/** + * Save information specific to this type of model to the scene. + * + * @param sceneAttributes + * Attributes for the scene. Scenes may be of different types + * (full, generic, etc) and the attributes should be checked when + * saving the scene. + * + * @param instanceName + * Name of instance in the scene. + */ +SceneClass* +ChartTwoCartesianAxis::saveToScene(const SceneAttributes* sceneAttributes, + const AString& instanceName) +{ + SceneClass* sceneClass = new SceneClass(instanceName, + "ChartTwoCartesianAxis", + 1); + m_sceneAssistant->saveMembers(sceneAttributes, + sceneClass); + + // Uncomment if sub-classes must save to scene + //saveSubClassDataToScene(sceneAttributes, + // sceneClass); + + return sceneClass; +} + +/** + * Restore information specific to the type of model from the scene. + * + * @param sceneAttributes + * Attributes for the scene. Scenes may be of different types + * (full, generic, etc) and the attributes should be checked when + * restoring the scene. + * + * @param sceneClass + * sceneClass from which model specific information is obtained. + */ +void +ChartTwoCartesianAxis::restoreFromScene(const SceneAttributes* sceneAttributes, + const SceneClass* sceneClass) +{ + if (sceneClass == NULL) { + return; + } + + // may not be in older scenes + m_displayedByUser = true; + m_numericsTextDisplayed = true; + m_numericsTextRotated = false; + + m_sceneAssistant->restoreMembers(sceneAttributes, + sceneClass); + + //Uncomment if sub-classes must restore from scene + //restoreSubClassDataFromScene(sceneAttributes, + // sceneClass); + +} + +/** + * Given the bounds of the data, determine the auto range minimum and maximum values. + * + * @param minimumValue + * Minimum data value + * @param maximumValue + * Maximum data value + * @param minimumOut + * Output minimum value for autoranging. + * @param maximumOut + * Output maximum value for autoranging. + * @param stepValueOut + * Output step value for scale. + * @param digitsRightOfDecimalOut + * Output with digits right of decimal. + * @return + * True if output values are valid, else false. + */ +bool +ChartTwoCartesianAxis::getAutoRangeMinimumAndMaximum(const float minimumValue, + const float maximumValue, + float& minimumOut, + float& maximumOut, + float& stepValueOut, + int32_t& digitsRightOfDecimalOut) const +{ + float minValue = minimumValue; + float maxValue = maximumValue; + + if (maxValue > minValue) { + double scaleStep = 0.0; + double scaleMin = 0.0; + double scaleMax = 0.0; + int32_t digitsRightOfDecimal = 0; + + ChartScaleAutoRanging::createAutoScale(minValue, + maxValue, + scaleMin, + scaleMax, + scaleStep, + digitsRightOfDecimal); + minimumOut = scaleMin; + maximumOut = scaleMax; + stepValueOut = scaleStep; + digitsRightOfDecimalOut = digitsRightOfDecimal; + + //m_rangeMinimumValue = minimumOut; + //m_rangeMaximumValue = maximumOut; + + return true; + } + + return false; +} + +/** + * Get the axis scale text values and their positions for drawing the scale. + * + * @param minimumDataValue + * Minimum data value + * @param maximumDataValue + * Maximum data value + * @param axisLength + * Length of axis (no specific unit type is assumed) + * @param minimumOut + * Output minimum value for autoranging. + * @param maximumOut + * Output maximum value for autoranging. + * @param scaleValuesOffsetInPixelsOut + * Output containing offset in pixels for the scale values. + * @param scaleValuesOut + * Output containing text for scale values. + * @return + * True if output data is valid, else false. + */ +bool +ChartTwoCartesianAxis::getScaleValuesAndOffsets(const float minimumDataValue, + const float maximumDataValue, + const float axisLength, + float& minimumOut, + float& maximumOut, + std::vector& scaleValuesOffsetInPixelsOut, + std::vector& scaleValuesOut) const +{ + float minimumValue = minimumDataValue; + float maximumValue = maximumDataValue; + + minimumOut = 0.0; + maximumOut = 0.0; + scaleValuesOffsetInPixelsOut.clear(); + scaleValuesOut.clear(); + + if (axisLength <= 0.0) { + CaretAssert(0); + return false; + } + + float labelsStart = 0.0; + float labelsEnd = 0.0; + float labelsStep = 1.0; + int32_t labelsDigitsRightOfDecimal = 0; + + switch (m_scaleRangeMode) { + case ChartTwoAxisScaleRangeModeEnum::AUTO: + break; + case ChartTwoAxisScaleRangeModeEnum::DATA: + break; + case ChartTwoAxisScaleRangeModeEnum::USER: + switch (m_axisLocation) { + case ChartAxisLocationEnum::CHART_AXIS_LOCATION_BOTTOM: + minimumValue = m_userScaleMinimumValue; + maximumValue = m_userScaleMaximumValue; + break; + case ChartAxisLocationEnum::CHART_AXIS_LOCATION_LEFT: + minimumValue = m_userScaleMinimumValue; + maximumValue = m_userScaleMaximumValue; + break; + case ChartAxisLocationEnum::CHART_AXIS_LOCATION_RIGHT: + minimumValue = m_userScaleMinimumValue; + maximumValue = m_userScaleMaximumValue; + break; + case ChartAxisLocationEnum::CHART_AXIS_LOCATION_TOP: + minimumValue = m_userScaleMinimumValue; + maximumValue = m_userScaleMaximumValue; + break; + } + break; + } + + if ( ! getAutoRangeMinimumAndMaximum(minimumValue, + maximumValue, + labelsStart, + labelsEnd, + labelsStep, + labelsDigitsRightOfDecimal)) { + return false; + } + + switch (m_scaleRangeMode) { + case ChartTwoAxisScaleRangeModeEnum::AUTO: + m_userScaleMinimumValue = labelsStart; + m_userScaleMaximumValue = labelsEnd; + break; + case ChartTwoAxisScaleRangeModeEnum::DATA: + { + const double range = maximumDataValue - minimumDataValue; + if (range > 0.0) { + const int32_t numSteps = MathFunctions::round((labelsEnd - labelsStart) / labelsStep); + if (numSteps > 0) { + labelsStart = minimumDataValue; + labelsEnd = maximumDataValue; + labelsStep = range / numSteps; + m_userScaleMinimumValue = labelsStart; + m_userScaleMaximumValue = labelsEnd; + } + } + } + break; + case ChartTwoAxisScaleRangeModeEnum::USER: +// labelsStart = m_userScaleMinimumValue; +// labelsEnd = m_userScaleMaximumValue; + { + const double range = m_userScaleMaximumValue - m_userScaleMinimumValue; + if (range > 0.0) { + const int32_t numSteps = MathFunctions::round((labelsEnd - labelsStart) / labelsStep); + if (numSteps > 0) { + labelsStart = m_userScaleMinimumValue; + labelsEnd = m_userScaleMaximumValue; + labelsStep = range / numSteps; + m_userScaleMinimumValue = labelsStart; + m_userScaleMaximumValue = labelsEnd; + } + } + } + break; + } + + switch (m_numericSubdivsionsMode) { + case ChartTwoNumericSubdivisionsModeEnum::AUTO: + break; + case ChartTwoNumericSubdivisionsModeEnum::USER: + { + const float labelsRange = labelsEnd - labelsStart; + if (labelsRange <= 0.0) { + return false; + } + const float dividend = (1.0 + m_userNumberOfSubdivisions); + labelsStep = labelsRange / dividend; + } + break; + } + + minimumOut = labelsStart; + maximumOut = labelsEnd; + + /* + * If the "labels end" or "labels start" value is not valid (infinity or not-a-number) there + * are invalid values in the data and will cause the labels processing later + * in this method to fail. So, alert the user that there is a problem in + * the data. + * + * A set is used to track those models for which the user has + * already been alerted. Otherwise, the alert message will be + * displayed every time this method is called (which is many) and + * the user will receive endless pop-ups. + */ + if ( (! MathFunctions::isNumeric(labelsStart)) + || (! MathFunctions::isNumeric(labelsEnd))) { + const AString msg("Invalid numbers (infinity or not-a-number) found when trying to create chart. " + "Run \"wb_command -file-information\" on files being charted to find the file " + "that contains invalid data so that the file can be fixed."); + CaretLogWarning(msg); + return false; + } + + float labelsRange = (labelsEnd - labelsStart); + if (labelsRange <= 0.0) { + return false; + } + + const float tickLabelsStep = labelsStep; + if (tickLabelsStep <= 0.0) { + return false; + } + + const float onePercentRange = labelsRange * 0.01f; + + std::vector labelNumericValues; + + float labelValue = labelsStart; + while (labelValue <= labelsEnd) { + float labelParametricValue = (labelValue - labelsStart) / labelsRange; + + float labelValueForText = labelValue; + + if (labelsRange >= 10.0) { + /* + * Is this the first label? + */ + if (labelValue <= labelsStart) { + /* + * Handles case when the minimum DATA value is just a little + * bit greater than the minimum value for axis labels such + * as in Data-Series data when the minimum data value is "1" + * and the minimum axis label value is "0". Without this + * code no value is displayed at the left edge of the axis. + */ + if (labelParametricValue < 0.0) { + const float nextParametricValue = ((labelValue + tickLabelsStep) - labelsStart) / labelsRange; + if (nextParametricValue > 0.05) { + labelParametricValue = 0.0; + labelValueForText = labelsStart; + } + } + } + + if (labelParametricValue < 0.0) { + if (labelParametricValue >= -0.01) { + labelParametricValue = 0.0; + } + } + + /* + * Is this the last label? + */ + if (labelValue >= labelsEnd) { + /* + * Like above, ensures a value is displayed at the right + * edge of the axis. + */ + if (labelParametricValue > 1.0) { + const float prevParametricValue = ((labelValue - tickLabelsStep) - labelsStart) / labelsRange; + if (prevParametricValue < 0.95) { + labelParametricValue = 1.0; + labelValueForText = labelsEnd; + } + } + } + + if (labelParametricValue > 1.0) { + if (labelParametricValue < 1.01) { + labelParametricValue = 1.0; + } + } + } + + if ((labelParametricValue >= 0.0) + && (labelParametricValue <= 1.0)) { + const float labelPixelsPosition = axisLength * labelParametricValue; + labelNumericValues.push_back(labelValueForText); + scaleValuesOffsetInPixelsOut.push_back(labelPixelsPosition); + } + else { + // std::cout << "Label value=" << labelValue << " parametric=" << labelParametricValue << " failed." << std::endl; + } + + labelValue += tickLabelsStep; + + /* + * It is possible that 'labelValue' may be slightly greater than 'labelsEnd' + * for the last label which results in the last label not displayed. + * So, if the 'labelValue' is slightly greater than 'labelsEnd', + * limit 'labelValue' so that the label at the end of the data range + * is displayed. + * + * Example: labelValue = 73.9500046 + * labelsEnd = 73.9499969 + */ + if (labelValue > labelsEnd) { + const float diff = labelValue - labelsEnd; + if (diff < onePercentRange) { + labelValue = labelsEnd; + } + } + } + + const int32_t numValues = static_cast(labelNumericValues.size()); + if (numValues > 0) { + scaleValuesOut.resize(numValues); + NumericTextFormatting::formatValueRange(m_userNumericFormat, + m_userDigitsRightOfDecimal, + &labelNumericValues[0], + &scaleValuesOut[0], + labelNumericValues.size()); + } + + CaretAssert(scaleValuesOffsetInPixelsOut.size() == scaleValuesOut.size()); + return ( ! scaleValuesOut.empty()); +} + diff --git a/src/Charting/ChartTwoCartesianAxis.h b/src/Charting/ChartTwoCartesianAxis.h new file mode 100644 index 0000000000000000000000000000000000000000..265749db1eb7e24c986c6ce263fd65a4b0ee3055 --- /dev/null +++ b/src/Charting/ChartTwoCartesianAxis.h @@ -0,0 +1,234 @@ +#ifndef __CHART_TWO_CARTESIAN_AXIS_H__ +#define __CHART_TWO_CARTESIAN_AXIS_H__ + +/*LICENSE_START*/ +/* + * Copyright (C) 2017 Washington University School of Medicine + * + * 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 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ +/*LICENSE_END*/ + +#include + +#include "CaretObject.h" +#include "ChartAxisLocationEnum.h" +#include "CaretUnitsTypeEnum.h" +#include "ChartTwoAxisScaleRangeModeEnum.h" +#include "ChartTwoNumericSubdivisionsModeEnum.h" +#include "NumericFormatModeEnum.h" +#include "SceneableInterface.h" + + +namespace caret { + class ChartTwoOverlaySet; + class SceneClassAssistant; + + class ChartTwoCartesianAxis : public CaretObject, public SceneableInterface { + + public: + ChartTwoCartesianAxis(const ChartTwoOverlaySet* parentChartOverlaySet, + const ChartAxisLocationEnum::Enum axisLocation); + + virtual ~ChartTwoCartesianAxis(); + + ChartTwoCartesianAxis(const ChartTwoCartesianAxis& obj); + + ChartTwoCartesianAxis& operator=(const ChartTwoCartesianAxis& obj); + + ChartAxisLocationEnum::Enum getAxisLocation() const; + + bool isDisplayedByUser() const; + + void setDisplayedByUser(const bool displayed); + + void getDataRange(float& rangeMinimumOut, + float& rangeMaximumOut) const; + + float getUserScaleMinimumValue() const; + + void setUserScaleMinimumValue(const float value); + + float getUserScaleMaximumValue() const; + + void setUserScaleMaximumValue(const float value); + + int32_t getUserDigitsRightOfDecimal() const; + + void setUserDigitsRightOfDecimal(const int32_t digitsRightOfDecimal); + + ChartTwoAxisScaleRangeModeEnum::Enum getScaleRangeMode() const; + + void setScaleRangeMode(const ChartTwoAxisScaleRangeModeEnum::Enum scaleRangeMode); + + CaretUnitsTypeEnum::Enum getUnits() const; + + void setUnits(CaretUnitsTypeEnum::Enum units); + + NumericFormatModeEnum::Enum getUserNumericFormat() const; + + void setUserNumericFormat(const NumericFormatModeEnum::Enum numericFormat); + + ChartTwoNumericSubdivisionsModeEnum::Enum getNumericSubdivsionsMode() const; + + void setNumericSubdivsionsMode(const ChartTwoNumericSubdivisionsModeEnum::Enum numericSubdivsionsMode); + + int32_t getUserNumberOfSubdivisions() const; + + void setUserNumberOfSubdivisions(const int32_t numberOfSubdivisions); + + bool isEnabledByChart() const; + + void setEnabledByChart(const bool enabled); + + bool isShowTickmarks() const; + + void setShowTickmarks(const bool showTickmarks); + + bool isShowLabel() const; + + void setShowLabel(const bool showLabel); + + float getLabelTextSize() const; + + void setLabelTextSize(const float labelTextSize); + + float getNumericsTextSize() const; + + void setNumericsTextSize(const float numericsTextSize); + + bool isNumericsTextDisplayed() const; + + void setNumericsTextDisplayed(const bool numericsTextDisplayed); + + bool isNumericsTextRotated() const; + + void setNumericsTextRotated(const bool numericsTextRotated); + + float getPaddingSize() const; + + void setPaddingSize(const float paddingSize); + + bool getScaleValuesAndOffsets(const float minimumDataValue, + const float maximumDataValue, + const float axisLength, + float& minimumOut, + float& maximumOut, + std::vector& scaleValuesOffsetInPixelsOut, + std::vector& scaleValuesOut) const; + + int32_t getLabelOverlayIndex(const int32_t maximumNumberOfOverlays) const; + + void setLabelOverlayIndex(const int32_t labelOverlayIndex); + + // ADD_NEW_METHODS_HERE + + virtual AString toString() const; + + virtual SceneClass* saveToScene(const SceneAttributes* sceneAttributes, + const AString& instanceName); + + virtual void restoreFromScene(const SceneAttributes* sceneAttributes, + const SceneClass* sceneClass); + + + + + + +// If there will be sub-classes of this class that need to save +// and restore data from scenes, these pure virtual methods can +// be uncommented to force their implementation by sub-classes. +// protected: +// virtual void saveSubClassDataToScene(const SceneAttributes* sceneAttributes, +// SceneClass* sceneClass) = 0; +// +// virtual void restoreSubClassDataFromScene(const SceneAttributes* sceneAttributes, +// const SceneClass* sceneClass) = 0; + + private: + void copyHelperChartTwoCartesianAxis(const ChartTwoCartesianAxis& obj); + + bool getAutoRangeMinimumAndMaximum(const float minimumValue, + const float maximumValue, + float& minimumOut, + float& maximumOut, + float& stepValueOut, + int32_t& digitsRightOfDecimalOut) const; + + void limitUserScaleMinMaxToValidRange(); + + const ChartTwoOverlaySet* m_parentChartOverlaySet; + + const ChartAxisLocationEnum::Enum m_axisLocation; + + std::unique_ptr m_sceneAssistant; + + mutable float m_userScaleMinimumValue = -100.0f; + + mutable float m_userScaleMaximumValue = 100.0f; + + float m_axisLabelsStepValue = 1.0f; + + int32_t m_userDigitsRightOfDecimal = 1; + + mutable int32_t m_titleOverlayIndex = 0; + + ChartTwoAxisScaleRangeModeEnum::Enum m_scaleRangeMode = ChartTwoAxisScaleRangeModeEnum::AUTO; + + CaretUnitsTypeEnum::Enum m_units = CaretUnitsTypeEnum::NONE; + + NumericFormatModeEnum::Enum m_userNumericFormat = NumericFormatModeEnum::AUTO; + + /** numeric subdivisions mode*/ + ChartTwoNumericSubdivisionsModeEnum::Enum m_numericSubdivsionsMode = ChartTwoNumericSubdivisionsModeEnum::AUTO; + + int32_t m_userNumberOfSubdivisions = 2; + + /** size of label text*/ + float m_labelTextSize = 2.5f; + + /** size of numerics text*/ + float m_numericsTextSize = 2.5f; + + /** display numeric text in scale*/ + bool m_numericsTextDisplayed = true; + + /** rotate numeric text*/ + bool m_numericsTextRotated = false; + + /** size of padding*/ + float m_paddingSize = 0.0f; + + bool m_enabledByChart = false; + + bool m_showTickmarks = true; + + /** show axis label*/ + bool m_showLabel; + + /** user display the axis*/ + bool m_displayedByUser = true; + + // ADD_NEW_MEMBERS_HERE + + }; + +#ifdef __CHART_TWO_CARTESIAN_AXIS_DECLARE__ + // +#endif // __CHART_TWO_CARTESIAN_AXIS_DECLARE__ + +} // namespace +#endif //__CHART_TWO_CARTESIAN_AXIS_H__ diff --git a/src/Charting/ChartTwoCompoundDataType.cxx b/src/Charting/ChartTwoCompoundDataType.cxx new file mode 100644 index 0000000000000000000000000000000000000000..4326d466372fdfe3daf3c9b7dacc7497f97fef28 --- /dev/null +++ b/src/Charting/ChartTwoCompoundDataType.cxx @@ -0,0 +1,420 @@ + +/*LICENSE_START*/ +/* + * Copyright (C) 2016 Washington University School of Medicine + * + * 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 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ +/*LICENSE_END*/ + +#define __CHART_TWO_COMPOUND_DATA_TYPE_DECLARE__ +#include "ChartTwoCompoundDataType.h" +#undef __CHART_TWO_COMPOUND_DATA_TYPE_DECLARE__ + +#include "CaretAssert.h" +#include "SceneClass.h" +#include "SceneClassAssistant.h" + +using namespace caret; + + + +/** + * \class caret::ChartTwoCompoundDataType + * \brief Compound type for type of chart + * \ingroup Charting + * + * A compound type for matching chart types. + * Equality is determined using several pieces of information. + */ + +/** + * Constructor. It is best to use the "new instance" methods. + * + * @param chartDataType + * Chart data type. + * @param histogramNumberOfBuckets + * Number of bins in the histogram. + * @param lineChartUnitsAxisX + * Line chart x-axis units. + * @param lineChartNumberOfElementsAxisX + * Line chart x-axis number of elements. + * @param matrixNumberOfRows + * Matrix number of rows. + * @param matrixNumberOfColumns + * Matrix number of columns. + */ +ChartTwoCompoundDataType::ChartTwoCompoundDataType(const ChartTwoDataTypeEnum::Enum chartDataType, + const int32_t histogramNumberOfBuckets, + const CaretUnitsTypeEnum::Enum lineChartUnitsAxisX, + const int32_t lineChartNumberOfElementsAxisX, + const int32_t matrixNumberOfRows, + const int32_t matrixNumberOfColumns) +: CaretObject() +{ + initializeChartTwoCompoundDataType(); + + m_chartDataType = chartDataType; + m_histogramNumberOfBuckets = histogramNumberOfBuckets; + m_lineChartUnitsAxisX = lineChartUnitsAxisX; + m_lineChartNumberOfElementsAxisX = lineChartNumberOfElementsAxisX; + m_matrixNumberOfRows = matrixNumberOfRows; + m_matrixNumberOfColumns = matrixNumberOfColumns; +} + +/** + * Default constructor of invalid instance. + */ +ChartTwoCompoundDataType::ChartTwoCompoundDataType() +{ + initializeChartTwoCompoundDataType(); +} + +/** + * Destructor. + */ +ChartTwoCompoundDataType::~ChartTwoCompoundDataType() +{ + delete m_sceneAssistant; +} + +/** + * @return A new instance for a histogram chart. + * + * @param histogramNumberOfBins + * Number of bins in the histogram. + */ +ChartTwoCompoundDataType +ChartTwoCompoundDataType::newInstanceForHistogram(const int32_t histogramNumberOfBins) +{ + return ChartTwoCompoundDataType(ChartTwoDataTypeEnum::CHART_DATA_TYPE_HISTOGRAM, + histogramNumberOfBins, + CaretUnitsTypeEnum::NONE, + 0, + 0, + 0); +} + +/** + * @return A new instance for a line-series chart. + * + * @param lineChartUnitsAxisX + * Line chart x-axis units. + * @param lineChartNumberOfElementsAxisX + * Line chart x-axis number of elements. + */ +ChartTwoCompoundDataType +ChartTwoCompoundDataType::newInstanceForLineSeries(const CaretUnitsTypeEnum::Enum lineChartUnitsAxisX, + const int32_t lineChartNumberOfElementsAxisX) +{ + return ChartTwoCompoundDataType(ChartTwoDataTypeEnum::CHART_DATA_TYPE_LINE_SERIES, + 0, + lineChartUnitsAxisX, + lineChartNumberOfElementsAxisX, + 0, + 0); +} + +/** + * @return A new instance for a matrix chart. + * + * @param matrixNumberOfRows + * Matrix number of rows. + * @param matrixNumberOfColumns + * Matrix number of columns. + */ +ChartTwoCompoundDataType +ChartTwoCompoundDataType::newInstanceForMatrix(const int32_t matrixNumberOfRows, + const int32_t matrixNumberOfColumns) +{ + return ChartTwoCompoundDataType(ChartTwoDataTypeEnum::CHART_DATA_TYPE_MATRIX, + 0, + CaretUnitsTypeEnum::NONE, + 0, + matrixNumberOfRows, + matrixNumberOfColumns); +} + + +/** + * Copy constructor. + * @param obj + * Object that is copied. + */ +ChartTwoCompoundDataType::ChartTwoCompoundDataType(const ChartTwoCompoundDataType& obj) +: CaretObject(obj), +SceneableInterface(obj) +{ + initializeChartTwoCompoundDataType(); + this->copyHelperChartTwoCompoundDataType(obj); +} + +/** + * Assignment operator. + * @param obj + * Data copied from obj to this. + * @return + * Reference to this object. + */ +ChartTwoCompoundDataType& +ChartTwoCompoundDataType::operator=(const ChartTwoCompoundDataType& obj) +{ + if (this != &obj) { + CaretObject::operator=(obj); + this->copyHelperChartTwoCompoundDataType(obj); + } + return *this; +} + +/** + * Helps with copying an object of this type. + * @param obj + * Object that is copied. + */ +void +ChartTwoCompoundDataType::copyHelperChartTwoCompoundDataType(const ChartTwoCompoundDataType& obj) +{ + m_chartDataType = obj.m_chartDataType; + m_lineChartUnitsAxisX = obj.m_lineChartUnitsAxisX; + m_lineChartNumberOfElementsAxisX = obj.m_lineChartNumberOfElementsAxisX; + m_matrixNumberOfRows = obj.m_matrixNumberOfRows; + m_matrixNumberOfColumns = obj.m_matrixNumberOfColumns; +} + +/** + * Initialize an invalid instance of this class. + */ +void +ChartTwoCompoundDataType::initializeChartTwoCompoundDataType() +{ + m_chartDataType = ChartTwoDataTypeEnum::CHART_DATA_TYPE_INVALID; + m_lineChartUnitsAxisX = CaretUnitsTypeEnum::NONE; + m_lineChartNumberOfElementsAxisX = 0; + m_matrixNumberOfRows = 0; + m_matrixNumberOfColumns = 0; + + m_sceneAssistant = new SceneClassAssistant(); + m_sceneAssistant->add("m_chartDataType", + &m_chartDataType); + m_sceneAssistant->add("m_lineChartUnitsAxisX", + &m_lineChartUnitsAxisX); + m_sceneAssistant->add("m_lineChartNumberOfElementsAxisX", + &m_lineChartNumberOfElementsAxisX); + m_sceneAssistant->add("m_matrixNumberOfRows", + &m_matrixNumberOfRows); + m_sceneAssistant->add("m_matrixNumberOfColumns", + &m_matrixNumberOfColumns); +} + + +/** + * Equality operator. + * + * Charts must be same type. + * Line charts must be same units OR same number of elements in X-axis. + * Matrix charts must have same number of rows and columns. + * + * @param obj + * Instance compared to this for equality. + * @return + * True if this instance and 'obj' instance are considered equal. + */ +bool +ChartTwoCompoundDataType::operator==(const ChartTwoCompoundDataType& obj) const +{ + if (this == &obj) { + return true; + } + + if (m_chartDataType == obj.m_chartDataType) { + switch (m_chartDataType) { + case ChartTwoDataTypeEnum::CHART_DATA_TYPE_INVALID: + return true; + break; + case ChartTwoDataTypeEnum::CHART_DATA_TYPE_HISTOGRAM: + return true; + break; + case ChartTwoDataTypeEnum::CHART_DATA_TYPE_LINE_SERIES: + { + if (m_lineChartUnitsAxisX == obj.m_lineChartUnitsAxisX) { + return true; + } + if (m_lineChartNumberOfElementsAxisX == obj.m_lineChartNumberOfElementsAxisX) { + return true; + } + } + break; + case ChartTwoDataTypeEnum::CHART_DATA_TYPE_MATRIX: + if ((m_matrixNumberOfRows == obj.m_matrixNumberOfRows) + && (m_matrixNumberOfColumns == obj.m_matrixNumberOfColumns)) { + return true; + } + break; + } + } + + return false; +} + +/** + * @return Type of chart data. + */ +ChartTwoDataTypeEnum::Enum +ChartTwoCompoundDataType::getChartTwoDataType() const +{ + return m_chartDataType; +} + +/** + * @return Histogram number of buckets. + */ +int32_t +ChartTwoCompoundDataType::getHistogramNumberOfBuckets() const +{ + return m_histogramNumberOfBuckets; +} + +/** + * @return Line chart X-axis units. + */ +CaretUnitsTypeEnum::Enum +ChartTwoCompoundDataType::getLineChartUnitsAxisX() const +{ + return m_lineChartUnitsAxisX; +} + +/** + * @return Line chart number of element in the X-axis. + */ +int32_t +ChartTwoCompoundDataType::getLineChartNumberOfElementsAxisX() const +{ + return m_lineChartNumberOfElementsAxisX; +} + +/** + * @return Matrix number of rows. + */ +int32_t +ChartTwoCompoundDataType::getMatrixNumberOfRows() const +{ + return m_matrixNumberOfRows; +} + +/** + * @return Matrix number of columns. + */ +int32_t +ChartTwoCompoundDataType::getMatrixNumberOfColumns() const +{ + return m_matrixNumberOfColumns; +} + +/** + * Get a description of this object's content. + * @return String describing this object's content. + */ +AString +ChartTwoCompoundDataType::toString() const +{ + const AString indent = " "; + AString text = ("type=" + + ChartTwoDataTypeEnum::toGuiName(m_chartDataType)); + + switch (m_chartDataType) { + case ChartTwoDataTypeEnum::CHART_DATA_TYPE_INVALID: + break; + case ChartTwoDataTypeEnum::CHART_DATA_TYPE_HISTOGRAM: + //text.appendWithNewLine(indent + // + "buckets=" + // + QString::number(m_histogramNumberOfBuckets)); + break; + case ChartTwoDataTypeEnum::CHART_DATA_TYPE_LINE_SERIES: + text.appendWithNewLine(indent + + "x=units=" + + CaretUnitsTypeEnum::toName(m_lineChartUnitsAxisX)); + text.appendWithNewLine(indent + + "x-elements=" + + AString::number(m_lineChartNumberOfElementsAxisX)); + break; + case ChartTwoDataTypeEnum::CHART_DATA_TYPE_MATRIX: + text.appendWithNewLine(indent + + "rows=" + + AString::number(m_matrixNumberOfRows)); + text.appendWithNewLine(indent + + "columns=" + + AString::number(m_matrixNumberOfColumns)); + break; + } + + return text; +} + +/** + * Save information specific to this type of model to the scene. + * + * @param sceneAttributes + * Attributes for the scene. Scenes may be of different types + * (full, generic, etc) and the attributes should be checked when + * saving the scene. + * + * @param instanceName + * Name of instance in the scene. + */ +SceneClass* +ChartTwoCompoundDataType::saveToScene(const SceneAttributes* sceneAttributes, + const AString& instanceName) +{ + SceneClass* sceneClass = new SceneClass(instanceName, + "ChartTwoCompoundDataType", + 1); + m_sceneAssistant->saveMembers(sceneAttributes, + sceneClass); + + // Uncomment if sub-classes must save to scene + //saveSubClassDataToScene(sceneAttributes, + // sceneClass); + + return sceneClass; +} + +/** + * Restore information specific to the type of model from the scene. + * + * @param sceneAttributes + * Attributes for the scene. Scenes may be of different types + * (full, generic, etc) and the attributes should be checked when + * restoring the scene. + * + * @param sceneClass + * sceneClass from which model specific information is obtained. + */ +void +ChartTwoCompoundDataType::restoreFromScene(const SceneAttributes* sceneAttributes, + const SceneClass* sceneClass) +{ + if (sceneClass == NULL) { + return; + } + + m_sceneAssistant->restoreMembers(sceneAttributes, + sceneClass); + + //Uncomment if sub-classes must restore from scene + //restoreSubClassDataFromScene(sceneAttributes, + // sceneClass); + +} + diff --git a/src/Charting/ChartTwoCompoundDataType.h b/src/Charting/ChartTwoCompoundDataType.h new file mode 100644 index 0000000000000000000000000000000000000000..5463afa7d2da445a2e1f7e73b2c8d80b87910703 --- /dev/null +++ b/src/Charting/ChartTwoCompoundDataType.h @@ -0,0 +1,128 @@ +#ifndef __CHART_TWO_COMPOUND_DATA_TYPE_H__ +#define __CHART_TWO_COMPOUND_DATA_TYPE_H__ + +/*LICENSE_START*/ +/* + * Copyright (C) 2016 Washington University School of Medicine + * + * 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 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ +/*LICENSE_END*/ + + +#include "CaretObject.h" +#include "CaretUnitsTypeEnum.h" +#include "ChartTwoDataTypeEnum.h" +#include "SceneableInterface.h" + + +namespace caret { + class SceneClassAssistant; + + class ChartTwoCompoundDataType : public CaretObject, public SceneableInterface { + + public: + ChartTwoCompoundDataType(); + + ChartTwoCompoundDataType(const ChartTwoDataTypeEnum::Enum chartDataType, + const int32_t histogramNumberOfBuckets, + const CaretUnitsTypeEnum::Enum lineChartUnitsAxisX, + const int32_t lineChartNumberOfElementsAxisX, + const int32_t matrixNumberOfRows, + const int32_t matrixNumberOfColumns); + + static ChartTwoCompoundDataType newInstanceForHistogram(const int32_t histogramNumberOfBuckets); + + static ChartTwoCompoundDataType newInstanceForLineSeries(const CaretUnitsTypeEnum::Enum lineChartUnitsAxisX, + const int32_t lineChartNumberOfElementsAxisX); + + static ChartTwoCompoundDataType newInstanceForMatrix(const int32_t matrixNumberOfRows, + const int32_t matrixNumberOfColumns); + + + virtual ~ChartTwoCompoundDataType(); + + ChartTwoCompoundDataType(const ChartTwoCompoundDataType& obj); + + ChartTwoCompoundDataType& operator=(const ChartTwoCompoundDataType& obj); + + bool operator==(const ChartTwoCompoundDataType& obj) const; + + ChartTwoDataTypeEnum::Enum getChartTwoDataType() const; + + int32_t getHistogramNumberOfBuckets() const; + + CaretUnitsTypeEnum::Enum getLineChartUnitsAxisX() const; + + int32_t getLineChartNumberOfElementsAxisX() const; + + int32_t getMatrixNumberOfRows() const; + + int32_t getMatrixNumberOfColumns() const; + + // ADD_NEW_METHODS_HERE + + virtual AString toString() const; + + virtual SceneClass* saveToScene(const SceneAttributes* sceneAttributes, + const AString& instanceName); + + virtual void restoreFromScene(const SceneAttributes* sceneAttributes, + const SceneClass* sceneClass); + + + + + + +// If there will be sub-classes of this class that need to save +// and restore data from scenes, these pure virtual methods can +// be uncommented to force their implementation by sub-classes. +// protected: +// virtual void saveSubClassDataToScene(const SceneAttributes* sceneAttributes, +// SceneClass* sceneClass) = 0; +// +// virtual void restoreSubClassDataFromScene(const SceneAttributes* sceneAttributes, +// const SceneClass* sceneClass) = 0; + + private: + void copyHelperChartTwoCompoundDataType(const ChartTwoCompoundDataType& obj); + + void initializeChartTwoCompoundDataType(); + + SceneClassAssistant* m_sceneAssistant; + + ChartTwoDataTypeEnum::Enum m_chartDataType; + + int32_t m_histogramNumberOfBuckets; + + CaretUnitsTypeEnum::Enum m_lineChartUnitsAxisX; + + int32_t m_lineChartNumberOfElementsAxisX; + + int32_t m_matrixNumberOfRows; + + int32_t m_matrixNumberOfColumns; + + // ADD_NEW_MEMBERS_HERE + + }; + +#ifdef __CHART_TWO_COMPOUND_DATA_TYPE_DECLARE__ + // +#endif // __CHART_TWO_COMPOUND_DATA_TYPE_DECLARE__ + +} // namespace +#endif //__CHART_TWO_COMPOUND_DATA_TYPE_H__ diff --git a/src/Charting/ChartTwoDataCartesian.cxx b/src/Charting/ChartTwoDataCartesian.cxx new file mode 100644 index 0000000000000000000000000000000000000000..65fdd1f1032eb20f25ac89b4b85eaf8d0a22af3f --- /dev/null +++ b/src/Charting/ChartTwoDataCartesian.cxx @@ -0,0 +1,525 @@ + +/*LICENSE_START*/ +/* + * Copyright (C) 2014 Washington University School of Medicine + * + * 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 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ +/*LICENSE_END*/ + +#define __CHART_TWO_DATA_CARTESIAN_DECLARE__ +#include "ChartTwoDataCartesian.h" +#undef __CHART_TWO_DATA_CARTESIAN_DECLARE__ + +#include + +#include + +#include "BoundingBox.h" +#include "CaretAssert.h" +#include "ChartPoint.h" +#include "GraphicsPrimitiveV3f.h" +#include "MapFileDataSelector.h" +#include "SceneClass.h" +#include "SceneClassAssistant.h" +#include "ScenePrimitiveArray.h" + +using namespace caret; + + + +/** + * \class caret::ChartTwoDataCartesian + * \brief Chart cartesian data. + * \ingroup Charting + */ + +/** + * Constructor. + * + * @param chartDataType + * Type of chart data model. + * @param dataAxisUnitsX + * Data units for X-axis. + * @param dataAxisUnitsY + * Data units for Y-axis. + * @param graphicsPrimitiveType + * Primitive type for graphics primitive drawing. + */ +ChartTwoDataCartesian::ChartTwoDataCartesian(const ChartTwoDataTypeEnum::Enum chartDataType, + const CaretUnitsTypeEnum::Enum dataAxisUnitsX, + const CaretUnitsTypeEnum::Enum dataAxisUnitsY, + const GraphicsPrimitive::PrimitiveType graphicsPrimitiveType) +: CaretObjectTracksModification(), +m_dataAxisUnitsX(dataAxisUnitsX), +m_dataAxisUnitsY(dataAxisUnitsY), +m_graphicsPrimitiveType(graphicsPrimitiveType) +{ + initializeMembersChartTwoDataCartesian(); + + switch (chartDataType) { + case ChartTwoDataTypeEnum::CHART_DATA_TYPE_INVALID: + CaretAssert(0); + break; + case ChartTwoDataTypeEnum::CHART_DATA_TYPE_HISTOGRAM: + CaretAssert(0); + break; + case ChartTwoDataTypeEnum::CHART_DATA_TYPE_LINE_SERIES: + break; + case ChartTwoDataTypeEnum::CHART_DATA_TYPE_MATRIX: + CaretAssert(0); + break; + } +} + +/** + * Destructor. + */ +ChartTwoDataCartesian::~ChartTwoDataCartesian() +{ + delete m_sceneAssistant; +} + +/** + * Initialize members of a new instance. + */ +void +ChartTwoDataCartesian::initializeMembersChartTwoDataCartesian() +{ + m_mapFileDataSelector = std::unique_ptr(new MapFileDataSelector()); + + m_selectionStatus = true; + m_color = CaretColorEnum::RED; + m_lineWidth = getDefaultLineWidth(); + m_timeStartInSecondsAxisX = 0.0; + m_timeStepInSecondsAxisX = 1.0; + + m_graphicsPrimitive = createGraphicsPrimitive(); + + std::vector colorEnums; + CaretColorEnum::getColorEnumsNoBlackOrWhite(colorEnums); + const int32_t numCaretColors = static_cast(colorEnums.size()); + + bool colorFound = false; + while ( ! colorFound) { + ChartTwoDataCartesian::caretColorIndex++; + if (ChartTwoDataCartesian::caretColorIndex >= numCaretColors) { + ChartTwoDataCartesian::caretColorIndex = 0; + } + + if (colorEnums[ChartTwoDataCartesian::caretColorIndex] == CaretColorEnum::BLACK) { + /* do not use black */ + } + else if (colorEnums[ChartTwoDataCartesian::caretColorIndex] == CaretColorEnum::WHITE) { + /* do not use white */ + } + else { + m_color = colorEnums[ChartTwoDataCartesian::caretColorIndex]; + colorFound = true; + } + } + setColor(m_color); + + m_sceneAssistant = new SceneClassAssistant(); + + m_sceneAssistant->add("m_selectionStatus", + &m_selectionStatus); + m_sceneAssistant->add("m_dataAxisUnitsX", + &m_dataAxisUnitsX); + m_sceneAssistant->add("m_dataAxisUnitsY", + &m_dataAxisUnitsY); + m_sceneAssistant->add("m_color", + &m_color); + m_sceneAssistant->add("m_lineWidth", + &m_lineWidth); + m_sceneAssistant->add("m_timeStartInSecondsAxisX", + &m_timeStartInSecondsAxisX); + m_sceneAssistant->add("m_timeStepInSecondsAxisX", + &m_timeStepInSecondsAxisX); + m_sceneAssistant->add("m_mapFileDataSelector", + "MapFileDataSelector", + m_mapFileDataSelector.get()); +} + +/** + * @return A new instance of the graphics primitive. + */ +std::unique_ptr +ChartTwoDataCartesian::createGraphicsPrimitive() +{ + float rgba[4]; + CaretColorEnum::toRGBAFloat(m_color, rgba); + + /* + * Note: LINES (line segments) are used so that individual segments can be identified. + * Cannot use line strip since it is identified as a single item. + */ + return std::unique_ptr(GraphicsPrimitive::newPrimitiveV3f(m_graphicsPrimitiveType, + rgba)); +} + +/** + * @return The map file data selector that will indicate the source of data for this data. + */ +const MapFileDataSelector* +ChartTwoDataCartesian::getMapFileDataSelector() const +{ + return m_mapFileDataSelector.get(); +} + +/** + * @param + */ +void +ChartTwoDataCartesian::setMapFileDataSelector(const MapFileDataSelector& mapFileDataSelector) +{ + *m_mapFileDataSelector = mapFileDataSelector; +} + +/** + * At times a copy of chart data will be needed BUT it must be + * the proper subclass so copy constructor and assignment operator + * will no function when this abstract, base class is used. Each + * subclass will override this method so that the returned class + * is of the proper type. + * + * @return Copy of this instance that is the actual subclass. + */ +ChartTwoDataCartesian* +ChartTwoDataCartesian::clone() const +{ + ChartTwoDataCartesian* cloneCopy = new ChartTwoDataCartesian(*this); + return cloneCopy; +} + +/** + * Copy constructor. + * @param obj + * Object that is copied. + */ +ChartTwoDataCartesian::ChartTwoDataCartesian(const ChartTwoDataCartesian& obj) +: CaretObjectTracksModification(obj), +SceneableInterface(obj), +m_dataAxisUnitsX(obj.m_dataAxisUnitsX), +m_dataAxisUnitsY(obj.m_dataAxisUnitsY), +m_graphicsPrimitiveType(obj.m_graphicsPrimitiveType) +{ + initializeMembersChartTwoDataCartesian(); + this->copyHelperChartTwoDataCartesian(obj); +} + +/** + * Assignment operator. + * @param obj + * Data copied from obj to this. + * @return + * Reference to this object. + */ +ChartTwoDataCartesian& +ChartTwoDataCartesian::operator=(const ChartTwoDataCartesian& obj) +{ + if (this != &obj) { + CaretObjectTracksModification::operator=(obj); + this->copyHelperChartTwoDataCartesian(obj); + } + return *this; +} + +/** + * Helps with copying an object of this type. + * @param obj + * Object that is copied. + */ +void +ChartTwoDataCartesian::copyHelperChartTwoDataCartesian(const ChartTwoDataCartesian& obj) +{ + m_selectionStatus = obj.m_selectionStatus; + *m_mapFileDataSelector = *obj.m_mapFileDataSelector; + m_dataAxisUnitsX = obj.m_dataAxisUnitsX; + m_dataAxisUnitsY = obj.m_dataAxisUnitsY; + + m_graphicsPrimitive.reset(dynamic_cast(obj.m_graphicsPrimitive->clone())); + + m_color = obj.m_color; + m_lineWidth = obj.m_lineWidth; + m_timeStartInSecondsAxisX = obj.m_timeStartInSecondsAxisX; + m_timeStepInSecondsAxisX = obj.m_timeStepInSecondsAxisX; +} + +/** + * @return Graphics primitive for drawing cartesian data. + */ +GraphicsPrimitiveV3f* +ChartTwoDataCartesian::getGraphicsPrimitive() const +{ + m_graphicsPrimitive->setLineWidth(GraphicsPrimitive::LineWidthType::PERCENTAGE_VIEWPORT_HEIGHT, + m_lineWidth); + return m_graphicsPrimitive.get(); +} + +/** + * @return The selection status + */ +bool +ChartTwoDataCartesian::isSelected() const +{ + return m_selectionStatus; +} + +/** + * Set the selection status. + * + * @param selectionStatus + * New selection status. + */ +void +ChartTwoDataCartesian::setSelected(const bool selectionStatus) +{ + m_selectionStatus = selectionStatus; + + /* + * When selection status is true, + * notify parent. + */ + // if (m_selectionStatus[tabIndex]) { + // if (m_parentChartModel != NULL) { + // m_parentChartModel->childChartTwoDataSelectionChanged(this); + // } + // } +} + + +/** + * Add a point. + * + * @param x + * X-coordinate. + * @param y + * Y-coordinate. + */ +void +ChartTwoDataCartesian::addPoint(const float x, + const float y) +{ + m_graphicsPrimitive->addVertex(x, y); +} + +/** + * Get a bounds box for the cartesian data. + * + * @param boundingBoxOut + * Output bounding box. + * @return + * True if bounding box is valid, else false. + */ +bool +ChartTwoDataCartesian::getBounds(BoundingBox& boundingBoxOut) const +{ + return m_graphicsPrimitive->getVertexBounds(boundingBoxOut); +} + +/** + * @return The time start in seconds for the X-Axis (Valid when + * the X-axis is time) + */ +float +ChartTwoDataCartesian::getTimeStartInSecondsAxisX() const +{ + return m_timeStartInSecondsAxisX; +} + +/** + * Set the time start in seconds for the X-Axis (Valid when + * the X-axis is time) + * + * @param timeStartInSecondsAxisX + * Time of first point in the X-axis. + */ +void +ChartTwoDataCartesian::setTimeStartInSecondsAxisX(const float timeStartInSecondsAxisX) +{ + m_timeStartInSecondsAxisX = timeStartInSecondsAxisX; +} + +/** + * @return The time step in seconds for the X-Axis (Valid when + * the X-axis is time) + */ +float +ChartTwoDataCartesian::getTimeStepInSecondsAxisX() const +{ + return m_timeStepInSecondsAxisX; +} + +/** + * Set the time step in seconds for the X-Axis (Valid when + * the X-axis is time) + * + * @param timeStepInSecondsAxisX + * Number of seconds between consecutive points in X-axis. + */ +void +ChartTwoDataCartesian::setTimeStepInSecondsAxisX(const float timeStepInSecondsAxisX) +{ + m_timeStepInSecondsAxisX = timeStepInSecondsAxisX; +} + +/** + * @return Data units for X axis + */ +CaretUnitsTypeEnum::Enum +ChartTwoDataCartesian::getDataAxisUnitsX() +{ + return m_dataAxisUnitsX; +} + +/** + * @return Data units for Y axis + */ +CaretUnitsTypeEnum::Enum +ChartTwoDataCartesian::getDataAxisUnitsY() +{ + return m_dataAxisUnitsY; +} + +/** + * @return Color for chart + */ +CaretColorEnum::Enum +ChartTwoDataCartesian::getColor() const +{ + return m_color; +} + +/** + * Set the color for the chart. + * + * @param color + * New color for chart. + */ +void +ChartTwoDataCartesian::setColor(const CaretColorEnum::Enum color) +{ + m_color = color; + + if (m_graphicsPrimitive != NULL) { + float rgba[4]; + CaretColorEnum::toRGBAFloat(m_color, rgba); + m_graphicsPrimitive->replaceAllVertexSolidFloatRGBA(rgba); + } +} + +/** + * @return The line width. + */ +float +ChartTwoDataCartesian::getLineWidth() const +{ + return m_lineWidth; +} + +/** + * Set line width. + * + * @param lineWidth + * New value for line width. + */ +void +ChartTwoDataCartesian::setLineWidth(const float lineWidth) +{ + m_lineWidth = lineWidth; + m_graphicsPrimitive->setLineWidth(GraphicsPrimitive::LineWidthType::PERCENTAGE_VIEWPORT_HEIGHT, + m_lineWidth); +} + +/** + * Save information specific to this type of model to the scene. + * + * @param sceneAttributes + * Attributes for the scene. Scenes may be of different types + * (full, generic, etc) and the attributes should be checked when + * saving the scene. + * + * @param instanceName + * Name of instance in the scene. + */ +SceneClass* +ChartTwoDataCartesian::saveToScene(const SceneAttributes* sceneAttributes, + const AString& instanceName) +{ + SceneClass* sceneClass = new SceneClass(instanceName, + "ChartTwoDataCartesian", + 1); + m_sceneAssistant->saveMembers(sceneAttributes, + sceneClass); + +// chartDataCartesian->addClass(m_mapFileDataSelector->saveToScene(sceneAttributes, +// "m_mapFileDataSelector")); + + const std::vector& xyz = m_graphicsPrimitive->getFloatXYZ(); + const int32_t numXYZ = static_cast(xyz.size()); + if (numXYZ > 0) { + sceneClass->addFloatArray("xyz", + &xyz[0], + numXYZ); + } + + return sceneClass; +} + +/** + * Restore information specific to the type of model from the scene. + * + * @param sceneAttributes + * Attributes for the scene. Scenes may be of different types + * (full, generic, etc) and the attributes should be checked when + * restoring the scene. + * + * @param sceneClass + * sceneClass from which model specific information is obtained. + */ +void +ChartTwoDataCartesian::restoreFromScene(const SceneAttributes* sceneAttributes, + const SceneClass* sceneClass) +{ + if (sceneClass == NULL) { + return; + } + m_graphicsPrimitive = createGraphicsPrimitive(); + + m_sceneAssistant->restoreMembers(sceneAttributes, sceneClass); + +// m_mapFileDataSelector->restoreFromScene(sceneAttributes, chartDataCartesian->getClass("m_mapFileDataSelector")); + + const ScenePrimitiveArray* xyzArray = sceneClass->getPrimitiveArray("xyz"); + if (xyzArray != NULL) { + const int32_t numElements = xyzArray->getNumberOfArrayElements(); + if (numElements > 0) { + std::vector data(numElements); + xyzArray->floatValues(&data[0], + numElements, + 0.0f); + const int32_t numXYZ = (numElements / 3); + for (int32_t i = 0; i < numXYZ; i++) { + const int32_t i3 = i * 3; + CaretAssertVectorIndex(data, i3+2); + addPoint(data[i3], data[i3+1]); + } + } + } + + setColor(getColor()); +} + diff --git a/src/Charting/ChartTwoDataCartesian.h b/src/Charting/ChartTwoDataCartesian.h new file mode 100644 index 0000000000000000000000000000000000000000..fe93f530a76d17e90db853e93e01033291cb9a66 --- /dev/null +++ b/src/Charting/ChartTwoDataCartesian.h @@ -0,0 +1,151 @@ +#ifndef __CHART_TWO_DATA_CARTESIAN_H__ +#define __CHART_TWO_DATA_CARTESIAN_H__ + +/*LICENSE_START*/ +/* + * Copyright (C) 2014 Washington University School of Medicine + * + * 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 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ +/*LICENSE_END*/ + +#include "CaretColorEnum.h" +#include "CaretObjectTracksModification.h" +#include "CaretUnitsTypeEnum.h" +#include "ChartTwoDataTypeEnum.h" +#include "GraphicsPrimitive.h" +#include "SceneableInterface.h" + + +namespace caret { + + class ChartPoint; + class GraphicsPrimitiveV3f; + class MapFileDataSelector; + class SceneClassAssistant; + + class ChartTwoDataCartesian : public CaretObjectTracksModification, public SceneableInterface { + + public: + ChartTwoDataCartesian(const ChartTwoDataTypeEnum::Enum chartDataType, + const CaretUnitsTypeEnum::Enum dataAxisUnitsX, + const CaretUnitsTypeEnum::Enum dataAxisUnitsY, + const GraphicsPrimitive::PrimitiveType graphicsPrimitiveType); + + virtual ~ChartTwoDataCartesian(); + + virtual ChartTwoDataCartesian* clone() const; + + bool isSelected() const; + + void setSelected(const bool selectionStatus); + + void addPoint(const float x, + const float y); + + bool getBounds(BoundingBox& boundingBoxOut) const; + + GraphicsPrimitiveV3f* getGraphicsPrimitive() const; + + const MapFileDataSelector* getMapFileDataSelector() const; + + void setMapFileDataSelector(const MapFileDataSelector& mapFileDataSelector); + + CaretUnitsTypeEnum::Enum getDataAxisUnitsX(); + + CaretUnitsTypeEnum::Enum getDataAxisUnitsY(); + + CaretColorEnum::Enum getColor() const; + + void setColor(const CaretColorEnum::Enum color); + + float getLineWidth() const; + + void setLineWidth(const float lineWidth); + + float getTimeStartInSecondsAxisX() const; + + void setTimeStartInSecondsAxisX(const float timeStart); + + float getTimeStepInSecondsAxisX() const; + + void setTimeStepInSecondsAxisX(const float timeStep); + + // ADD_NEW_METHODS_HERE + + virtual SceneClass* saveToScene(const SceneAttributes* sceneAttributes, + const AString& instanceName); + + virtual void restoreFromScene(const SceneAttributes* sceneAttributes, + const SceneClass* sceneClass); + + /** + * @return The default line width for cartesian charts. + */ + static float getDefaultLineWidth() { return 0.5f; } + + protected: +// virtual void saveSubClassDataToScene(const SceneAttributes* sceneAttributes, +// SceneClass* sceneClass); +// +// virtual void restoreSubClassDataFromScene(const SceneAttributes* sceneAttributes, +// const SceneClass* sceneClass); + + + private: + ChartTwoDataCartesian(const ChartTwoDataCartesian& obj); + + ChartTwoDataCartesian& operator=(const ChartTwoDataCartesian& obj); + + void copyHelperChartTwoDataCartesian(const ChartTwoDataCartesian& obj); + + void initializeMembersChartTwoDataCartesian(); + + std::unique_ptr createGraphicsPrimitive(); + + std::unique_ptr m_mapFileDataSelector; + + std::unique_ptr m_graphicsPrimitive; + + CaretUnitsTypeEnum::Enum m_dataAxisUnitsX; + + CaretUnitsTypeEnum::Enum m_dataAxisUnitsY; + + const GraphicsPrimitive::PrimitiveType m_graphicsPrimitiveType; + + bool m_selectionStatus; + + CaretColorEnum::Enum m_color; + + float m_lineWidth; + + float m_timeStartInSecondsAxisX; + + float m_timeStepInSecondsAxisX; + + static int32_t caretColorIndex; + + SceneClassAssistant* m_sceneAssistant; + + // ADD_NEW_MEMBERS_HERE + + }; + +#ifdef __CHART_TWO_DATA_CARTESIAN_DECLARE__ + int32_t ChartTwoDataCartesian::caretColorIndex = 0; +#endif // __CHART_TWO_DATA_CARTESIAN_DECLARE__ + +} // namespace +#endif //__CHART_TWO_DATA_CARTESIAN_H__ diff --git a/src/Charting/ChartTwoDataTypeEnum.cxx b/src/Charting/ChartTwoDataTypeEnum.cxx new file mode 100644 index 0000000000000000000000000000000000000000..9bac88d38061f5765a7c83fa64b08bf309feb2cc --- /dev/null +++ b/src/Charting/ChartTwoDataTypeEnum.cxx @@ -0,0 +1,384 @@ + +/*LICENSE_START*/ +/* + * Copyright (C) 2016 Washington University School of Medicine + * + * 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 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ +/*LICENSE_END*/ + +#include +#define __CHART_TWO_DATA_TYPE_ENUM_DECLARE__ +#include "ChartTwoDataTypeEnum.h" +#undef __CHART_TWO_DATA_TYPE_ENUM_DECLARE__ + +#include "CaretAssert.h" + +using namespace caret; + + +/** + * \class caret::ChartTwoDataTypeEnum + * \brief ChartDrawingType + * + * Using this enumerated type in the GUI with an EnumComboBoxTemplate + * + * Header File (.h) + * Forward declare the data type: + * class EnumComboBoxTemplate; + * + * Declare the member: + * EnumComboBoxTemplate* m_chartDrawingTypeEnumComboBox; + * + * Declare a slot that is called when user changes selection + * private slots: + * void chartDrawingTypeEnumComboBoxItemActivated(); + * + * Implementation File (.cxx) + * Include the header files + * #include "EnumComboBoxTemplate.h" + * #include "ChartTwoDataTypeEnum.h" + * + * Instatiate: + * m_chartDrawingTypeEnumComboBox = new EnumComboBoxTemplate(this); + * m_chartDrawingTypeEnumComboBox->setup(); + * + * Get notified when the user changes the selection: + * QObject::connect(m_chartDrawingTypeEnumComboBox, SIGNAL(itemActivated()), + * this, SLOT(chartDrawingTypeEnumComboBoxItemActivated())); + * + * Update the selection: + * m_chartDrawingTypeEnumComboBox->setSelectedItem(NEW_VALUE); + * + * Read the selection: + * const ChartTwoDataTypeEnum::Enum VARIABLE = m_chartDrawingTypeEnumComboBox->getSelectedItem(); + * + */ + +/** + * Constructor. + * + * @param enumValue + * An enumerated value. + * @param name + * Name of enumerated value. + * + * @param guiName + * User-friendly name for use in user-interface. + */ +ChartTwoDataTypeEnum::ChartTwoDataTypeEnum(const Enum enumValue, + const AString& name, + const AString& guiName) +{ + this->enumValue = enumValue; + this->integerCode = integerCodeCounter++; + this->name = name; + this->guiName = guiName; +} + +/** + * Destructor. + */ +ChartTwoDataTypeEnum::~ChartTwoDataTypeEnum() +{ +} + +/** + * Initialize the enumerated metadata. + */ +void +ChartTwoDataTypeEnum::initialize() +{ + if (initializedFlag) { + return; + } + initializedFlag = true; + + enumData.push_back(ChartTwoDataTypeEnum(CHART_DATA_TYPE_INVALID, + "CHART_DATA_TYPE_INVALID", + "Invalid")); + + enumData.push_back(ChartTwoDataTypeEnum(CHART_DATA_TYPE_HISTOGRAM, + "CHART_DATA_TYPE_HISTOGRAM", + "Histogram")); + + enumData.push_back(ChartTwoDataTypeEnum(CHART_DATA_TYPE_LINE_SERIES, + "CHART_DATA_TYPE_LINE_SERIES", + "Line Series")); + + enumData.push_back(ChartTwoDataTypeEnum(CHART_DATA_TYPE_MATRIX, + "CHART_DATA_TYPE_MATRIX", + "Matrix")); + + /* If this fails (chart types change), update value for NUMBER_OF_CHART_DATA_TYPES */ + CaretAssertMessage(enumData.size() == NUMBER_OF_CHART_DATA_TYPES, + "Have chart types changed?"); +} + +/** + * Find the data for and enumerated value. + * @param enumValue + * The enumerated value. + * @return Pointer to data for this enumerated type + * or NULL if no data for type or if type is invalid. + */ +const ChartTwoDataTypeEnum* +ChartTwoDataTypeEnum::findData(const Enum enumValue) +{ + if (initializedFlag == false) initialize(); + + size_t num = enumData.size(); + for (size_t i = 0; i < num; i++) { + const ChartTwoDataTypeEnum* d = &enumData[i]; + if (d->enumValue == enumValue) { + return d; + } + } + + return NULL; +} + +/** + * Get a string representation of the enumerated type. + * @param enumValue + * Enumerated value. + * @return + * String representing enumerated value. + */ +AString +ChartTwoDataTypeEnum::toName(Enum enumValue) { + if (initializedFlag == false) initialize(); + + const ChartTwoDataTypeEnum* enumInstance = findData(enumValue); + return enumInstance->name; +} + +/** + * Get an enumerated value corresponding to its name. + * @param name + * Name of enumerated value. + * @param isValidOut + * If not NULL, it is set indicating that a + * enum value exists for the input name. + * @return + * Enumerated value. + */ +ChartTwoDataTypeEnum::Enum +ChartTwoDataTypeEnum::fromName(const AString& name, bool* isValidOut) +{ + if (initializedFlag == false) initialize(); + + bool validFlag = false; + Enum enumValue = ChartTwoDataTypeEnum::enumData[0].enumValue; + + for (std::vector::iterator iter = enumData.begin(); + iter != enumData.end(); + iter++) { + const ChartTwoDataTypeEnum& d = *iter; + if (d.name == name) { + enumValue = d.enumValue; + validFlag = true; + break; + } + } + + if (isValidOut != 0) { + *isValidOut = validFlag; + } + else if (validFlag == false) { + CaretAssertMessage(0, AString("Name " + name + "failed to match enumerated value for type ChartTwoDataTypeEnum")); + } + return enumValue; +} + +/** + * Get a GUI string representation of the enumerated type. + * @param enumValue + * Enumerated value. + * @return + * String representing enumerated value. + */ +AString +ChartTwoDataTypeEnum::toGuiName(Enum enumValue) { + if (initializedFlag == false) initialize(); + + const ChartTwoDataTypeEnum* enumInstance = findData(enumValue); + return enumInstance->guiName; +} + +/** + * Get an enumerated value corresponding to its GUI name. + * @param s + * Name of enumerated value. + * @param isValidOut + * If not NULL, it is set indicating that a + * enum value exists for the input name. + * @return + * Enumerated value. + */ +ChartTwoDataTypeEnum::Enum +ChartTwoDataTypeEnum::fromGuiName(const AString& guiName, bool* isValidOut) +{ + if (initializedFlag == false) initialize(); + + bool validFlag = false; + Enum enumValue = ChartTwoDataTypeEnum::enumData[0].enumValue; + + for (std::vector::iterator iter = enumData.begin(); + iter != enumData.end(); + iter++) { + const ChartTwoDataTypeEnum& d = *iter; + if (d.guiName == guiName) { + enumValue = d.enumValue; + validFlag = true; + break; + } + } + + if (isValidOut != 0) { + *isValidOut = validFlag; + } + else if (validFlag == false) { + CaretAssertMessage(0, AString("guiName " + guiName + "failed to match enumerated value for type ChartTwoDataTypeEnum")); + } + return enumValue; +} + +/** + * Get the integer code for a data type. + * + * @return + * Integer code for data type. + */ +int32_t +ChartTwoDataTypeEnum::toIntegerCode(Enum enumValue) +{ + if (initializedFlag == false) initialize(); + const ChartTwoDataTypeEnum* enumInstance = findData(enumValue); + return enumInstance->integerCode; +} + +/** + * Find the data type corresponding to an integer code. + * + * @param integerCode + * Integer code for enum. + * @param isValidOut + * If not NULL, on exit isValidOut will indicate if + * integer code is valid. + * @return + * Enum for integer code. + */ +ChartTwoDataTypeEnum::Enum +ChartTwoDataTypeEnum::fromIntegerCode(const int32_t integerCode, bool* isValidOut) +{ + if (initializedFlag == false) initialize(); + + bool validFlag = false; + Enum enumValue = ChartTwoDataTypeEnum::enumData[0].enumValue; + + for (std::vector::iterator iter = enumData.begin(); + iter != enumData.end(); + iter++) { + const ChartTwoDataTypeEnum& enumInstance = *iter; + if (enumInstance.integerCode == integerCode) { + enumValue = enumInstance.enumValue; + validFlag = true; + break; + } + } + + if (isValidOut != 0) { + *isValidOut = validFlag; + } + else if (validFlag == false) { + CaretAssertMessage(0, AString("Integer code " + AString::number(integerCode) + "failed to match enumerated value for type ChartTwoDataTypeEnum")); + } + return enumValue; +} + +/** + * Get all of the enumerated type values. The values can be used + * as parameters to toXXX() methods to get associated metadata. + * + * @param allEnums + * A vector that is OUTPUT containing all of the enumerated values. + */ +void +ChartTwoDataTypeEnum::getAllEnums(std::vector& allEnums) +{ + if (initializedFlag == false) initialize(); + + allEnums.clear(); + + for (std::vector::iterator iter = enumData.begin(); + iter != enumData.end(); + iter++) { + allEnums.push_back(iter->enumValue); + } +} + +/** + * Get all of the names of the enumerated type values. + * + * @param allNames + * A vector that is OUTPUT containing all of the names of the enumerated values. + * @param isSorted + * If true, the names are sorted in alphabetical order. + */ +void +ChartTwoDataTypeEnum::getAllNames(std::vector& allNames, const bool isSorted) +{ + if (initializedFlag == false) initialize(); + + allNames.clear(); + + for (std::vector::iterator iter = enumData.begin(); + iter != enumData.end(); + iter++) { + allNames.push_back(ChartTwoDataTypeEnum::toName(iter->enumValue)); + } + + if (isSorted) { + std::sort(allNames.begin(), allNames.end()); + } +} + +/** + * Get all of the GUI names of the enumerated type values. + * + * @param allNames + * A vector that is OUTPUT containing all of the GUI names of the enumerated values. + * @param isSorted + * If true, the names are sorted in alphabetical order. + */ +void +ChartTwoDataTypeEnum::getAllGuiNames(std::vector& allGuiNames, const bool isSorted) +{ + if (initializedFlag == false) initialize(); + + allGuiNames.clear(); + + for (std::vector::iterator iter = enumData.begin(); + iter != enumData.end(); + iter++) { + allGuiNames.push_back(ChartTwoDataTypeEnum::toGuiName(iter->enumValue)); + } + + if (isSorted) { + std::sort(allGuiNames.begin(), allGuiNames.end()); + } +} + diff --git a/src/Charting/ChartTwoDataTypeEnum.h b/src/Charting/ChartTwoDataTypeEnum.h new file mode 100644 index 0000000000000000000000000000000000000000..af3b482a5849c2cd57c6a3742d15eec016eb1c9b --- /dev/null +++ b/src/Charting/ChartTwoDataTypeEnum.h @@ -0,0 +1,110 @@ +#ifndef __CHART_TWO_DATA_TYPE_ENUM_H__ +#define __CHART_TWO_DATA_TYPE_ENUM_H__ + +/*LICENSE_START*/ +/* + * Copyright (C) 2016 Washington University School of Medicine + * + * 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 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ +/*LICENSE_END*/ + + +#include +#include +#include "AString.h" + +namespace caret { + +class ChartTwoDataTypeEnum { + +public: + /** + * Enumerated values. + */ + enum Enum { + /** Invalid */ + CHART_DATA_TYPE_INVALID, + /** Histogram */ + CHART_DATA_TYPE_HISTOGRAM, + /** Line Series */ + CHART_DATA_TYPE_LINE_SERIES, + /** Matrix */ + CHART_DATA_TYPE_MATRIX + }; + + + ~ChartTwoDataTypeEnum(); + + static AString toName(Enum enumValue); + + static Enum fromName(const AString& name, bool* isValidOut); + + static AString toGuiName(Enum enumValue); + + static Enum fromGuiName(const AString& guiName, bool* isValidOut); + + static int32_t toIntegerCode(Enum enumValue); + + static Enum fromIntegerCode(const int32_t integerCode, bool* isValidOut); + + static void getAllEnums(std::vector& allEnums); + + static void getAllNames(std::vector& allNames, const bool isSorted); + + static void getAllGuiNames(std::vector& allGuiNames, const bool isSorted); + + static const int32_t NUMBER_OF_CHART_DATA_TYPES = 4; + +private: + ChartTwoDataTypeEnum(const Enum enumValue, + const AString& name, + const AString& guiName); + + static const ChartTwoDataTypeEnum* findData(const Enum enumValue); + + /** Holds all instance of enum values and associated metadata */ + static std::vector enumData; + + /** Initialize instances that contain the enum values and metadata */ + static void initialize(); + + /** Indicates instance of enum values and metadata have been initialized */ + static bool initializedFlag; + + /** Auto generated integer codes */ + static int32_t integerCodeCounter; + + /** The enumerated type value for an instance */ + Enum enumValue; + + /** The integer code associated with an enumerated value */ + int32_t integerCode; + + /** The name, a text string that is identical to the enumerated value */ + AString name; + + /** A user-friendly name that is displayed in the GUI */ + AString guiName; +}; + +#ifdef __CHART_TWO_DATA_TYPE_ENUM_DECLARE__ +std::vector ChartTwoDataTypeEnum::enumData; +bool ChartTwoDataTypeEnum::initializedFlag = false; +int32_t ChartTwoDataTypeEnum::integerCodeCounter = 0; +#endif // __CHART_TWO_DATA_TYPE_ENUM_DECLARE__ + +} // namespace +#endif //__CHART_TWO_DATA_TYPE_ENUM_H__ diff --git a/src/Charting/ChartTwoHistogramContentTypeEnum.cxx b/src/Charting/ChartTwoHistogramContentTypeEnum.cxx new file mode 100644 index 0000000000000000000000000000000000000000..395828fe3d3188a8af2cb42c2b699f7e5ebc91c1 --- /dev/null +++ b/src/Charting/ChartTwoHistogramContentTypeEnum.cxx @@ -0,0 +1,373 @@ + +/*LICENSE_START*/ +/* + * Copyright (C) 2017 Washington University School of Medicine + * + * 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 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ +/*LICENSE_END*/ + +#include +#define __CHART_TWO_HISTOGRAM_CONTENT_TYPE_ENUM_DECLARE__ +#include "ChartTwoHistogramContentTypeEnum.h" +#undef __CHART_TWO_HISTOGRAM_CONTENT_TYPE_ENUM_DECLARE__ + +#include "CaretAssert.h" + +using namespace caret; + + +/** + * \class caret::ChartTwoHistogramContentTypeEnum + * \brief Type of content for histograms. + * + * Using this enumerated type in the GUI with an EnumComboBoxTemplate + * + * Header File (.h) + * Forward declare the data type: + * class EnumComboBoxTemplate; + * + * Declare the member: + * EnumComboBoxTemplate* m_chartTwoHistogramContentTypeEnumComboBox; + * + * Declare a slot that is called when user changes selection + * private slots: + * void chartTwoHistogramContentTypeEnumComboBoxItemActivated(); + * + * Implementation File (.cxx) + * Include the header files + * #include "EnumComboBoxTemplate.h" + * #include "ChartTwoHistogramContentTypeEnum.h" + * + * Instatiate: + * m_chartTwoHistogramContentTypeEnumComboBox = new EnumComboBoxTemplate(this); + * m_chartTwoHistogramContentTypeEnumComboBox->setup(); + * + * Get notified when the user changes the selection: + * QObject::connect(m_chartTwoHistogramContentTypeEnumComboBox, SIGNAL(itemActivated()), + * this, SLOT(chartTwoHistogramContentTypeEnumComboBoxItemActivated())); + * + * Update the selection: + * m_chartTwoHistogramContentTypeEnumComboBox->setSelectedItem(NEW_VALUE); + * + * Read the selection: + * const ChartTwoHistogramContentTypeEnum::Enum VARIABLE = m_chartTwoHistogramContentTypeEnumComboBox->getSelectedItem(); + * + */ + +/** + * Constructor. + * + * @param enumValue + * An enumerated value. + * @param name + * Name of enumerated value. + * + * @param guiName + * User-friendly name for use in user-interface. + */ +ChartTwoHistogramContentTypeEnum::ChartTwoHistogramContentTypeEnum(const Enum enumValue, + const AString& name, + const AString& guiName) +{ + this->enumValue = enumValue; + this->integerCode = integerCodeCounter++; + this->name = name; + this->guiName = guiName; +} + +/** + * Destructor. + */ +ChartTwoHistogramContentTypeEnum::~ChartTwoHistogramContentTypeEnum() +{ +} + +/** + * Initialize the enumerated metadata. + */ +void +ChartTwoHistogramContentTypeEnum::initialize() +{ + if (initializedFlag) { + return; + } + initializedFlag = true; + + enumData.push_back(ChartTwoHistogramContentTypeEnum(HISTOGRAM_CONTENT_TYPE_UNSUPPORTED, + "HISTOGRAM_CONTENT_TYPE_UNSUPPORTED", + "Unsupported")); + + enumData.push_back(ChartTwoHistogramContentTypeEnum(HISTOGRAM_CONTENT_TYPE_MAP_DATA, + "HISTOGRAM_CONTENT_TYPE_MAP_DATA", + "Map Data")); + +} + +/** + * Find the data for and enumerated value. + * @param enumValue + * The enumerated value. + * @return Pointer to data for this enumerated type + * or NULL if no data for type or if type is invalid. + */ +const ChartTwoHistogramContentTypeEnum* +ChartTwoHistogramContentTypeEnum::findData(const Enum enumValue) +{ + if (initializedFlag == false) initialize(); + + size_t num = enumData.size(); + for (size_t i = 0; i < num; i++) { + const ChartTwoHistogramContentTypeEnum* d = &enumData[i]; + if (d->enumValue == enumValue) { + return d; + } + } + + return NULL; +} + +/** + * Get a string representation of the enumerated type. + * @param enumValue + * Enumerated value. + * @return + * String representing enumerated value. + */ +AString +ChartTwoHistogramContentTypeEnum::toName(Enum enumValue) { + if (initializedFlag == false) initialize(); + + const ChartTwoHistogramContentTypeEnum* enumInstance = findData(enumValue); + return enumInstance->name; +} + +/** + * Get an enumerated value corresponding to its name. + * @param name + * Name of enumerated value. + * @param isValidOut + * If not NULL, it is set indicating that a + * enum value exists for the input name. + * @return + * Enumerated value. + */ +ChartTwoHistogramContentTypeEnum::Enum +ChartTwoHistogramContentTypeEnum::fromName(const AString& name, bool* isValidOut) +{ + if (initializedFlag == false) initialize(); + + bool validFlag = false; + Enum enumValue = ChartTwoHistogramContentTypeEnum::enumData[0].enumValue; + + for (std::vector::iterator iter = enumData.begin(); + iter != enumData.end(); + iter++) { + const ChartTwoHistogramContentTypeEnum& d = *iter; + if (d.name == name) { + enumValue = d.enumValue; + validFlag = true; + break; + } + } + + if (isValidOut != 0) { + *isValidOut = validFlag; + } + else if (validFlag == false) { + CaretAssertMessage(0, AString("Name " + name + "failed to match enumerated value for type ChartTwoHistogramContentTypeEnum")); + } + return enumValue; +} + +/** + * Get a GUI string representation of the enumerated type. + * @param enumValue + * Enumerated value. + * @return + * String representing enumerated value. + */ +AString +ChartTwoHistogramContentTypeEnum::toGuiName(Enum enumValue) { + if (initializedFlag == false) initialize(); + + const ChartTwoHistogramContentTypeEnum* enumInstance = findData(enumValue); + return enumInstance->guiName; +} + +/** + * Get an enumerated value corresponding to its GUI name. + * @param s + * Name of enumerated value. + * @param isValidOut + * If not NULL, it is set indicating that a + * enum value exists for the input name. + * @return + * Enumerated value. + */ +ChartTwoHistogramContentTypeEnum::Enum +ChartTwoHistogramContentTypeEnum::fromGuiName(const AString& guiName, bool* isValidOut) +{ + if (initializedFlag == false) initialize(); + + bool validFlag = false; + Enum enumValue = ChartTwoHistogramContentTypeEnum::enumData[0].enumValue; + + for (std::vector::iterator iter = enumData.begin(); + iter != enumData.end(); + iter++) { + const ChartTwoHistogramContentTypeEnum& d = *iter; + if (d.guiName == guiName) { + enumValue = d.enumValue; + validFlag = true; + break; + } + } + + if (isValidOut != 0) { + *isValidOut = validFlag; + } + else if (validFlag == false) { + CaretAssertMessage(0, AString("guiName " + guiName + "failed to match enumerated value for type ChartTwoHistogramContentTypeEnum")); + } + return enumValue; +} + +/** + * Get the integer code for a data type. + * + * @return + * Integer code for data type. + */ +int32_t +ChartTwoHistogramContentTypeEnum::toIntegerCode(Enum enumValue) +{ + if (initializedFlag == false) initialize(); + const ChartTwoHistogramContentTypeEnum* enumInstance = findData(enumValue); + return enumInstance->integerCode; +} + +/** + * Find the data type corresponding to an integer code. + * + * @param integerCode + * Integer code for enum. + * @param isValidOut + * If not NULL, on exit isValidOut will indicate if + * integer code is valid. + * @return + * Enum for integer code. + */ +ChartTwoHistogramContentTypeEnum::Enum +ChartTwoHistogramContentTypeEnum::fromIntegerCode(const int32_t integerCode, bool* isValidOut) +{ + if (initializedFlag == false) initialize(); + + bool validFlag = false; + Enum enumValue = ChartTwoHistogramContentTypeEnum::enumData[0].enumValue; + + for (std::vector::iterator iter = enumData.begin(); + iter != enumData.end(); + iter++) { + const ChartTwoHistogramContentTypeEnum& enumInstance = *iter; + if (enumInstance.integerCode == integerCode) { + enumValue = enumInstance.enumValue; + validFlag = true; + break; + } + } + + if (isValidOut != 0) { + *isValidOut = validFlag; + } + else if (validFlag == false) { + CaretAssertMessage(0, AString("Integer code " + AString::number(integerCode) + "failed to match enumerated value for type ChartTwoHistogramContentTypeEnum")); + } + return enumValue; +} + +/** + * Get all of the enumerated type values. The values can be used + * as parameters to toXXX() methods to get associated metadata. + * + * @param allEnums + * A vector that is OUTPUT containing all of the enumerated values. + */ +void +ChartTwoHistogramContentTypeEnum::getAllEnums(std::vector& allEnums) +{ + if (initializedFlag == false) initialize(); + + allEnums.clear(); + + for (std::vector::iterator iter = enumData.begin(); + iter != enumData.end(); + iter++) { + allEnums.push_back(iter->enumValue); + } +} + +/** + * Get all of the names of the enumerated type values. + * + * @param allNames + * A vector that is OUTPUT containing all of the names of the enumerated values. + * @param isSorted + * If true, the names are sorted in alphabetical order. + */ +void +ChartTwoHistogramContentTypeEnum::getAllNames(std::vector& allNames, const bool isSorted) +{ + if (initializedFlag == false) initialize(); + + allNames.clear(); + + for (std::vector::iterator iter = enumData.begin(); + iter != enumData.end(); + iter++) { + allNames.push_back(ChartTwoHistogramContentTypeEnum::toName(iter->enumValue)); + } + + if (isSorted) { + std::sort(allNames.begin(), allNames.end()); + } +} + +/** + * Get all of the GUI names of the enumerated type values. + * + * @param allNames + * A vector that is OUTPUT containing all of the GUI names of the enumerated values. + * @param isSorted + * If true, the names are sorted in alphabetical order. + */ +void +ChartTwoHistogramContentTypeEnum::getAllGuiNames(std::vector& allGuiNames, const bool isSorted) +{ + if (initializedFlag == false) initialize(); + + allGuiNames.clear(); + + for (std::vector::iterator iter = enumData.begin(); + iter != enumData.end(); + iter++) { + allGuiNames.push_back(ChartTwoHistogramContentTypeEnum::toGuiName(iter->enumValue)); + } + + if (isSorted) { + std::sort(allGuiNames.begin(), allGuiNames.end()); + } +} + diff --git a/src/Charting/ChartTwoHistogramContentTypeEnum.h b/src/Charting/ChartTwoHistogramContentTypeEnum.h new file mode 100644 index 0000000000000000000000000000000000000000..dff74c9de314c403f5467fbc8ff701ae1edf7690 --- /dev/null +++ b/src/Charting/ChartTwoHistogramContentTypeEnum.h @@ -0,0 +1,104 @@ +#ifndef __CHART_TWO_HISTOGRAM_CONTENT_TYPE_ENUM_H__ +#define __CHART_TWO_HISTOGRAM_CONTENT_TYPE_ENUM_H__ + +/*LICENSE_START*/ +/* + * Copyright (C) 2017 Washington University School of Medicine + * + * 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 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ +/*LICENSE_END*/ + + +#include +#include +#include "AString.h" + +namespace caret { + +class ChartTwoHistogramContentTypeEnum { + +public: + /** + * Enumerated values. + */ + enum Enum { + /** Unsupported */ + HISTOGRAM_CONTENT_TYPE_UNSUPPORTED, + /** Map Data Content */ + HISTOGRAM_CONTENT_TYPE_MAP_DATA + }; + + + ~ChartTwoHistogramContentTypeEnum(); + + static AString toName(Enum enumValue); + + static Enum fromName(const AString& name, bool* isValidOut); + + static AString toGuiName(Enum enumValue); + + static Enum fromGuiName(const AString& guiName, bool* isValidOut); + + static int32_t toIntegerCode(Enum enumValue); + + static Enum fromIntegerCode(const int32_t integerCode, bool* isValidOut); + + static void getAllEnums(std::vector& allEnums); + + static void getAllNames(std::vector& allNames, const bool isSorted); + + static void getAllGuiNames(std::vector& allGuiNames, const bool isSorted); + +private: + ChartTwoHistogramContentTypeEnum(const Enum enumValue, + const AString& name, + const AString& guiName); + + static const ChartTwoHistogramContentTypeEnum* findData(const Enum enumValue); + + /** Holds all instance of enum values and associated metadata */ + static std::vector enumData; + + /** Initialize instances that contain the enum values and metadata */ + static void initialize(); + + /** Indicates instance of enum values and metadata have been initialized */ + static bool initializedFlag; + + /** Auto generated integer codes */ + static int32_t integerCodeCounter; + + /** The enumerated type value for an instance */ + Enum enumValue; + + /** The integer code associated with an enumerated value */ + int32_t integerCode; + + /** The name, a text string that is identical to the enumerated value */ + AString name; + + /** A user-friendly name that is displayed in the GUI */ + AString guiName; +}; + +#ifdef __CHART_TWO_HISTOGRAM_CONTENT_TYPE_ENUM_DECLARE__ +std::vector ChartTwoHistogramContentTypeEnum::enumData; +bool ChartTwoHistogramContentTypeEnum::initializedFlag = false; +int32_t ChartTwoHistogramContentTypeEnum::integerCodeCounter = 0; +#endif // __CHART_TWO_HISTOGRAM_CONTENT_TYPE_ENUM_DECLARE__ + +} // namespace +#endif //__CHART_TWO_HISTOGRAM_CONTENT_TYPE_ENUM_H__ diff --git a/src/Charting/ChartTwoLineSeriesContentTypeEnum.cxx b/src/Charting/ChartTwoLineSeriesContentTypeEnum.cxx new file mode 100644 index 0000000000000000000000000000000000000000..51bbae99b9231ecef3baf2c956a9903b1c23b4a8 --- /dev/null +++ b/src/Charting/ChartTwoLineSeriesContentTypeEnum.cxx @@ -0,0 +1,377 @@ + +/*LICENSE_START*/ +/* + * Copyright (C) 2017 Washington University School of Medicine + * + * 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 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ +/*LICENSE_END*/ + +#include +#define __CHART_TWO_LINE_SERIES_CONTENT_TYPE_ENUM_DECLARE__ +#include "ChartTwoLineSeriesContentTypeEnum.h" +#undef __CHART_TWO_LINE_SERIES_CONTENT_TYPE_ENUM_DECLARE__ + +#include "CaretAssert.h" + +using namespace caret; + + +/** + * \class caret::ChartTwoLineSeriesContentTypeEnum + * \brief Type of content for line series charting. + * + * Using this enumerated type in the GUI with an EnumComboBoxTemplate + * + * Header File (.h) + * Forward declare the data type: + * class EnumComboBoxTemplate; + * + * Declare the member: + * EnumComboBoxTemplate* m_chartTwoLineSeriesContentTypeEnumComboBox; + * + * Declare a slot that is called when user changes selection + * private slots: + * void chartTwoLineSeriesContentTypeEnumComboBoxItemActivated(); + * + * Implementation File (.cxx) + * Include the header files + * #include "EnumComboBoxTemplate.h" + * #include "ChartTwoLineSeriesContentTypeEnum.h" + * + * Instatiate: + * m_chartTwoLineSeriesContentTypeEnumComboBox = new EnumComboBoxTemplate(this); + * m_chartTwoLineSeriesContentTypeEnumComboBox->setup(); + * + * Get notified when the user changes the selection: + * QObject::connect(m_chartTwoLineSeriesContentTypeEnumComboBox, SIGNAL(itemActivated()), + * this, SLOT(chartTwoLineSeriesContentTypeEnumComboBoxItemActivated())); + * + * Update the selection: + * m_chartTwoLineSeriesContentTypeEnumComboBox->setSelectedItem(NEW_VALUE); + * + * Read the selection: + * const ChartTwoLineSeriesContentTypeEnum::Enum VARIABLE = m_chartTwoLineSeriesContentTypeEnumComboBox->getSelectedItem(); + * + */ + +/** + * Constructor. + * + * @param enumValue + * An enumerated value. + * @param name + * Name of enumerated value. + * + * @param guiName + * User-friendly name for use in user-interface. + */ +ChartTwoLineSeriesContentTypeEnum::ChartTwoLineSeriesContentTypeEnum(const Enum enumValue, + const AString& name, + const AString& guiName) +{ + this->enumValue = enumValue; + this->integerCode = integerCodeCounter++; + this->name = name; + this->guiName = guiName; +} + +/** + * Destructor. + */ +ChartTwoLineSeriesContentTypeEnum::~ChartTwoLineSeriesContentTypeEnum() +{ +} + +/** + * Initialize the enumerated metadata. + */ +void +ChartTwoLineSeriesContentTypeEnum::initialize() +{ + if (initializedFlag) { + return; + } + initializedFlag = true; + + enumData.push_back(ChartTwoLineSeriesContentTypeEnum(LINE_SERIES_CONTENT_UNSUPPORTED, + "LINE_SERIES_CONTENT_UNSUPPORTED", + "Unsupported")); + + enumData.push_back(ChartTwoLineSeriesContentTypeEnum(LINE_SERIES_CONTENT_BRAINORDINATE_DATA, + "LINE_SERIES_CONTENT_BRAINORDINATE_DATA", + "Brainordinate Data")); + + enumData.push_back(ChartTwoLineSeriesContentTypeEnum(LINE_SERIES_CONTENT_ROW_SCALAR_DATA, + "LINE_SERIES_CONTENT_ROW_SCALAR_DATA", + "Row Scalar Data")); + +} + +/** + * Find the data for and enumerated value. + * @param enumValue + * The enumerated value. + * @return Pointer to data for this enumerated type + * or NULL if no data for type or if type is invalid. + */ +const ChartTwoLineSeriesContentTypeEnum* +ChartTwoLineSeriesContentTypeEnum::findData(const Enum enumValue) +{ + if (initializedFlag == false) initialize(); + + size_t num = enumData.size(); + for (size_t i = 0; i < num; i++) { + const ChartTwoLineSeriesContentTypeEnum* d = &enumData[i]; + if (d->enumValue == enumValue) { + return d; + } + } + + return NULL; +} + +/** + * Get a string representation of the enumerated type. + * @param enumValue + * Enumerated value. + * @return + * String representing enumerated value. + */ +AString +ChartTwoLineSeriesContentTypeEnum::toName(Enum enumValue) { + if (initializedFlag == false) initialize(); + + const ChartTwoLineSeriesContentTypeEnum* enumInstance = findData(enumValue); + return enumInstance->name; +} + +/** + * Get an enumerated value corresponding to its name. + * @param name + * Name of enumerated value. + * @param isValidOut + * If not NULL, it is set indicating that a + * enum value exists for the input name. + * @return + * Enumerated value. + */ +ChartTwoLineSeriesContentTypeEnum::Enum +ChartTwoLineSeriesContentTypeEnum::fromName(const AString& name, bool* isValidOut) +{ + if (initializedFlag == false) initialize(); + + bool validFlag = false; + Enum enumValue = ChartTwoLineSeriesContentTypeEnum::enumData[0].enumValue; + + for (std::vector::iterator iter = enumData.begin(); + iter != enumData.end(); + iter++) { + const ChartTwoLineSeriesContentTypeEnum& d = *iter; + if (d.name == name) { + enumValue = d.enumValue; + validFlag = true; + break; + } + } + + if (isValidOut != 0) { + *isValidOut = validFlag; + } + else if (validFlag == false) { + CaretAssertMessage(0, AString("Name " + name + "failed to match enumerated value for type ChartTwoLineSeriesContentTypeEnum")); + } + return enumValue; +} + +/** + * Get a GUI string representation of the enumerated type. + * @param enumValue + * Enumerated value. + * @return + * String representing enumerated value. + */ +AString +ChartTwoLineSeriesContentTypeEnum::toGuiName(Enum enumValue) { + if (initializedFlag == false) initialize(); + + const ChartTwoLineSeriesContentTypeEnum* enumInstance = findData(enumValue); + return enumInstance->guiName; +} + +/** + * Get an enumerated value corresponding to its GUI name. + * @param s + * Name of enumerated value. + * @param isValidOut + * If not NULL, it is set indicating that a + * enum value exists for the input name. + * @return + * Enumerated value. + */ +ChartTwoLineSeriesContentTypeEnum::Enum +ChartTwoLineSeriesContentTypeEnum::fromGuiName(const AString& guiName, bool* isValidOut) +{ + if (initializedFlag == false) initialize(); + + bool validFlag = false; + Enum enumValue = ChartTwoLineSeriesContentTypeEnum::enumData[0].enumValue; + + for (std::vector::iterator iter = enumData.begin(); + iter != enumData.end(); + iter++) { + const ChartTwoLineSeriesContentTypeEnum& d = *iter; + if (d.guiName == guiName) { + enumValue = d.enumValue; + validFlag = true; + break; + } + } + + if (isValidOut != 0) { + *isValidOut = validFlag; + } + else if (validFlag == false) { + CaretAssertMessage(0, AString("guiName " + guiName + "failed to match enumerated value for type ChartTwoLineSeriesContentTypeEnum")); + } + return enumValue; +} + +/** + * Get the integer code for a data type. + * + * @return + * Integer code for data type. + */ +int32_t +ChartTwoLineSeriesContentTypeEnum::toIntegerCode(Enum enumValue) +{ + if (initializedFlag == false) initialize(); + const ChartTwoLineSeriesContentTypeEnum* enumInstance = findData(enumValue); + return enumInstance->integerCode; +} + +/** + * Find the data type corresponding to an integer code. + * + * @param integerCode + * Integer code for enum. + * @param isValidOut + * If not NULL, on exit isValidOut will indicate if + * integer code is valid. + * @return + * Enum for integer code. + */ +ChartTwoLineSeriesContentTypeEnum::Enum +ChartTwoLineSeriesContentTypeEnum::fromIntegerCode(const int32_t integerCode, bool* isValidOut) +{ + if (initializedFlag == false) initialize(); + + bool validFlag = false; + Enum enumValue = ChartTwoLineSeriesContentTypeEnum::enumData[0].enumValue; + + for (std::vector::iterator iter = enumData.begin(); + iter != enumData.end(); + iter++) { + const ChartTwoLineSeriesContentTypeEnum& enumInstance = *iter; + if (enumInstance.integerCode == integerCode) { + enumValue = enumInstance.enumValue; + validFlag = true; + break; + } + } + + if (isValidOut != 0) { + *isValidOut = validFlag; + } + else if (validFlag == false) { + CaretAssertMessage(0, AString("Integer code " + AString::number(integerCode) + "failed to match enumerated value for type ChartTwoLineSeriesContentTypeEnum")); + } + return enumValue; +} + +/** + * Get all of the enumerated type values. The values can be used + * as parameters to toXXX() methods to get associated metadata. + * + * @param allEnums + * A vector that is OUTPUT containing all of the enumerated values. + */ +void +ChartTwoLineSeriesContentTypeEnum::getAllEnums(std::vector& allEnums) +{ + if (initializedFlag == false) initialize(); + + allEnums.clear(); + + for (std::vector::iterator iter = enumData.begin(); + iter != enumData.end(); + iter++) { + allEnums.push_back(iter->enumValue); + } +} + +/** + * Get all of the names of the enumerated type values. + * + * @param allNames + * A vector that is OUTPUT containing all of the names of the enumerated values. + * @param isSorted + * If true, the names are sorted in alphabetical order. + */ +void +ChartTwoLineSeriesContentTypeEnum::getAllNames(std::vector& allNames, const bool isSorted) +{ + if (initializedFlag == false) initialize(); + + allNames.clear(); + + for (std::vector::iterator iter = enumData.begin(); + iter != enumData.end(); + iter++) { + allNames.push_back(ChartTwoLineSeriesContentTypeEnum::toName(iter->enumValue)); + } + + if (isSorted) { + std::sort(allNames.begin(), allNames.end()); + } +} + +/** + * Get all of the GUI names of the enumerated type values. + * + * @param allNames + * A vector that is OUTPUT containing all of the GUI names of the enumerated values. + * @param isSorted + * If true, the names are sorted in alphabetical order. + */ +void +ChartTwoLineSeriesContentTypeEnum::getAllGuiNames(std::vector& allGuiNames, const bool isSorted) +{ + if (initializedFlag == false) initialize(); + + allGuiNames.clear(); + + for (std::vector::iterator iter = enumData.begin(); + iter != enumData.end(); + iter++) { + allGuiNames.push_back(ChartTwoLineSeriesContentTypeEnum::toGuiName(iter->enumValue)); + } + + if (isSorted) { + std::sort(allGuiNames.begin(), allGuiNames.end()); + } +} + diff --git a/src/Charting/ChartTwoLineSeriesContentTypeEnum.h b/src/Charting/ChartTwoLineSeriesContentTypeEnum.h new file mode 100644 index 0000000000000000000000000000000000000000..525ada68f76d202bc72b1788917e554295f87b2c --- /dev/null +++ b/src/Charting/ChartTwoLineSeriesContentTypeEnum.h @@ -0,0 +1,106 @@ +#ifndef __CHART_TWO_LINE_SERIES_CONTENT_TYPE_ENUM_H__ +#define __CHART_TWO_LINE_SERIES_CONTENT_TYPE_ENUM_H__ + +/*LICENSE_START*/ +/* + * Copyright (C) 2017 Washington University School of Medicine + * + * 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 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ +/*LICENSE_END*/ + + +#include +#include +#include "AString.h" + +namespace caret { + +class ChartTwoLineSeriesContentTypeEnum { + +public: + /** + * Enumerated values. + */ + enum Enum { + /** Unsupported */ + LINE_SERIES_CONTENT_UNSUPPORTED, + /** Brainordinate Data Content */ + LINE_SERIES_CONTENT_BRAINORDINATE_DATA, + /** Row Scalars Content */ + LINE_SERIES_CONTENT_ROW_SCALAR_DATA + }; + + + ~ChartTwoLineSeriesContentTypeEnum(); + + static AString toName(Enum enumValue); + + static Enum fromName(const AString& name, bool* isValidOut); + + static AString toGuiName(Enum enumValue); + + static Enum fromGuiName(const AString& guiName, bool* isValidOut); + + static int32_t toIntegerCode(Enum enumValue); + + static Enum fromIntegerCode(const int32_t integerCode, bool* isValidOut); + + static void getAllEnums(std::vector& allEnums); + + static void getAllNames(std::vector& allNames, const bool isSorted); + + static void getAllGuiNames(std::vector& allGuiNames, const bool isSorted); + +private: + ChartTwoLineSeriesContentTypeEnum(const Enum enumValue, + const AString& name, + const AString& guiName); + + static const ChartTwoLineSeriesContentTypeEnum* findData(const Enum enumValue); + + /** Holds all instance of enum values and associated metadata */ + static std::vector enumData; + + /** Initialize instances that contain the enum values and metadata */ + static void initialize(); + + /** Indicates instance of enum values and metadata have been initialized */ + static bool initializedFlag; + + /** Auto generated integer codes */ + static int32_t integerCodeCounter; + + /** The enumerated type value for an instance */ + Enum enumValue; + + /** The integer code associated with an enumerated value */ + int32_t integerCode; + + /** The name, a text string that is identical to the enumerated value */ + AString name; + + /** A user-friendly name that is displayed in the GUI */ + AString guiName; +}; + +#ifdef __CHART_TWO_LINE_SERIES_CONTENT_TYPE_ENUM_DECLARE__ +std::vector ChartTwoLineSeriesContentTypeEnum::enumData; +bool ChartTwoLineSeriesContentTypeEnum::initializedFlag = false; +int32_t ChartTwoLineSeriesContentTypeEnum::integerCodeCounter = 0; +#endif // __CHART_TWO_LINE_SERIES_CONTENT_TYPE_ENUM_DECLARE__ + +} // namespace +#endif //__CHART_TWO_LINE_SERIES_CONTENT_TYPE_ENUM_H__ diff --git a/src/Charting/ChartTwoLineSeriesHistory.cxx b/src/Charting/ChartTwoLineSeriesHistory.cxx new file mode 100644 index 0000000000000000000000000000000000000000..7ea1038348afb77f6b4da48a6bd53936c33cf350 --- /dev/null +++ b/src/Charting/ChartTwoLineSeriesHistory.cxx @@ -0,0 +1,619 @@ + +/*LICENSE_START*/ +/* + * Copyright (C) 2016 Washington University School of Medicine + * + * 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 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ +/*LICENSE_END*/ + +#define __CHART_TWO_LINE_SERIES_HISTORY_DECLARE__ +#include "ChartTwoLineSeriesHistory.h" +#undef __CHART_TWO_LINE_SERIES_HISTORY_DECLARE__ + +#include "BoundingBox.h" +#include "CaretAssert.h" +#include "CaretLogger.h" +#include "ChartTwoDataCartesian.h" +#include "SceneAttributes.h" +#include "SceneClass.h" +#include "SceneClassAssistant.h" + +using namespace caret; + + + +/** + * \class caret::ChartTwoLineSeriesHistory + * \brief Contains history of line series chart for a tab. + * \ingroup Charting + */ + +/** + * Constructor. + */ +ChartTwoLineSeriesHistory::ChartTwoLineSeriesHistory(const GraphicsPrimitive::PrimitiveType defaultGraphicsPrimitiveType) +: CaretObjectTracksModification(), +SceneableInterface(), +m_defaultGraphicsPrimitiveType(defaultGraphicsPrimitiveType) +{ + initializeInstance(); +} + +/** + * Destructor. + */ +ChartTwoLineSeriesHistory::~ChartTwoLineSeriesHistory() +{ + clearHistory(); + delete m_sceneAssistant; +} + +/** + * Copy constructor. + * @param obj + * Object that is copied. + */ +ChartTwoLineSeriesHistory::ChartTwoLineSeriesHistory(const ChartTwoLineSeriesHistory& obj) +: CaretObjectTracksModification(obj), +SceneableInterface(obj), +m_defaultGraphicsPrimitiveType(obj.m_defaultGraphicsPrimitiveType) +{ + initializeInstance(); + this->copyHelperChartTwoLineSeriesHistory(obj); +} + +/** + * Assignment operator. + * @param obj + * Data copied from obj to this. + * @return + * Reference to this object. + */ +ChartTwoLineSeriesHistory& +ChartTwoLineSeriesHistory::operator=(const ChartTwoLineSeriesHistory& obj) +{ + if (this != &obj) { + CaretObjectTracksModification::operator=(obj); + this->copyHelperChartTwoLineSeriesHistory(obj); + } + return *this; +} + +bool +ChartTwoLineSeriesHistory::isLoadingEnabled() const +{ + return m_loadingEnabled; +} + +/** + * @return Default type of graphics primitive for this line-series history. + */ +GraphicsPrimitive::PrimitiveType +ChartTwoLineSeriesHistory::getDefaultGraphicsPrimitiveType() const +{ + return m_defaultGraphicsPrimitiveType; +} + +/** + * Set loading line series charts enabled + * + * @param loadingEnabled + * New value for loading line series charts enabled + */ +void +ChartTwoLineSeriesHistory::setLoadingEnabled(const bool loadingEnabled) +{ + m_loadingEnabled = loadingEnabled; +} + +/** + * Generate the default color. + */ +CaretColorEnum::Enum +ChartTwoLineSeriesHistory::generateDefaultColor() +{ + /* + * No black or white since they are used for backgrounds + */ + std::vector colors; + CaretColorEnum::getColorEnumsNoBlackOrWhite(colors); + CaretAssert( ! colors.empty()); + CaretColorEnum::Enum color = colors[0]; + + const int32_t numColors = static_cast(colors.size()); + CaretAssert(numColors > 0); + if (s_defaultColorIndexGenerator < 0) { + s_defaultColorIndexGenerator = 0; + } + else if (s_defaultColorIndexGenerator >= numColors) { + s_defaultColorIndexGenerator = 0; + } + + CaretAssertVectorIndex(colors, s_defaultColorIndexGenerator); + color = colors[s_defaultColorIndexGenerator]; + + /* move to next color */ + ++s_defaultColorIndexGenerator; + + return color; +} + +/** + * Validate the default color. + */ +void +ChartTwoLineSeriesHistory::validateDefaultColor() +{ + std::vector allEnums; + CaretColorEnum::getColorAndOptionalEnums(allEnums, (CaretColorEnum::ColorOptions::OPTION_INCLUDE_CUSTOM_COLOR + || CaretColorEnum::CaretColorEnum::OPTION_INCLUDE_NONE_COLOR)); + if (std::find(allEnums.begin(), + allEnums.end(), + m_defaultColor) == allEnums.end()) { + const AString msg("Default color enum is invalid. Integer value: " + AString::number((int)m_defaultColor)); + CaretLogSevere(msg); + m_defaultColor = CaretColorEnum::RED; + } + + if (m_defaultColor == CaretColorEnum::CUSTOM) { + const AString msg("Default color CUSTOM is not allowed"); + CaretLogSevere(msg); + m_defaultColor = CaretColorEnum::RED; + } + else if (m_defaultColor == CaretColorEnum::NONE) { + const AString msg("Default color NONE is not allowed"); + CaretLogSevere(msg); + m_defaultColor = CaretColorEnum::RED; + } +} + +/** + * Initialize an instance of this class. + */ +void +ChartTwoLineSeriesHistory::initializeInstance() +{ + m_defaultColor = ChartTwoLineSeriesHistory::generateDefaultColor(); + validateDefaultColor(); + + m_defaultLineWidth = ChartTwoDataCartesian::getDefaultLineWidth(); + + const int32_t defaultHistoryCount = 1; + m_loadingEnabled = false; + + m_sceneAssistant = new SceneClassAssistant(); + m_displayCount = defaultHistoryCount; + + m_sceneAssistant->add("m_loadingEnabled", + &m_loadingEnabled); + m_sceneAssistant->add("m_defaultColor", + &m_defaultColor); + m_sceneAssistant->add("m_defaultLineWidth", + &m_defaultLineWidth); + m_sceneAssistant->add("m_displayCount", + &m_displayCount); +} + + +/** + * Helps with copying an object of this type. + * @param obj + * Object that is copied. + */ +void +ChartTwoLineSeriesHistory::copyHelperChartTwoLineSeriesHistory(const ChartTwoLineSeriesHistory& obj) +{ m_loadingEnabled = obj.m_loadingEnabled; + + clearHistory(); + + for (const auto item : m_chartHistory) { + ChartTwoDataCartesian* cartData = item->clone(); + CaretAssert(cartData); + + addHistoryItemNoDefaults(cartData); + } + + m_defaultColor = obj.m_defaultColor; + m_defaultLineWidth = obj.m_defaultLineWidth; + m_displayCount = obj.m_displayCount; + validateDefaultColor(); +} + +/** + * @return The default color. + */ +CaretColorEnum::Enum +ChartTwoLineSeriesHistory::getDefaultColor() const +{ + return m_defaultColor; +} + +/** + * Set the default color. + * + * @param defaultColor New value for default color. + */ +void +ChartTwoLineSeriesHistory::setDefaultColor(const CaretColorEnum::Enum defaultColor) +{ + if (defaultColor != m_defaultColor) { + m_defaultColor = defaultColor; + validateDefaultColor(); + setModified(); + } +} + +/** + * @return Default width of lines + */ +float +ChartTwoLineSeriesHistory::getDefaultLineWidth() const +{ + return m_defaultLineWidth; +} + +/** + * Set Default width of lines + * + * @param defaultLineWidth + * New value for Default width of lines + */ +void +ChartTwoLineSeriesHistory::setDefaultLineWidth(const float defaultLineWidth) +{ + if (defaultLineWidth != m_defaultLineWidth) { + m_defaultLineWidth = defaultLineWidth; + setModified(); + } +} + +/** + * @return Count of items for display. + */ +int32_t +ChartTwoLineSeriesHistory::getDisplayCount() const +{ + return m_displayCount; +} + +/** + * Set count of items for display. + * + * @param count + * New value for count. + */ +void +ChartTwoLineSeriesHistory::setDisplayCount(const int32_t count) +{ + if (count != m_displayCount) { + m_displayCount = count; + updateDisplayedHistoryItems(); + setModified(); + } +} + +/** + * Update the number of displayed history items. + */ +void ChartTwoLineSeriesHistory::updateDisplayedHistoryItems() +{ + int32_t itemsDisplayedCount = 0; + const int32_t historyCount = getHistoryCount(); + for (int32_t i = 0; i < historyCount; i++) { + ChartTwoDataCartesian* item = getHistoryItem(i); + if (item->isSelected()) { + itemsDisplayedCount++; + if (itemsDisplayedCount > m_displayCount) { + item->setSelected(false); + } + } + } + + /* + * Limit the number of history items to + * avoid excessive memory usage + */ + const int32_t maximumItems = std::max(m_displayCount, + s_maximumRetainedHistoryCount); + if (historyCount > maximumItems) { + for (int32_t i = maximumItems; i < historyCount; i++) { + CaretAssertVectorIndex(m_chartHistory, i); + CaretAssert(m_chartHistory[i]); + delete m_chartHistory[i]; + m_chartHistory[i]= NULL; + } + + m_chartHistory.resize(maximumItems); + } +} + +/** + * @return The maximum number of history items that are + * retained. + */ +int32_t +ChartTwoLineSeriesHistory::getMaximumRetainedHistoryCount() +{ + return s_maximumRetainedHistoryCount; +} + + +/** + * @return Count of items in history. + */ +int32_t +ChartTwoLineSeriesHistory::getHistoryCount() const +{ + return m_chartHistory.size(); +} + +/** + * Add a history item. This instance takes ownership of the item + * and will delete it. Defaults such as line color and width assigned to item. + * + * @param historyItem + * Added to history. + */ +void +ChartTwoLineSeriesHistory::addHistoryItem(ChartTwoDataCartesian* historyItem) +{ + CaretAssert(historyItem); + historyItem->setColor(m_defaultColor); + historyItem->setLineWidth(m_defaultLineWidth); + addHistoryItemNoDefaults(historyItem); + updateDisplayedHistoryItems(); +} + +/** + * Add a history item. No defaults are applied to the history items. + * + * @param historyItem + * Added to history. + */ +void +ChartTwoLineSeriesHistory::addHistoryItemNoDefaults(ChartTwoDataCartesian* historyItem) +{ + CaretAssert(historyItem); + m_chartHistory.push_front(historyItem); +} + +/** + * @return History item at the given index. + * + * @param index + * Index of the item. + */ +ChartTwoDataCartesian* +ChartTwoLineSeriesHistory::getHistoryItem(const int32_t index) +{ + CaretAssertVectorIndex(m_chartHistory, index); + return m_chartHistory[index]; +} + +/** + * @return History item at the given index (const method). + * + * @param index + * Index of the item. + */ +const ChartTwoDataCartesian* +ChartTwoLineSeriesHistory::getHistoryItem(const int32_t index) const +{ + CaretAssertVectorIndex(m_chartHistory, index); + return m_chartHistory[index]; +} + +void +ChartTwoLineSeriesHistory::removeHistoryItem(const int32_t index) +{ + CaretAssertVectorIndex(m_chartHistory, index); + delete m_chartHistory[index]; + m_chartHistory.erase(m_chartHistory.begin() + index); +} + +/** + * @return Clear the history. + */ +void +ChartTwoLineSeriesHistory::clearHistory() +{ + for (auto item : m_chartHistory) { + delete item; + } + m_chartHistory.clear(); +} + + +/** + * Get a description of this object's content. + * @return String describing this object's content. + */ +AString +ChartTwoLineSeriesHistory::toString() const +{ + return "ChartTwoLineSeriesHistory"; +} + +/** + * @return Is this instance modified? + */ +bool +ChartTwoLineSeriesHistory::isModified() const +{ + if (CaretObjectTracksModification::isModified()) { + return true; + } + + for (const auto item : m_chartHistory) { + if (item->isModified()) { + return true; + } + } + + return false; +} + +/** + * Clear modified status for this instance. + */ +void +ChartTwoLineSeriesHistory::clearModified() +{ + for (const auto item : m_chartHistory) { + item->clearModified(); + } +} + +/** + * Get a bounding box for data displayed within this overlay. + * Bounds are provided for histogram and line-series charts only. + * + * @param boundingBox + * Upon exit contains bounds for data within this overlay + * @return + * True if the bounds are valid, else false. + */ +bool +ChartTwoLineSeriesHistory::getBounds(BoundingBox& boundingBoxOut) const +{ + boundingBoxOut.resetForUpdate(); + + bool validFlag = false; + + for (const auto data : m_chartHistory) { + CaretAssert(data); + if (data->isSelected()) { + BoundingBox boundingBox; + if (data->getBounds(boundingBox)) { + boundingBoxOut.update(boundingBox.getMinX(), + boundingBox.getMinY(), + boundingBox.getMinZ()); + boundingBoxOut.update(boundingBox.getMaxX(), + boundingBox.getMaxY(), + boundingBox.getMaxZ()); + validFlag = true; + } + } + } + + return validFlag; +} + +/** + * Save information specific to this type of model to the scene. + * + * @param sceneAttributes + * Attributes for the scene. Scenes may be of different types + * (full, generic, etc) and the attributes should be checked when + * saving the scene. + * + * @param instanceName + * Name of instance in the scene. + */ +SceneClass* +ChartTwoLineSeriesHistory::saveToScene(const SceneAttributes* sceneAttributes, + const AString& instanceName) +{ + SceneClass* sceneClass = new SceneClass(instanceName, + "ChartTwoLineSeriesHistory", + 1); + m_sceneAssistant->saveMembers(sceneAttributes, + sceneClass); + + /* + * Save chart history but only the displayed items + */ + const int32_t numHistory = static_cast(m_chartHistory.size()); + if (numHistory > 0) { + SceneObjectMapIntegerKey* chartHistoryMap = new SceneObjectMapIntegerKey("m_chartHistoryMap", + SceneObjectDataTypeEnum::SCENE_CLASS); + for (int32_t i = 0; i < numHistory; i++) { + CaretAssertVectorIndex(m_chartHistory, i); + if (m_chartHistory[i]->isSelected()) { + chartHistoryMap->addClass(i, + m_chartHistory[i]->saveToScene(sceneAttributes, + "m_chartHistory")); + } + } + + if (chartHistoryMap->isEmpty()) { + delete chartHistoryMap; + chartHistoryMap = NULL; + } + else { + sceneClass->addChild(chartHistoryMap); + } + } + + // Uncomment if sub-classes must save to scene + //saveSubClassDataToScene(sceneAttributes, + // sceneClass); + + return sceneClass; +} + +/** + * Restore information specific to the type of model from the scene. + * + * @param sceneAttributes + * Attributes for the scene. Scenes may be of different types + * (full, generic, etc) and the attributes should be checked when + * restoring the scene. + * + * @param sceneClass + * sceneClass from which model specific information is obtained. + */ +void +ChartTwoLineSeriesHistory::restoreFromScene(const SceneAttributes* sceneAttributes, + const SceneClass* sceneClass) +{ + if (sceneClass == NULL) { + return; + } + + clearHistory(); + + m_sceneAssistant->restoreMembers(sceneAttributes, + sceneClass); + validateDefaultColor(); + + /* + * Restore chart matrix properties + * Need to reverse keys since items are added on at beginning + */ + const SceneObjectMapIntegerKey* chartHistoryMap = sceneClass->getMapIntegerKey("m_chartHistoryMap"); + if (chartHistoryMap != NULL) { + std::vector indices = chartHistoryMap->getKeys(); + std::reverse(indices.begin(), indices.end()); + for (auto itemIndex : indices) { + const SceneClass* historyClass = chartHistoryMap->classValue(itemIndex); + ChartTwoDataCartesian* historyItem = new ChartTwoDataCartesian(ChartTwoDataTypeEnum::CHART_DATA_TYPE_LINE_SERIES, + CaretUnitsTypeEnum::NONE, + CaretUnitsTypeEnum::NONE, + m_defaultGraphicsPrimitiveType); + historyItem->restoreFromScene(sceneAttributes, + historyClass); + + addHistoryItemNoDefaults(historyItem); + } + } + + //Uncomment if sub-classes must restore from scene + //restoreSubClassDataFromScene(sceneAttributes, + // sceneClass); + +} + diff --git a/src/Charting/ChartTwoLineSeriesHistory.h b/src/Charting/ChartTwoLineSeriesHistory.h new file mode 100644 index 0000000000000000000000000000000000000000..24231f40013b0b707696c1828fc29aa8e89841ae --- /dev/null +++ b/src/Charting/ChartTwoLineSeriesHistory.h @@ -0,0 +1,149 @@ +#ifndef __CHART_TWO_LINE_SERIES_HISTORY_H__ +#define __CHART_TWO_LINE_SERIES_HISTORY_H__ + +/*LICENSE_START*/ +/* + * Copyright (C) 2016 Washington University School of Medicine + * + * 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 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ +/*LICENSE_END*/ + +#include + +#include "BrainConstants.h" +#include "CaretColorEnum.h" +#include "CaretObjectTracksModification.h" +#include "GraphicsPrimitive.h" +#include "SceneableInterface.h" + + +namespace caret { + class BoundingBox; + class ChartTwoDataCartesian; + class SceneClassAssistant; + + class ChartTwoLineSeriesHistory : public CaretObjectTracksModification, public SceneableInterface { + + public: + ChartTwoLineSeriesHistory(const GraphicsPrimitive::PrimitiveType defaultGraphicsPrimitiveType); + + virtual ~ChartTwoLineSeriesHistory(); + + ChartTwoLineSeriesHistory(const ChartTwoLineSeriesHistory& obj); + + ChartTwoLineSeriesHistory& operator=(const ChartTwoLineSeriesHistory& obj); + + GraphicsPrimitive::PrimitiveType getDefaultGraphicsPrimitiveType() const; + + bool isLoadingEnabled() const; + + void setLoadingEnabled(const bool enabled); + + CaretColorEnum::Enum getDefaultColor() const; + + void setDefaultColor(const CaretColorEnum::Enum defaultColor); + + float getDefaultLineWidth() const; + + void setDefaultLineWidth(const float defaultLineWidth); + + int32_t getDisplayCount() const; + + void setDisplayCount(const int32_t count); + + int32_t getHistoryCount() const; + + void addHistoryItem(ChartTwoDataCartesian* historyItem); + + ChartTwoDataCartesian* getHistoryItem(const int32_t index); + + const ChartTwoDataCartesian* getHistoryItem(const int32_t index) const; + + void removeHistoryItem(const int32_t index); + + void clearHistory(); + + bool getBounds(BoundingBox& boundingBoxOut) const; + + // ADD_NEW_METHODS_HERE + + virtual AString toString() const override; + + virtual SceneClass* saveToScene(const SceneAttributes* sceneAttributes, + const AString& instanceName) override; + + virtual void restoreFromScene(const SceneAttributes* sceneAttributes, + const SceneClass* sceneClass) override; + + virtual bool isModified() const override; + + virtual void clearModified() override; + + static int32_t getMaximumRetainedHistoryCount(); + + +// If there will be sub-classes of this class that need to save +// and restore data from scenes, these pure virtual methods can +// be uncommented to force their implementation by sub-classes. +// protected: +// virtual void saveSubClassDataToScene(const SceneAttributes* sceneAttributes, +// SceneClass* sceneClass) = 0; +// +// virtual void restoreSubClassDataFromScene(const SceneAttributes* sceneAttributes, +// const SceneClass* sceneClass) = 0; + + private: + void copyHelperChartTwoLineSeriesHistory(const ChartTwoLineSeriesHistory& obj); + + void initializeInstance(); + + void updateDisplayedHistoryItems(); + + void addHistoryItemNoDefaults(ChartTwoDataCartesian* historyItem); + + static CaretColorEnum::Enum generateDefaultColor(); + + void validateDefaultColor(); + + const GraphicsPrimitive::PrimitiveType m_defaultGraphicsPrimitiveType; + + bool m_loadingEnabled = false; + + SceneClassAssistant* m_sceneAssistant = NULL; + + std::deque m_chartHistory; + + CaretColorEnum::Enum m_defaultColor = CaretColorEnum::BLUE; + + float m_defaultLineWidth; + + int32_t m_displayCount; + + static int32_t s_defaultColorIndexGenerator; + + static const int32_t s_maximumRetainedHistoryCount; + + // ADD_NEW_MEMBERS_HERE + + }; + +#ifdef __CHART_TWO_LINE_SERIES_HISTORY_DECLARE__ + int32_t ChartTwoLineSeriesHistory::s_defaultColorIndexGenerator = 0; + const int32_t ChartTwoLineSeriesHistory::s_maximumRetainedHistoryCount = 20; +#endif // __CHART_TWO_LINE_SERIES_HISTORY_DECLARE__ + +} // namespace +#endif //__CHART_TWO_LINE_SERIES_HISTORY_H__ diff --git a/src/Charting/ChartTwoMatrixContentTypeEnum.cxx b/src/Charting/ChartTwoMatrixContentTypeEnum.cxx new file mode 100644 index 0000000000000000000000000000000000000000..8eccff67929d0706f86c57455da5ac74db05568c --- /dev/null +++ b/src/Charting/ChartTwoMatrixContentTypeEnum.cxx @@ -0,0 +1,376 @@ + +/*LICENSE_START*/ +/* + * Copyright (C) 2017 Washington University School of Medicine + * + * 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 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ +/*LICENSE_END*/ + +#include +#define __CHART_TWO_MATRIX_CONTENT_TYPE_ENUM_DECLARE__ +#include "ChartTwoMatrixContentTypeEnum.h" +#undef __CHART_TWO_MATRIX_CONTENT_TYPE_ENUM_DECLARE__ + +#include "CaretAssert.h" + +using namespace caret; + + +/** + * \class caret::ChartTwoMatrixContentTypeEnum + * \brief Type of content for matrix charting. + * + * Using this enumerated type in the GUI with an EnumComboBoxTemplate + * + * Header File (.h) + * Forward declare the data type: + * class EnumComboBoxTemplate; + * + * Declare the member: + * EnumComboBoxTemplate* m_chartTwoMatrixDataTypeEnumComboBox; + * + * Declare a slot that is called when user changes selection + * private slots: + * void chartTwoMatrixDataTypeEnumComboBoxItemActivated(); + * + * Implementation File (.cxx) + * Include the header files + * #include "EnumComboBoxTemplate.h" + * #include "ChartTwoMatrixContentTypeEnum.h" + * + * Instatiate: + * m_chartTwoMatrixDataTypeEnumComboBox = new EnumComboBoxTemplate(this); + * m_chartTwoMatrixDataTypeEnumComboBox->setup(); + * + * Get notified when the user changes the selection: + * QObject::connect(m_chartTwoMatrixDataTypeEnumComboBox, SIGNAL(itemActivated()), + * this, SLOT(chartTwoMatrixDataTypeEnumComboBoxItemActivated())); + * + * Update the selection: + * m_chartTwoMatrixDataTypeEnumComboBox->setSelectedItem(NEW_VALUE); + * + * Read the selection: + * const ChartTwoMatrixContentTypeEnum::Enum VARIABLE = m_chartTwoMatrixDataTypeEnumComboBox->getSelectedItem(); + * + */ + +/** + * Constructor. + * + * @param enumValue + * An enumerated value. + * @param name + * Name of enumerated value. + * + * @param guiName + * User-friendly name for use in user-interface. + */ +ChartTwoMatrixContentTypeEnum::ChartTwoMatrixContentTypeEnum(const Enum enumValue, + const AString& name, + const AString& guiName) +{ + this->enumValue = enumValue; + this->integerCode = integerCodeCounter++; + this->name = name; + this->guiName = guiName; +} + +/** + * Destructor. + */ +ChartTwoMatrixContentTypeEnum::~ChartTwoMatrixContentTypeEnum() +{ +} + +/** + * Initialize the enumerated metadata. + */ +void +ChartTwoMatrixContentTypeEnum::initialize() +{ + if (initializedFlag) { + return; + } + initializedFlag = true; + + enumData.push_back(ChartTwoMatrixContentTypeEnum(MATRIX_CONTENT_UNSUPPORTED, + "MATRIX_CONTENT_UNSUPPORTED", + "Unsupported")); + + enumData.push_back(ChartTwoMatrixContentTypeEnum(MATRIX_CONTENT_BRAINORDINATE_MAPPABLE, + "MATRIX_CONTENT_BRAINORDINATE_MAPPABLE", + "Brainordinate Mappable")); + + enumData.push_back(ChartTwoMatrixContentTypeEnum(MATRIX_CONTENT_SCALARS, + "MATRIX_CONTENT_SCALARS", + "Scalars")); +} + +/** + * Find the data for and enumerated value. + * @param enumValue + * The enumerated value. + * @return Pointer to data for this enumerated type + * or NULL if no data for type or if type is invalid. + */ +const ChartTwoMatrixContentTypeEnum* +ChartTwoMatrixContentTypeEnum::findData(const Enum enumValue) +{ + if (initializedFlag == false) initialize(); + + size_t num = enumData.size(); + for (size_t i = 0; i < num; i++) { + const ChartTwoMatrixContentTypeEnum* d = &enumData[i]; + if (d->enumValue == enumValue) { + return d; + } + } + + return NULL; +} + +/** + * Get a string representation of the enumerated type. + * @param enumValue + * Enumerated value. + * @return + * String representing enumerated value. + */ +AString +ChartTwoMatrixContentTypeEnum::toName(Enum enumValue) { + if (initializedFlag == false) initialize(); + + const ChartTwoMatrixContentTypeEnum* enumInstance = findData(enumValue); + return enumInstance->name; +} + +/** + * Get an enumerated value corresponding to its name. + * @param name + * Name of enumerated value. + * @param isValidOut + * If not NULL, it is set indicating that a + * enum value exists for the input name. + * @return + * Enumerated value. + */ +ChartTwoMatrixContentTypeEnum::Enum +ChartTwoMatrixContentTypeEnum::fromName(const AString& name, bool* isValidOut) +{ + if (initializedFlag == false) initialize(); + + bool validFlag = false; + Enum enumValue = ChartTwoMatrixContentTypeEnum::enumData[0].enumValue; + + for (std::vector::iterator iter = enumData.begin(); + iter != enumData.end(); + iter++) { + const ChartTwoMatrixContentTypeEnum& d = *iter; + if (d.name == name) { + enumValue = d.enumValue; + validFlag = true; + break; + } + } + + if (isValidOut != 0) { + *isValidOut = validFlag; + } + else if (validFlag == false) { + CaretAssertMessage(0, AString("Name " + name + "failed to match enumerated value for type ChartTwoMatrixContentTypeEnum")); + } + return enumValue; +} + +/** + * Get a GUI string representation of the enumerated type. + * @param enumValue + * Enumerated value. + * @return + * String representing enumerated value. + */ +AString +ChartTwoMatrixContentTypeEnum::toGuiName(Enum enumValue) { + if (initializedFlag == false) initialize(); + + const ChartTwoMatrixContentTypeEnum* enumInstance = findData(enumValue); + return enumInstance->guiName; +} + +/** + * Get an enumerated value corresponding to its GUI name. + * @param s + * Name of enumerated value. + * @param isValidOut + * If not NULL, it is set indicating that a + * enum value exists for the input name. + * @return + * Enumerated value. + */ +ChartTwoMatrixContentTypeEnum::Enum +ChartTwoMatrixContentTypeEnum::fromGuiName(const AString& guiName, bool* isValidOut) +{ + if (initializedFlag == false) initialize(); + + bool validFlag = false; + Enum enumValue = ChartTwoMatrixContentTypeEnum::enumData[0].enumValue; + + for (std::vector::iterator iter = enumData.begin(); + iter != enumData.end(); + iter++) { + const ChartTwoMatrixContentTypeEnum& d = *iter; + if (d.guiName == guiName) { + enumValue = d.enumValue; + validFlag = true; + break; + } + } + + if (isValidOut != 0) { + *isValidOut = validFlag; + } + else if (validFlag == false) { + CaretAssertMessage(0, AString("guiName " + guiName + "failed to match enumerated value for type ChartTwoMatrixContentTypeEnum")); + } + return enumValue; +} + +/** + * Get the integer code for a data type. + * + * @return + * Integer code for data type. + */ +int32_t +ChartTwoMatrixContentTypeEnum::toIntegerCode(Enum enumValue) +{ + if (initializedFlag == false) initialize(); + const ChartTwoMatrixContentTypeEnum* enumInstance = findData(enumValue); + return enumInstance->integerCode; +} + +/** + * Find the data type corresponding to an integer code. + * + * @param integerCode + * Integer code for enum. + * @param isValidOut + * If not NULL, on exit isValidOut will indicate if + * integer code is valid. + * @return + * Enum for integer code. + */ +ChartTwoMatrixContentTypeEnum::Enum +ChartTwoMatrixContentTypeEnum::fromIntegerCode(const int32_t integerCode, bool* isValidOut) +{ + if (initializedFlag == false) initialize(); + + bool validFlag = false; + Enum enumValue = ChartTwoMatrixContentTypeEnum::enumData[0].enumValue; + + for (std::vector::iterator iter = enumData.begin(); + iter != enumData.end(); + iter++) { + const ChartTwoMatrixContentTypeEnum& enumInstance = *iter; + if (enumInstance.integerCode == integerCode) { + enumValue = enumInstance.enumValue; + validFlag = true; + break; + } + } + + if (isValidOut != 0) { + *isValidOut = validFlag; + } + else if (validFlag == false) { + CaretAssertMessage(0, AString("Integer code " + AString::number(integerCode) + "failed to match enumerated value for type ChartTwoMatrixContentTypeEnum")); + } + return enumValue; +} + +/** + * Get all of the enumerated type values. The values can be used + * as parameters to toXXX() methods to get associated metadata. + * + * @param allEnums + * A vector that is OUTPUT containing all of the enumerated values. + */ +void +ChartTwoMatrixContentTypeEnum::getAllEnums(std::vector& allEnums) +{ + if (initializedFlag == false) initialize(); + + allEnums.clear(); + + for (std::vector::iterator iter = enumData.begin(); + iter != enumData.end(); + iter++) { + allEnums.push_back(iter->enumValue); + } +} + +/** + * Get all of the names of the enumerated type values. + * + * @param allNames + * A vector that is OUTPUT containing all of the names of the enumerated values. + * @param isSorted + * If true, the names are sorted in alphabetical order. + */ +void +ChartTwoMatrixContentTypeEnum::getAllNames(std::vector& allNames, const bool isSorted) +{ + if (initializedFlag == false) initialize(); + + allNames.clear(); + + for (std::vector::iterator iter = enumData.begin(); + iter != enumData.end(); + iter++) { + allNames.push_back(ChartTwoMatrixContentTypeEnum::toName(iter->enumValue)); + } + + if (isSorted) { + std::sort(allNames.begin(), allNames.end()); + } +} + +/** + * Get all of the GUI names of the enumerated type values. + * + * @param allNames + * A vector that is OUTPUT containing all of the GUI names of the enumerated values. + * @param isSorted + * If true, the names are sorted in alphabetical order. + */ +void +ChartTwoMatrixContentTypeEnum::getAllGuiNames(std::vector& allGuiNames, const bool isSorted) +{ + if (initializedFlag == false) initialize(); + + allGuiNames.clear(); + + for (std::vector::iterator iter = enumData.begin(); + iter != enumData.end(); + iter++) { + allGuiNames.push_back(ChartTwoMatrixContentTypeEnum::toGuiName(iter->enumValue)); + } + + if (isSorted) { + std::sort(allGuiNames.begin(), allGuiNames.end()); + } +} + diff --git a/src/Charting/ChartTwoMatrixContentTypeEnum.h b/src/Charting/ChartTwoMatrixContentTypeEnum.h new file mode 100644 index 0000000000000000000000000000000000000000..436277db79fe56a0b42ee18fab67fc892150c32c --- /dev/null +++ b/src/Charting/ChartTwoMatrixContentTypeEnum.h @@ -0,0 +1,106 @@ +#ifndef __CHART_TWO_MATRIX_CONTENT_TYPE_ENUM_H__ +#define __CHART_TWO_MATRIX_CONTENT_TYPE_ENUM_H__ + +/*LICENSE_START*/ +/* + * Copyright (C) 2017 Washington University School of Medicine + * + * 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 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ +/*LICENSE_END*/ + + +#include +#include +#include "AString.h" + +namespace caret { + +class ChartTwoMatrixContentTypeEnum { + +public: + /** + * Enumerated values. + */ + enum Enum { + /** Unsupported */ + MATRIX_CONTENT_UNSUPPORTED, + /** Brainordinate mappable content */ + MATRIX_CONTENT_BRAINORDINATE_MAPPABLE, + /** Scalar content */ + MATRIX_CONTENT_SCALARS + }; + + + ~ChartTwoMatrixContentTypeEnum(); + + static AString toName(Enum enumValue); + + static Enum fromName(const AString& name, bool* isValidOut); + + static AString toGuiName(Enum enumValue); + + static Enum fromGuiName(const AString& guiName, bool* isValidOut); + + static int32_t toIntegerCode(Enum enumValue); + + static Enum fromIntegerCode(const int32_t integerCode, bool* isValidOut); + + static void getAllEnums(std::vector& allEnums); + + static void getAllNames(std::vector& allNames, const bool isSorted); + + static void getAllGuiNames(std::vector& allGuiNames, const bool isSorted); + +private: + ChartTwoMatrixContentTypeEnum(const Enum enumValue, + const AString& name, + const AString& guiName); + + static const ChartTwoMatrixContentTypeEnum* findData(const Enum enumValue); + + /** Holds all instance of enum values and associated metadata */ + static std::vector enumData; + + /** Initialize instances that contain the enum values and metadata */ + static void initialize(); + + /** Indicates instance of enum values and metadata have been initialized */ + static bool initializedFlag; + + /** Auto generated integer codes */ + static int32_t integerCodeCounter; + + /** The enumerated type value for an instance */ + Enum enumValue; + + /** The integer code associated with an enumerated value */ + int32_t integerCode; + + /** The name, a text string that is identical to the enumerated value */ + AString name; + + /** A user-friendly name that is displayed in the GUI */ + AString guiName; +}; + +#ifdef __CHART_TWO_MATRIX_CONTENT_TYPE_ENUM_DECLARE__ +std::vector ChartTwoMatrixContentTypeEnum::enumData; +bool ChartTwoMatrixContentTypeEnum::initializedFlag = false; +int32_t ChartTwoMatrixContentTypeEnum::integerCodeCounter = 0; +#endif // __CHART_TWO_MATRIX_CONTENT_TYPE_ENUM_DECLARE__ + +} // namespace +#endif //__CHART_TWO_MATRIX_CONTENT_TYPE_ENUM_H__ diff --git a/src/Charting/ChartTwoMatrixDisplayProperties.cxx b/src/Charting/ChartTwoMatrixDisplayProperties.cxx new file mode 100644 index 0000000000000000000000000000000000000000..dc896c863f04c75d81adac78b67ec15c9089e307 --- /dev/null +++ b/src/Charting/ChartTwoMatrixDisplayProperties.cxx @@ -0,0 +1,299 @@ + +/*LICENSE_START*/ +/* + * Copyright (C) 2017 Washington University School of Medicine + * + * 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 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ +/*LICENSE_END*/ + +#define __CHART_TWO_MATRIX_DISPLAY_PROPERTIES_DECLARE__ +#include "ChartTwoMatrixDisplayProperties.h" +#undef __CHART_TWO_MATRIX_DISPLAY_PROPERTIES_DECLARE__ + +#include "CaretAssert.h" +#include "EventManager.h" +#include "SceneClass.h" +#include "SceneClassAssistant.h" + +using namespace caret; + + + +/** + * \class caret::ChartTwoMatrixDisplayProperties + * \brief Display properties for version two of Matrix charting. + * \ingroup Charting + */ + +/** + * Constructor. + */ +ChartTwoMatrixDisplayProperties::ChartTwoMatrixDisplayProperties() +: CaretObject(), +EventListenerInterface(), +SceneableInterface() +{ + initializeInstance(); +} + +/** + * Destructor. + */ +ChartTwoMatrixDisplayProperties::~ChartTwoMatrixDisplayProperties() +{ + EventManager::get()->removeAllEventsFromListener(this); + delete m_sceneAssistant; +} + +/** + * Initialize a new instance of this class. + */ +void +ChartTwoMatrixDisplayProperties::initializeInstance() +{ + resetPropertiesToDefault(); + m_sceneAssistant = new SceneClassAssistant(); + m_sceneAssistant->add("m_cellPercentageZoomWidth", + &m_cellPercentageZoomWidth); + m_sceneAssistant->add("m_cellPercentageZoomHeight", + &m_cellPercentageZoomHeight); + m_sceneAssistant->add("m_displayGridLinesFlag", + &m_displayGridLinesFlag); + m_sceneAssistant->add("m_highlightSelectedRowColumnFlag", + &m_highlightSelectedRowColumnFlag); + + // EventManager::get()->addEventListener(this, EventTypeEnum::); +} + + +/** + * Copy constructor. + * @param obj + * Object that is copied. + */ +ChartTwoMatrixDisplayProperties::ChartTwoMatrixDisplayProperties(const ChartTwoMatrixDisplayProperties& obj) +: CaretObject(obj), +EventListenerInterface(), +SceneableInterface(obj) +{ + initializeInstance(); + + this->copyHelperChartTwoMatrixDisplayProperties(obj); +} + +/** + * Assignment operator. + * @param obj + * Data copied from obj to this. + * @return + * Reference to this object. + */ +ChartTwoMatrixDisplayProperties& +ChartTwoMatrixDisplayProperties::operator=(const ChartTwoMatrixDisplayProperties& obj) +{ + if (this != &obj) { + CaretObject::operator=(obj); + this->copyHelperChartTwoMatrixDisplayProperties(obj); + } + return *this; +} + +/** + * Helps with copying an object of this type. + * @param obj + * Object that is copied. + */ +void +ChartTwoMatrixDisplayProperties::copyHelperChartTwoMatrixDisplayProperties(const ChartTwoMatrixDisplayProperties& obj) +{ + m_cellPercentageZoomWidth = obj.m_cellPercentageZoomWidth; + m_cellPercentageZoomHeight = obj.m_cellPercentageZoomHeight; + m_displayGridLinesFlag = obj.m_displayGridLinesFlag; + m_highlightSelectedRowColumnFlag = obj.m_highlightSelectedRowColumnFlag; +} + +/** + * Get a description of this object's content. + * @return String describing this object's content. + */ +AString +ChartTwoMatrixDisplayProperties::toString() const +{ + return "ChartTwoMatrixDisplayProperties"; +} + +/** + * Receive an event. + * + * @param event + * An event for which this instance is listening. + */ +void +ChartTwoMatrixDisplayProperties::receiveEvent(Event* /*event*/) +{ +} + +/** + * Reset the display properties to their default values. + */ +void +ChartTwoMatrixDisplayProperties::resetPropertiesToDefault() +{ + m_cellPercentageZoomWidth = 100.0; + m_cellPercentageZoomHeight = 100.0; + m_highlightSelectedRowColumnFlag = true; + m_displayGridLinesFlag = false; +} + +/** + * @return Display grid lines status. + */ +bool +ChartTwoMatrixDisplayProperties::isGridLinesDisplayed() const +{ + return m_displayGridLinesFlag; +} + +/** + * Set the grid lines display status. + * + * @param displayGridLines + * New status for display of grid lines. + */ +void +ChartTwoMatrixDisplayProperties::setGridLinesDisplayed(const bool displayGridLines) +{ + m_displayGridLinesFlag = displayGridLines; +} + +/** + * @return highlight selected row/column status. + */ +bool +ChartTwoMatrixDisplayProperties::isSelectedRowColumnHighlighted() const +{ + return m_highlightSelectedRowColumnFlag; +} + +/** + * Set the row/column highlighting status. + * + * @param highlightStatus + * New highlighting status. + */ +void +ChartTwoMatrixDisplayProperties::setSelectedRowColumnHighlighted(const bool highlightStatus) +{ + m_highlightSelectedRowColumnFlag = highlightStatus; +} + +/** + * @return The cell percentage zoom width. + */ +float +ChartTwoMatrixDisplayProperties::getCellPercentageZoomWidth() const +{ + return m_cellPercentageZoomWidth; +} + +/** + * Set the cell percentage zoom width. + * + * @param cellPercentageZoomWidth + * New value for cell percentage zoom width. + */ +void +ChartTwoMatrixDisplayProperties::setCellPercentageZoomWidth(const float cellPercentageZoomWidth) +{ + m_cellPercentageZoomWidth = cellPercentageZoomWidth; +} + +/** + * @return The cell percentage zoom height. + */ +float +ChartTwoMatrixDisplayProperties::getCellPercentageZoomHeight() const +{ + return m_cellPercentageZoomHeight; +} + +/** + * Set the cell percentage zoom height. + * + * @param cellPercentageZoomHeight + * New value for cell percentage zoom height. + */ +void ChartTwoMatrixDisplayProperties::setCellPercentageZoomHeight(const float cellPercentageZoomHeight) +{ + m_cellPercentageZoomHeight = cellPercentageZoomHeight; +} + +/** + * Save information specific to this type of model to the scene. + * + * @param sceneAttributes + * Attributes for the scene. Scenes may be of different types + * (full, generic, etc) and the attributes should be checked when + * saving the scene. + * + * @param instanceName + * Name of instance in the scene. + */ +SceneClass* +ChartTwoMatrixDisplayProperties::saveToScene(const SceneAttributes* sceneAttributes, + const AString& instanceName) +{ + SceneClass* sceneClass = new SceneClass(instanceName, + "ChartTwoMatrixDisplayProperties", + 1); + m_sceneAssistant->saveMembers(sceneAttributes, + sceneClass); + + // Uncomment if sub-classes must save to scene + //saveSubClassDataToScene(sceneAttributes, + // sceneClass); + + return sceneClass; +} + +/** + * Restore information specific to the type of model from the scene. + * + * @param sceneAttributes + * Attributes for the scene. Scenes may be of different types + * (full, generic, etc) and the attributes should be checked when + * restoring the scene. + * + * @param sceneClass + * sceneClass from which model specific information is obtained. + */ +void +ChartTwoMatrixDisplayProperties::restoreFromScene(const SceneAttributes* sceneAttributes, + const SceneClass* sceneClass) +{ + if (sceneClass == NULL) { + return; + } + + m_sceneAssistant->restoreMembers(sceneAttributes, + sceneClass); + + //Uncomment if sub-classes must restore from scene + //restoreSubClassDataFromScene(sceneAttributes, + // sceneClass); + +} + diff --git a/src/Charting/ChartTwoMatrixDisplayProperties.h b/src/Charting/ChartTwoMatrixDisplayProperties.h new file mode 100644 index 0000000000000000000000000000000000000000..19a9c75e5e31f57e9dbd106ae3fb370aca7bbbbf --- /dev/null +++ b/src/Charting/ChartTwoMatrixDisplayProperties.h @@ -0,0 +1,114 @@ +#ifndef __CHART_TWO_MATRIX_DISPLAY_PROPERTIES_H__ +#define __CHART_TWO_MATRIX_DISPLAY_PROPERTIES_H__ + +/*LICENSE_START*/ +/* + * Copyright (C) 2017 Washington University School of Medicine + * + * 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 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ +/*LICENSE_END*/ + + +#include "CaretObject.h" + +#include "EventListenerInterface.h" +#include "SceneableInterface.h" + + +namespace caret { + class SceneClassAssistant; + + class ChartTwoMatrixDisplayProperties : public CaretObject, public EventListenerInterface, public SceneableInterface { + + public: + ChartTwoMatrixDisplayProperties(); + + virtual ~ChartTwoMatrixDisplayProperties(); + + ChartTwoMatrixDisplayProperties(const ChartTwoMatrixDisplayProperties& obj); + + ChartTwoMatrixDisplayProperties& operator=(const ChartTwoMatrixDisplayProperties& obj); + + bool isGridLinesDisplayed() const; + + void setGridLinesDisplayed(const bool displayGridLines); + + void resetPropertiesToDefault(); + + bool isSelectedRowColumnHighlighted() const; + + void setSelectedRowColumnHighlighted(const bool highlightStatus); + + float getCellPercentageZoomWidth() const; + + void setCellPercentageZoomWidth(const float cellPercentageZoomWidth); + + float getCellPercentageZoomHeight() const; + + void setCellPercentageZoomHeight(const float cellPercentageZoomHeight); + + // ADD_NEW_METHODS_HERE + + virtual AString toString() const; + + virtual void receiveEvent(Event* event); + + virtual SceneClass* saveToScene(const SceneAttributes* sceneAttributes, + const AString& instanceName); + + virtual void restoreFromScene(const SceneAttributes* sceneAttributes, + const SceneClass* sceneClass); + + + + + + +// If there will be sub-classes of this class that need to save +// and restore data from scenes, these pure virtual methods can +// be uncommented to force their implementation by sub-classes. +// protected: +// virtual void saveSubClassDataToScene(const SceneAttributes* sceneAttributes, +// SceneClass* sceneClass) = 0; +// +// virtual void restoreSubClassDataFromScene(const SceneAttributes* sceneAttributes, +// const SceneClass* sceneClass) = 0; + + private: + void copyHelperChartTwoMatrixDisplayProperties(const ChartTwoMatrixDisplayProperties& obj); + + void initializeInstance(); + + SceneClassAssistant* m_sceneAssistant; + + float m_cellPercentageZoomHeight; + + float m_cellPercentageZoomWidth; + + bool m_highlightSelectedRowColumnFlag; + + bool m_displayGridLinesFlag; + + // ADD_NEW_MEMBERS_HERE + + }; + +#ifdef __CHART_TWO_MATRIX_DISPLAY_PROPERTIES_DECLARE__ + // +#endif // __CHART_TWO_MATRIX_DISPLAY_PROPERTIES_DECLARE__ + +} // namespace +#endif //__CHART_TWO_MATRIX_DISPLAY_PROPERTIES_H__ diff --git a/src/Charting/ChartTwoMatrixLoadingDimensionEnum.cxx b/src/Charting/ChartTwoMatrixLoadingDimensionEnum.cxx new file mode 100644 index 0000000000000000000000000000000000000000..e365596f35b48b9663c200cf6be8fdfa05c97045 --- /dev/null +++ b/src/Charting/ChartTwoMatrixLoadingDimensionEnum.cxx @@ -0,0 +1,372 @@ + +/*LICENSE_START*/ +/* + * Copyright (C) 2014 Washington University School of Medicine + * + * 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 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ +/*LICENSE_END*/ + +#include +#define __CHART_TWO_MATRIX_LOADING_TYPE_ENUM_DECLARE__ +#include "ChartTwoMatrixLoadingDimensionEnum.h" +#undef __CHART_TWO_MATRIX_LOADING_TYPE_ENUM_DECLARE__ + +#include "CaretAssert.h" + +using namespace caret; + + +/** + * \class caret::ChartTwoMatrixLoadingDimensionEnum + * \brief Enumerated type for loading matrix data by row or column + * + * Using this enumerated type in the GUI with an EnumComboBoxTemplate + * + * Header File (.h) + * Forward declare the data type: + * class EnumComboBoxTemplate; + * + * Declare the member: + * EnumComboBoxTemplate* m_ChartTwoMatrixLoadingDimensionEnumComboBox; + * + * Declare a slot that is called when user changes selection + * private slots: + * void ChartTwoMatrixLoadingDimensionEnumComboBoxItemActivated(); + * + * Implementation File (.cxx) + * Include the header files + * #include "EnumComboBoxTemplate.h" + * #include "ChartTwoMatrixLoadingDimensionEnum.h" + * + * Instatiate: + * m_ChartTwoMatrixLoadingDimensionEnumComboBox = new EnumComboBoxTemplate(this); + * m_ChartTwoMatrixLoadingDimensionEnumComboBox->setup(); + * + * Get notified when the user changes the selection: + * QObject::connect(m_ChartTwoMatrixLoadingDimensionEnumComboBox, SIGNAL(itemActivated()), + * this, SLOT(ChartTwoMatrixLoadingDimensionEnumComboBoxItemActivated())); + * + * Update the selection: + * m_ChartTwoMatrixLoadingDimensionEnumComboBox->setSelectedItem(NEW_VALUE); + * + * Read the selection: + * const ChartTwoMatrixLoadingDimensionEnum::Enum VARIABLE = m_ChartTwoMatrixLoadingDimensionEnumComboBox->getSelectedItem(); + * + */ + +/** + * Constructor. + * + * @param enumValue + * An enumerated value. + * @param name + * Name of enumerated value. + * + * @param guiName + * User-friendly name for use in user-interface. + */ +ChartTwoMatrixLoadingDimensionEnum::ChartTwoMatrixLoadingDimensionEnum(const Enum enumValue, + const AString& name, + const AString& guiName) +{ + this->enumValue = enumValue; + this->integerCode = integerCodeCounter++; + this->name = name; + this->guiName = guiName; +} + +/** + * Destructor. + */ +ChartTwoMatrixLoadingDimensionEnum::~ChartTwoMatrixLoadingDimensionEnum() +{ +} + +/** + * Initialize the enumerated metadata. + */ +void +ChartTwoMatrixLoadingDimensionEnum::initialize() +{ + if (initializedFlag) { + return; + } + initializedFlag = true; + + enumData.push_back(ChartTwoMatrixLoadingDimensionEnum(CHART_MATRIX_LOADING_BY_ROW, + "CHART_MATRIX_LOADING_BY_ROW", + "Row")); + + enumData.push_back(ChartTwoMatrixLoadingDimensionEnum(CHART_MATRIX_LOADING_BY_COLUMN, + "CHART_MATRIX_LOADING_BY_COLUMN", + "Column")); +} + +/** + * Find the data for and enumerated value. + * @param enumValue + * The enumerated value. + * @return Pointer to data for this enumerated type + * or NULL if no data for type or if type is invalid. + */ +const ChartTwoMatrixLoadingDimensionEnum* +ChartTwoMatrixLoadingDimensionEnum::findData(const Enum enumValue) +{ + if (initializedFlag == false) initialize(); + + size_t num = enumData.size(); + for (size_t i = 0; i < num; i++) { + const ChartTwoMatrixLoadingDimensionEnum* d = &enumData[i]; + if (d->enumValue == enumValue) { + return d; + } + } + + return NULL; +} + +/** + * Get a string representation of the enumerated type. + * @param enumValue + * Enumerated value. + * @return + * String representing enumerated value. + */ +AString +ChartTwoMatrixLoadingDimensionEnum::toName(Enum enumValue) { + if (initializedFlag == false) initialize(); + + const ChartTwoMatrixLoadingDimensionEnum* enumInstance = findData(enumValue); + return enumInstance->name; +} + +/** + * Get an enumerated value corresponding to its name. + * @param name + * Name of enumerated value. + * @param isValidOut + * If not NULL, it is set indicating that a + * enum value exists for the input name. + * @return + * Enumerated value. + */ +ChartTwoMatrixLoadingDimensionEnum::Enum +ChartTwoMatrixLoadingDimensionEnum::fromName(const AString& name, bool* isValidOut) +{ + if (initializedFlag == false) initialize(); + + bool validFlag = false; + Enum enumValue = ChartTwoMatrixLoadingDimensionEnum::enumData[0].enumValue; + + for (std::vector::iterator iter = enumData.begin(); + iter != enumData.end(); + iter++) { + const ChartTwoMatrixLoadingDimensionEnum& d = *iter; + if (d.name == name) { + enumValue = d.enumValue; + validFlag = true; + break; + } + } + + if (isValidOut != 0) { + *isValidOut = validFlag; + } + else if (validFlag == false) { + CaretAssertMessage(0, AString("Name " + name + "failed to match enumerated value for type ChartTwoMatrixLoadingDimensionEnum")); + } + return enumValue; +} + +/** + * Get a GUI string representation of the enumerated type. + * @param enumValue + * Enumerated value. + * @return + * String representing enumerated value. + */ +AString +ChartTwoMatrixLoadingDimensionEnum::toGuiName(Enum enumValue) { + if (initializedFlag == false) initialize(); + + const ChartTwoMatrixLoadingDimensionEnum* enumInstance = findData(enumValue); + return enumInstance->guiName; +} + +/** + * Get an enumerated value corresponding to its GUI name. + * @param s + * Name of enumerated value. + * @param isValidOut + * If not NULL, it is set indicating that a + * enum value exists for the input name. + * @return + * Enumerated value. + */ +ChartTwoMatrixLoadingDimensionEnum::Enum +ChartTwoMatrixLoadingDimensionEnum::fromGuiName(const AString& guiName, bool* isValidOut) +{ + if (initializedFlag == false) initialize(); + + bool validFlag = false; + Enum enumValue = ChartTwoMatrixLoadingDimensionEnum::enumData[0].enumValue; + + for (std::vector::iterator iter = enumData.begin(); + iter != enumData.end(); + iter++) { + const ChartTwoMatrixLoadingDimensionEnum& d = *iter; + if (d.guiName == guiName) { + enumValue = d.enumValue; + validFlag = true; + break; + } + } + + if (isValidOut != 0) { + *isValidOut = validFlag; + } + else if (validFlag == false) { + CaretAssertMessage(0, AString("guiName " + guiName + "failed to match enumerated value for type ChartTwoMatrixLoadingDimensionEnum")); + } + return enumValue; +} + +/** + * Get the integer code for a data type. + * + * @return + * Integer code for data type. + */ +int32_t +ChartTwoMatrixLoadingDimensionEnum::toIntegerCode(Enum enumValue) +{ + if (initializedFlag == false) initialize(); + const ChartTwoMatrixLoadingDimensionEnum* enumInstance = findData(enumValue); + return enumInstance->integerCode; +} + +/** + * Find the data type corresponding to an integer code. + * + * @param integerCode + * Integer code for enum. + * @param isValidOut + * If not NULL, on exit isValidOut will indicate if + * integer code is valid. + * @return + * Enum for integer code. + */ +ChartTwoMatrixLoadingDimensionEnum::Enum +ChartTwoMatrixLoadingDimensionEnum::fromIntegerCode(const int32_t integerCode, bool* isValidOut) +{ + if (initializedFlag == false) initialize(); + + bool validFlag = false; + Enum enumValue = ChartTwoMatrixLoadingDimensionEnum::enumData[0].enumValue; + + for (std::vector::iterator iter = enumData.begin(); + iter != enumData.end(); + iter++) { + const ChartTwoMatrixLoadingDimensionEnum& enumInstance = *iter; + if (enumInstance.integerCode == integerCode) { + enumValue = enumInstance.enumValue; + validFlag = true; + break; + } + } + + if (isValidOut != 0) { + *isValidOut = validFlag; + } + else if (validFlag == false) { + CaretAssertMessage(0, AString("Integer code " + AString::number(integerCode) + "failed to match enumerated value for type ChartTwoMatrixLoadingDimensionEnum")); + } + return enumValue; +} + +/** + * Get all of the enumerated type values. The values can be used + * as parameters to toXXX() methods to get associated metadata. + * + * @param allEnums + * A vector that is OUTPUT containing all of the enumerated values. + */ +void +ChartTwoMatrixLoadingDimensionEnum::getAllEnums(std::vector& allEnums) +{ + if (initializedFlag == false) initialize(); + + allEnums.clear(); + + for (std::vector::iterator iter = enumData.begin(); + iter != enumData.end(); + iter++) { + allEnums.push_back(iter->enumValue); + } +} + +/** + * Get all of the names of the enumerated type values. + * + * @param allNames + * A vector that is OUTPUT containing all of the names of the enumerated values. + * @param isSorted + * If true, the names are sorted in alphabetical order. + */ +void +ChartTwoMatrixLoadingDimensionEnum::getAllNames(std::vector& allNames, const bool isSorted) +{ + if (initializedFlag == false) initialize(); + + allNames.clear(); + + for (std::vector::iterator iter = enumData.begin(); + iter != enumData.end(); + iter++) { + allNames.push_back(ChartTwoMatrixLoadingDimensionEnum::toName(iter->enumValue)); + } + + if (isSorted) { + std::sort(allNames.begin(), allNames.end()); + } +} + +/** + * Get all of the GUI names of the enumerated type values. + * + * @param allNames + * A vector that is OUTPUT containing all of the GUI names of the enumerated values. + * @param isSorted + * If true, the names are sorted in alphabetical order. + */ +void +ChartTwoMatrixLoadingDimensionEnum::getAllGuiNames(std::vector& allGuiNames, const bool isSorted) +{ + if (initializedFlag == false) initialize(); + + allGuiNames.clear(); + + for (std::vector::iterator iter = enumData.begin(); + iter != enumData.end(); + iter++) { + allGuiNames.push_back(ChartTwoMatrixLoadingDimensionEnum::toGuiName(iter->enumValue)); + } + + if (isSorted) { + std::sort(allGuiNames.begin(), allGuiNames.end()); + } +} + diff --git a/src/Charting/ChartTwoMatrixLoadingDimensionEnum.h b/src/Charting/ChartTwoMatrixLoadingDimensionEnum.h new file mode 100644 index 0000000000000000000000000000000000000000..758ca595f43014dd4515d984964882162e3fd5e4 --- /dev/null +++ b/src/Charting/ChartTwoMatrixLoadingDimensionEnum.h @@ -0,0 +1,104 @@ +#ifndef __CHART_TWO_MATRIX_LOADING_TYPE_ENUM_H__ +#define __CHART_TWO_MATRIX_LOADING_TYPE_ENUM_H__ + +/*LICENSE_START*/ +/* + * Copyright (C) 2014 Washington University School of Medicine + * + * 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 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ +/*LICENSE_END*/ + + +#include +#include +#include "AString.h" + +namespace caret { + +class ChartTwoMatrixLoadingDimensionEnum { + +public: + /** + * Enumerated values. + */ + enum Enum { + /** Load data from column */ + CHART_MATRIX_LOADING_BY_COLUMN, + /** Load data from row */ + CHART_MATRIX_LOADING_BY_ROW + }; + + + ~ChartTwoMatrixLoadingDimensionEnum(); + + static AString toName(Enum enumValue); + + static Enum fromName(const AString& name, bool* isValidOut); + + static AString toGuiName(Enum enumValue); + + static Enum fromGuiName(const AString& guiName, bool* isValidOut); + + static int32_t toIntegerCode(Enum enumValue); + + static Enum fromIntegerCode(const int32_t integerCode, bool* isValidOut); + + static void getAllEnums(std::vector& allEnums); + + static void getAllNames(std::vector& allNames, const bool isSorted); + + static void getAllGuiNames(std::vector& allGuiNames, const bool isSorted); + +private: + ChartTwoMatrixLoadingDimensionEnum(const Enum enumValue, + const AString& name, + const AString& guiName); + + static const ChartTwoMatrixLoadingDimensionEnum* findData(const Enum enumValue); + + /** Holds all instance of enum values and associated metadata */ + static std::vector enumData; + + /** Initialize instances that contain the enum values and metadata */ + static void initialize(); + + /** Indicates instance of enum values and metadata have been initialized */ + static bool initializedFlag; + + /** Auto generated integer codes */ + static int32_t integerCodeCounter; + + /** The enumerated type value for an instance */ + Enum enumValue; + + /** The integer code associated with an enumerated value */ + int32_t integerCode; + + /** The name, a text string that is identical to the enumerated value */ + AString name; + + /** A user-friendly name that is displayed in the GUI */ + AString guiName; +}; + +#ifdef __CHART_TWO_MATRIX_LOADING_TYPE_ENUM_DECLARE__ +std::vector ChartTwoMatrixLoadingDimensionEnum::enumData; +bool ChartTwoMatrixLoadingDimensionEnum::initializedFlag = false; +int32_t ChartTwoMatrixLoadingDimensionEnum::integerCodeCounter = 0; +#endif // __CHART_TWO_MATRIX_LOADING_TYPE_ENUM_DECLARE__ + +} // namespace +#endif //__CHART_TWO_MATRIX_LOADING_TYPE_ENUM_H__ diff --git a/src/Charting/ChartTwoMatrixTriangularViewingModeEnum.cxx b/src/Charting/ChartTwoMatrixTriangularViewingModeEnum.cxx new file mode 100644 index 0000000000000000000000000000000000000000..0f91af4d74b3d8fd102b79a4816b12b5d94e83d5 --- /dev/null +++ b/src/Charting/ChartTwoMatrixTriangularViewingModeEnum.cxx @@ -0,0 +1,381 @@ + +/*LICENSE_START*/ +/* + * Copyright (C) 2016 Washington University School of Medicine + * + * 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 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ +/*LICENSE_END*/ + +#include + +#define __CHART_TWO_MATRIX_TRIANGULAR_VIEWING_MODE_ENUM_DECLARE__ +#include "ChartTwoMatrixTriangularViewingModeEnum.h" +#undef __CHART_TWO_MATRIX_TRIANGULAR_VIEWING_MODE_ENUM_DECLARE__ + +#include "CaretAssert.h" + +using namespace caret; + + +/** + * \class caret::ChartTwoMatrixTriangularViewingModeEnum + * \brief Types of viewing for matrix. + * + * Using this enumerated type in the GUI with an EnumComboBoxTemplate + * + * Header File (.h) + * Forward declare the data type: + * class EnumComboBoxTemplate; + * + * Declare the member: + * EnumComboBoxTemplate* m_matrixViewingTypeEnumComboBox; + * + * Declare a slot that is called when user changes selection + * private slots: + * void matrixViewingTypeEnumComboBoxItemActivated(); + * + * Implementation File (.cxx) + * Include the header files + * #include "EnumComboBoxTemplate.h" + * #include "ChartTwoMatrixTriangularViewingModeEnum.h" + * + * Instatiate: + * m_matrixViewingTypeEnumComboBox = new EnumComboBoxTemplate(this); + * m_matrixViewingTypeEnumComboBox->setup(); + * + * Get notified when the user changes the selection: + * QObject::connect(m_matrixViewingTypeEnumComboBox, SIGNAL(itemActivated()), + * this, SLOT(matrixViewingTypeEnumComboBoxItemActivated())); + * + * Update the selection: + * m_matrixViewingTypeEnumComboBox->setSelectedItem(NEW_VALUE); + * + * Read the selection: + * const ChartTwoMatrixTriangularViewingModeEnum::Enum VARIABLE = m_matrixViewingTypeEnumComboBox->getSelectedItem(); + * + */ + +/** + * Constructor. + * + * @param enumValue + * An enumerated value. + * @param name + * Name of enumerated value. + * + * @param guiName + * User-friendly name for use in user-interface. + */ +ChartTwoMatrixTriangularViewingModeEnum::ChartTwoMatrixTriangularViewingModeEnum(const Enum enumValue, + const AString& name, + const AString& guiName) +{ + this->enumValue = enumValue; + this->integerCode = integerCodeCounter++; + this->name = name; + this->guiName = guiName; +} + +/** + * Destructor. + */ +ChartTwoMatrixTriangularViewingModeEnum::~ChartTwoMatrixTriangularViewingModeEnum() +{ +} + +/** + * Initialize the enumerated metadata. + */ +void +ChartTwoMatrixTriangularViewingModeEnum::initialize() +{ + if (initializedFlag) { + return; + } + initializedFlag = true; + + enumData.push_back(ChartTwoMatrixTriangularViewingModeEnum(MATRIX_VIEW_FULL, + "MATRIX_VIEW_FULL", + "Full")); + + enumData.push_back(ChartTwoMatrixTriangularViewingModeEnum(MATRIX_VIEW_FULL_NO_DIAGONAL, + "MATRIX_VIEW_FULL_NO_DIAGONAL", + "Full No Diagonal")); + + enumData.push_back(ChartTwoMatrixTriangularViewingModeEnum(MATRIX_VIEW_LOWER_NO_DIAGONAL, + "MATRIX_VIEW_LOWER_NO_DIAGONAL", + "Lower No Diagonal")); + + enumData.push_back(ChartTwoMatrixTriangularViewingModeEnum(MATRIX_VIEW_UPPER_NO_DIAGONAL, + "MATRIX_VIEW_UPPER_NO_DIAGONAL", + "Upper No Diagonal")); +} + +/** + * Find the data for and enumerated value. + * @param enumValue + * The enumerated value. + * @return Pointer to data for this enumerated type + * or NULL if no data for type or if type is invalid. + */ +const ChartTwoMatrixTriangularViewingModeEnum* +ChartTwoMatrixTriangularViewingModeEnum::findData(const Enum enumValue) +{ + if (initializedFlag == false) initialize(); + + size_t num = enumData.size(); + for (size_t i = 0; i < num; i++) { + const ChartTwoMatrixTriangularViewingModeEnum* d = &enumData[i]; + if (d->enumValue == enumValue) { + return d; + } + } + + return NULL; +} + +/** + * Get a string representation of the enumerated type. + * @param enumValue + * Enumerated value. + * @return + * String representing enumerated value. + */ +AString +ChartTwoMatrixTriangularViewingModeEnum::toName(Enum enumValue) { + if (initializedFlag == false) initialize(); + + const ChartTwoMatrixTriangularViewingModeEnum* enumInstance = findData(enumValue); + return enumInstance->name; +} + +/** + * Get an enumerated value corresponding to its name. + * @param name + * Name of enumerated value. + * @param isValidOut + * If not NULL, it is set indicating that a + * enum value exists for the input name. + * @return + * Enumerated value. + */ +ChartTwoMatrixTriangularViewingModeEnum::Enum +ChartTwoMatrixTriangularViewingModeEnum::fromName(const AString& name, bool* isValidOut) +{ + if (initializedFlag == false) initialize(); + + bool validFlag = false; + Enum enumValue = ChartTwoMatrixTriangularViewingModeEnum::enumData[0].enumValue; + + for (std::vector::iterator iter = enumData.begin(); + iter != enumData.end(); + iter++) { + const ChartTwoMatrixTriangularViewingModeEnum& d = *iter; + if (d.name == name) { + enumValue = d.enumValue; + validFlag = true; + break; + } + } + + if (isValidOut != 0) { + *isValidOut = validFlag; + } + else if (validFlag == false) { + CaretAssertMessage(0, AString("Name " + name + "failed to match enumerated value for type ChartTwoMatrixTriangularViewingModeEnum")); + } + return enumValue; +} + +/** + * Get a GUI string representation of the enumerated type. + * @param enumValue + * Enumerated value. + * @return + * String representing enumerated value. + */ +AString +ChartTwoMatrixTriangularViewingModeEnum::toGuiName(Enum enumValue) { + if (initializedFlag == false) initialize(); + + const ChartTwoMatrixTriangularViewingModeEnum* enumInstance = findData(enumValue); + return enumInstance->guiName; +} + +/** + * Get an enumerated value corresponding to its GUI name. + * @param s + * Name of enumerated value. + * @param isValidOut + * If not NULL, it is set indicating that a + * enum value exists for the input name. + * @return + * Enumerated value. + */ +ChartTwoMatrixTriangularViewingModeEnum::Enum +ChartTwoMatrixTriangularViewingModeEnum::fromGuiName(const AString& guiName, bool* isValidOut) +{ + if (initializedFlag == false) initialize(); + + bool validFlag = false; + Enum enumValue = ChartTwoMatrixTriangularViewingModeEnum::enumData[0].enumValue; + + for (std::vector::iterator iter = enumData.begin(); + iter != enumData.end(); + iter++) { + const ChartTwoMatrixTriangularViewingModeEnum& d = *iter; + if (d.guiName == guiName) { + enumValue = d.enumValue; + validFlag = true; + break; + } + } + + if (isValidOut != 0) { + *isValidOut = validFlag; + } + else if (validFlag == false) { + CaretAssertMessage(0, AString("guiName " + guiName + "failed to match enumerated value for type ChartTwoMatrixTriangularViewingModeEnum")); + } + return enumValue; +} + +/** + * Get the integer code for a data type. + * + * @return + * Integer code for data type. + */ +int32_t +ChartTwoMatrixTriangularViewingModeEnum::toIntegerCode(Enum enumValue) +{ + if (initializedFlag == false) initialize(); + const ChartTwoMatrixTriangularViewingModeEnum* enumInstance = findData(enumValue); + return enumInstance->integerCode; +} + +/** + * Find the data type corresponding to an integer code. + * + * @param integerCode + * Integer code for enum. + * @param isValidOut + * If not NULL, on exit isValidOut will indicate if + * integer code is valid. + * @return + * Enum for integer code. + */ +ChartTwoMatrixTriangularViewingModeEnum::Enum +ChartTwoMatrixTriangularViewingModeEnum::fromIntegerCode(const int32_t integerCode, bool* isValidOut) +{ + if (initializedFlag == false) initialize(); + + bool validFlag = false; + Enum enumValue = ChartTwoMatrixTriangularViewingModeEnum::enumData[0].enumValue; + + for (std::vector::iterator iter = enumData.begin(); + iter != enumData.end(); + iter++) { + const ChartTwoMatrixTriangularViewingModeEnum& enumInstance = *iter; + if (enumInstance.integerCode == integerCode) { + enumValue = enumInstance.enumValue; + validFlag = true; + break; + } + } + + if (isValidOut != 0) { + *isValidOut = validFlag; + } + else if (validFlag == false) { + CaretAssertMessage(0, AString("Integer code " + AString::number(integerCode) + "failed to match enumerated value for type ChartTwoMatrixTriangularViewingModeEnum")); + } + return enumValue; +} + +/** + * Get all of the enumerated type values. The values can be used + * as parameters to toXXX() methods to get associated metadata. + * + * @param allEnums + * A vector that is OUTPUT containing all of the enumerated values. + */ +void +ChartTwoMatrixTriangularViewingModeEnum::getAllEnums(std::vector& allEnums) +{ + if (initializedFlag == false) initialize(); + + allEnums.clear(); + + for (std::vector::iterator iter = enumData.begin(); + iter != enumData.end(); + iter++) { + allEnums.push_back(iter->enumValue); + } +} + +/** + * Get all of the names of the enumerated type values. + * + * @param allNames + * A vector that is OUTPUT containing all of the names of the enumerated values. + * @param isSorted + * If true, the names are sorted in alphabetical order. + */ +void +ChartTwoMatrixTriangularViewingModeEnum::getAllNames(std::vector& allNames, const bool isSorted) +{ + if (initializedFlag == false) initialize(); + + allNames.clear(); + + for (std::vector::iterator iter = enumData.begin(); + iter != enumData.end(); + iter++) { + allNames.push_back(ChartTwoMatrixTriangularViewingModeEnum::toName(iter->enumValue)); + } + + if (isSorted) { + std::sort(allNames.begin(), allNames.end()); + } +} + +/** + * Get all of the GUI names of the enumerated type values. + * + * @param allNames + * A vector that is OUTPUT containing all of the GUI names of the enumerated values. + * @param isSorted + * If true, the names are sorted in alphabetical order. + */ +void +ChartTwoMatrixTriangularViewingModeEnum::getAllGuiNames(std::vector& allGuiNames, const bool isSorted) +{ + if (initializedFlag == false) initialize(); + + allGuiNames.clear(); + + for (std::vector::iterator iter = enumData.begin(); + iter != enumData.end(); + iter++) { + allGuiNames.push_back(ChartTwoMatrixTriangularViewingModeEnum::toGuiName(iter->enumValue)); + } + + if (isSorted) { + std::sort(allGuiNames.begin(), allGuiNames.end()); + } +} + diff --git a/src/Charting/ChartTwoMatrixTriangularViewingModeEnum.h b/src/Charting/ChartTwoMatrixTriangularViewingModeEnum.h new file mode 100644 index 0000000000000000000000000000000000000000..ae942295f62fe1cb351b4d53667b93b45330139d --- /dev/null +++ b/src/Charting/ChartTwoMatrixTriangularViewingModeEnum.h @@ -0,0 +1,107 @@ +#ifndef __CHART_TWO_MATRIX_TRIANGULAR_VIEWING_MODE_ENUM_H__ +#define __CHART_TWO_MATRIX_TRIANGULAR_VIEWING_MODE_ENUM_H__ + +/*LICENSE_START*/ +/* + * Copyright (C) 2016 Washington University School of Medicine + * + * 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 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ +/*LICENSE_END*/ + + +#include +#include +#include "AString.h" + +namespace caret { + +class ChartTwoMatrixTriangularViewingModeEnum { + +public: + /** + * Enumerated values. + */ + enum Enum { + /** View the full matrix */ + MATRIX_VIEW_FULL, + /** View the full matrix but hide the diagonal */ + MATRIX_VIEW_FULL_NO_DIAGONAL, + /** View the lower half of the matrix but hide the diagonal */ + MATRIX_VIEW_LOWER_NO_DIAGONAL, + /** View the upper half of the matrix but hide the diagonal */ + MATRIX_VIEW_UPPER_NO_DIAGONAL + }; + + ~ChartTwoMatrixTriangularViewingModeEnum(); + + static AString toName(Enum enumValue); + + static Enum fromName(const AString& name, bool* isValidOut); + + static AString toGuiName(Enum enumValue); + + static Enum fromGuiName(const AString& guiName, bool* isValidOut); + + static int32_t toIntegerCode(Enum enumValue); + + static Enum fromIntegerCode(const int32_t integerCode, bool* isValidOut); + + static void getAllEnums(std::vector& allEnums); + + static void getAllNames(std::vector& allNames, const bool isSorted); + + static void getAllGuiNames(std::vector& allGuiNames, const bool isSorted); + +private: + ChartTwoMatrixTriangularViewingModeEnum(const Enum enumValue, + const AString& name, + const AString& guiName); + + static const ChartTwoMatrixTriangularViewingModeEnum* findData(const Enum enumValue); + + /** Holds all instance of enum values and associated metadata */ + static std::vector enumData; + + /** Initialize instances that contain the enum values and metadata */ + static void initialize(); + + /** Indicates instance of enum values and metadata have been initialized */ + static bool initializedFlag; + + /** Auto generated integer codes */ + static int32_t integerCodeCounter; + + /** The enumerated type value for an instance */ + Enum enumValue; + + /** The integer code associated with an enumerated value */ + int32_t integerCode; + + /** The name, a text string that is identical to the enumerated value */ + AString name; + + /** A user-friendly name that is displayed in the GUI */ + AString guiName; +}; + +#ifdef __CHART_TWO_MATRIX_TRIANGULAR_VIEWING_MODE_ENUM_DECLARE__ +std::vector ChartTwoMatrixTriangularViewingModeEnum::enumData; +bool ChartTwoMatrixTriangularViewingModeEnum::initializedFlag = false; +int32_t ChartTwoMatrixTriangularViewingModeEnum::integerCodeCounter = 0; +#endif // __CHART_TWO_MATRIX_TRIANGULAR_VIEWING_MODE_ENUM_DECLARE__ + +} // namespace +#endif //__CHART_TWO_MATRIX_TRIANGULAR_VIEWING_MODE_ENUM_H__ diff --git a/src/Charting/ChartTwoNumericSubdivisionsModeEnum.cxx b/src/Charting/ChartTwoNumericSubdivisionsModeEnum.cxx new file mode 100644 index 0000000000000000000000000000000000000000..cdffdd5a46a7fe59cb3fa146db49c1cf024927fa --- /dev/null +++ b/src/Charting/ChartTwoNumericSubdivisionsModeEnum.cxx @@ -0,0 +1,373 @@ + +/*LICENSE_START*/ +/* + * Copyright (C) 2017 Washington University School of Medicine + * + * 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 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ +/*LICENSE_END*/ + +#include +#define __CHART_TWO_NUMERIC_SUBDIVISIONS_MODE_ENUM_DECLARE__ +#include "ChartTwoNumericSubdivisionsModeEnum.h" +#undef __CHART_TWO_NUMERIC_SUBDIVISIONS_MODE_ENUM_DECLARE__ + +#include "CaretAssert.h" + +using namespace caret; + + +/** + * \class caret::ChartTwoNumericSubdivisionsModeEnum + * \brief Enumerated type for charting numeric subdivisions + * + * Using this enumerated type in the GUI with an EnumComboBoxTemplate + * + * Header File (.h) + * Forward declare the data type: + * class EnumComboBoxTemplate; + * + * Declare the member: + * EnumComboBoxTemplate* m_chartTwoNumericSubdivisionsModeEnumComboBox; + * + * Declare a slot that is called when user changes selection + * private slots: + * void chartTwoNumericSubdivisionsModeEnumComboBoxItemActivated(); + * + * Implementation File (.cxx) + * Include the header files + * #include "EnumComboBoxTemplate.h" + * #include "ChartTwoNumericSubdivisionsModeEnum.h" + * + * Instatiate: + * m_chartTwoNumericSubdivisionsModeEnumComboBox = new EnumComboBoxTemplate(this); + * m_chartTwoNumericSubdivisionsModeEnumComboBox->setup(); + * + * Get notified when the user changes the selection: + * QObject::connect(m_chartTwoNumericSubdivisionsModeEnumComboBox, SIGNAL(itemActivated()), + * this, SLOT(chartTwoNumericSubdivisionsModeEnumComboBoxItemActivated())); + * + * Update the selection: + * m_chartTwoNumericSubdivisionsModeEnumComboBox->setSelectedItem(NEW_VALUE); + * + * Read the selection: + * const ChartTwoNumericSubdivisionsModeEnum::Enum VARIABLE = m_chartTwoNumericSubdivisionsModeEnumComboBox->getSelectedItem(); + * + */ + +/** + * Constructor. + * + * @param enumValue + * An enumerated value. + * @param name + * Name of enumerated value. + * + * @param guiName + * User-friendly name for use in user-interface. + */ +ChartTwoNumericSubdivisionsModeEnum::ChartTwoNumericSubdivisionsModeEnum(const Enum enumValue, + const AString& name, + const AString& guiName) +{ + this->enumValue = enumValue; + this->integerCode = integerCodeCounter++; + this->name = name; + this->guiName = guiName; +} + +/** + * Destructor. + */ +ChartTwoNumericSubdivisionsModeEnum::~ChartTwoNumericSubdivisionsModeEnum() +{ +} + +/** + * Initialize the enumerated metadata. + */ +void +ChartTwoNumericSubdivisionsModeEnum::initialize() +{ + if (initializedFlag) { + return; + } + initializedFlag = true; + + enumData.push_back(ChartTwoNumericSubdivisionsModeEnum(AUTO, + "AUTO", + "Auto")); + + enumData.push_back(ChartTwoNumericSubdivisionsModeEnum(USER, + "USER", + "User")); + +} + +/** + * Find the data for and enumerated value. + * @param enumValue + * The enumerated value. + * @return Pointer to data for this enumerated type + * or NULL if no data for type or if type is invalid. + */ +const ChartTwoNumericSubdivisionsModeEnum* +ChartTwoNumericSubdivisionsModeEnum::findData(const Enum enumValue) +{ + if (initializedFlag == false) initialize(); + + size_t num = enumData.size(); + for (size_t i = 0; i < num; i++) { + const ChartTwoNumericSubdivisionsModeEnum* d = &enumData[i]; + if (d->enumValue == enumValue) { + return d; + } + } + + return NULL; +} + +/** + * Get a string representation of the enumerated type. + * @param enumValue + * Enumerated value. + * @return + * String representing enumerated value. + */ +AString +ChartTwoNumericSubdivisionsModeEnum::toName(Enum enumValue) { + if (initializedFlag == false) initialize(); + + const ChartTwoNumericSubdivisionsModeEnum* enumInstance = findData(enumValue); + return enumInstance->name; +} + +/** + * Get an enumerated value corresponding to its name. + * @param name + * Name of enumerated value. + * @param isValidOut + * If not NULL, it is set indicating that a + * enum value exists for the input name. + * @return + * Enumerated value. + */ +ChartTwoNumericSubdivisionsModeEnum::Enum +ChartTwoNumericSubdivisionsModeEnum::fromName(const AString& name, bool* isValidOut) +{ + if (initializedFlag == false) initialize(); + + bool validFlag = false; + Enum enumValue = ChartTwoNumericSubdivisionsModeEnum::enumData[0].enumValue; + + for (std::vector::iterator iter = enumData.begin(); + iter != enumData.end(); + iter++) { + const ChartTwoNumericSubdivisionsModeEnum& d = *iter; + if (d.name == name) { + enumValue = d.enumValue; + validFlag = true; + break; + } + } + + if (isValidOut != 0) { + *isValidOut = validFlag; + } + else if (validFlag == false) { + CaretAssertMessage(0, AString("Name " + name + "failed to match enumerated value for type ChartTwoNumericSubdivisionsModeEnum")); + } + return enumValue; +} + +/** + * Get a GUI string representation of the enumerated type. + * @param enumValue + * Enumerated value. + * @return + * String representing enumerated value. + */ +AString +ChartTwoNumericSubdivisionsModeEnum::toGuiName(Enum enumValue) { + if (initializedFlag == false) initialize(); + + const ChartTwoNumericSubdivisionsModeEnum* enumInstance = findData(enumValue); + return enumInstance->guiName; +} + +/** + * Get an enumerated value corresponding to its GUI name. + * @param s + * Name of enumerated value. + * @param isValidOut + * If not NULL, it is set indicating that a + * enum value exists for the input name. + * @return + * Enumerated value. + */ +ChartTwoNumericSubdivisionsModeEnum::Enum +ChartTwoNumericSubdivisionsModeEnum::fromGuiName(const AString& guiName, bool* isValidOut) +{ + if (initializedFlag == false) initialize(); + + bool validFlag = false; + Enum enumValue = ChartTwoNumericSubdivisionsModeEnum::enumData[0].enumValue; + + for (std::vector::iterator iter = enumData.begin(); + iter != enumData.end(); + iter++) { + const ChartTwoNumericSubdivisionsModeEnum& d = *iter; + if (d.guiName == guiName) { + enumValue = d.enumValue; + validFlag = true; + break; + } + } + + if (isValidOut != 0) { + *isValidOut = validFlag; + } + else if (validFlag == false) { + CaretAssertMessage(0, AString("guiName " + guiName + "failed to match enumerated value for type ChartTwoNumericSubdivisionsModeEnum")); + } + return enumValue; +} + +/** + * Get the integer code for a data type. + * + * @return + * Integer code for data type. + */ +int32_t +ChartTwoNumericSubdivisionsModeEnum::toIntegerCode(Enum enumValue) +{ + if (initializedFlag == false) initialize(); + const ChartTwoNumericSubdivisionsModeEnum* enumInstance = findData(enumValue); + return enumInstance->integerCode; +} + +/** + * Find the data type corresponding to an integer code. + * + * @param integerCode + * Integer code for enum. + * @param isValidOut + * If not NULL, on exit isValidOut will indicate if + * integer code is valid. + * @return + * Enum for integer code. + */ +ChartTwoNumericSubdivisionsModeEnum::Enum +ChartTwoNumericSubdivisionsModeEnum::fromIntegerCode(const int32_t integerCode, bool* isValidOut) +{ + if (initializedFlag == false) initialize(); + + bool validFlag = false; + Enum enumValue = ChartTwoNumericSubdivisionsModeEnum::enumData[0].enumValue; + + for (std::vector::iterator iter = enumData.begin(); + iter != enumData.end(); + iter++) { + const ChartTwoNumericSubdivisionsModeEnum& enumInstance = *iter; + if (enumInstance.integerCode == integerCode) { + enumValue = enumInstance.enumValue; + validFlag = true; + break; + } + } + + if (isValidOut != 0) { + *isValidOut = validFlag; + } + else if (validFlag == false) { + CaretAssertMessage(0, AString("Integer code " + AString::number(integerCode) + "failed to match enumerated value for type ChartTwoNumericSubdivisionsModeEnum")); + } + return enumValue; +} + +/** + * Get all of the enumerated type values. The values can be used + * as parameters to toXXX() methods to get associated metadata. + * + * @param allEnums + * A vector that is OUTPUT containing all of the enumerated values. + */ +void +ChartTwoNumericSubdivisionsModeEnum::getAllEnums(std::vector& allEnums) +{ + if (initializedFlag == false) initialize(); + + allEnums.clear(); + + for (std::vector::iterator iter = enumData.begin(); + iter != enumData.end(); + iter++) { + allEnums.push_back(iter->enumValue); + } +} + +/** + * Get all of the names of the enumerated type values. + * + * @param allNames + * A vector that is OUTPUT containing all of the names of the enumerated values. + * @param isSorted + * If true, the names are sorted in alphabetical order. + */ +void +ChartTwoNumericSubdivisionsModeEnum::getAllNames(std::vector& allNames, const bool isSorted) +{ + if (initializedFlag == false) initialize(); + + allNames.clear(); + + for (std::vector::iterator iter = enumData.begin(); + iter != enumData.end(); + iter++) { + allNames.push_back(ChartTwoNumericSubdivisionsModeEnum::toName(iter->enumValue)); + } + + if (isSorted) { + std::sort(allNames.begin(), allNames.end()); + } +} + +/** + * Get all of the GUI names of the enumerated type values. + * + * @param allNames + * A vector that is OUTPUT containing all of the GUI names of the enumerated values. + * @param isSorted + * If true, the names are sorted in alphabetical order. + */ +void +ChartTwoNumericSubdivisionsModeEnum::getAllGuiNames(std::vector& allGuiNames, const bool isSorted) +{ + if (initializedFlag == false) initialize(); + + allGuiNames.clear(); + + for (std::vector::iterator iter = enumData.begin(); + iter != enumData.end(); + iter++) { + allGuiNames.push_back(ChartTwoNumericSubdivisionsModeEnum::toGuiName(iter->enumValue)); + } + + if (isSorted) { + std::sort(allGuiNames.begin(), allGuiNames.end()); + } +} + diff --git a/src/Charting/ChartTwoNumericSubdivisionsModeEnum.h b/src/Charting/ChartTwoNumericSubdivisionsModeEnum.h new file mode 100644 index 0000000000000000000000000000000000000000..605dd09c56bf8488d5407e8aa84210f067b1dc95 --- /dev/null +++ b/src/Charting/ChartTwoNumericSubdivisionsModeEnum.h @@ -0,0 +1,104 @@ +#ifndef __CHART_TWO_NUMERIC_SUBDIVISIONS_MODE_ENUM_H__ +#define __CHART_TWO_NUMERIC_SUBDIVISIONS_MODE_ENUM_H__ + +/*LICENSE_START*/ +/* + * Copyright (C) 2017 Washington University School of Medicine + * + * 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 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ +/*LICENSE_END*/ + + +#include +#include +#include "AString.h" + +namespace caret { + +class ChartTwoNumericSubdivisionsModeEnum { + +public: + /** + * Enumerated values. + */ + enum Enum { + /** Automatic */ + AUTO, + /** User-defined subdivisions */ + USER + }; + + + ~ChartTwoNumericSubdivisionsModeEnum(); + + static AString toName(Enum enumValue); + + static Enum fromName(const AString& name, bool* isValidOut); + + static AString toGuiName(Enum enumValue); + + static Enum fromGuiName(const AString& guiName, bool* isValidOut); + + static int32_t toIntegerCode(Enum enumValue); + + static Enum fromIntegerCode(const int32_t integerCode, bool* isValidOut); + + static void getAllEnums(std::vector& allEnums); + + static void getAllNames(std::vector& allNames, const bool isSorted); + + static void getAllGuiNames(std::vector& allGuiNames, const bool isSorted); + +private: + ChartTwoNumericSubdivisionsModeEnum(const Enum enumValue, + const AString& name, + const AString& guiName); + + static const ChartTwoNumericSubdivisionsModeEnum* findData(const Enum enumValue); + + /** Holds all instance of enum values and associated metadata */ + static std::vector enumData; + + /** Initialize instances that contain the enum values and metadata */ + static void initialize(); + + /** Indicates instance of enum values and metadata have been initialized */ + static bool initializedFlag; + + /** Auto generated integer codes */ + static int32_t integerCodeCounter; + + /** The enumerated type value for an instance */ + Enum enumValue; + + /** The integer code associated with an enumerated value */ + int32_t integerCode; + + /** The name, a text string that is identical to the enumerated value */ + AString name; + + /** A user-friendly name that is displayed in the GUI */ + AString guiName; +}; + +#ifdef __CHART_TWO_NUMERIC_SUBDIVISIONS_MODE_ENUM_DECLARE__ +std::vector ChartTwoNumericSubdivisionsModeEnum::enumData; +bool ChartTwoNumericSubdivisionsModeEnum::initializedFlag = false; +int32_t ChartTwoNumericSubdivisionsModeEnum::integerCodeCounter = 0; +#endif // __CHART_TWO_NUMERIC_SUBDIVISIONS_MODE_ENUM_DECLARE__ + +} // namespace +#endif //__CHART_TWO_NUMERIC_SUBDIVISIONS_MODE_ENUM_H__ diff --git a/src/Charting/ChartTwoTitle.cxx b/src/Charting/ChartTwoTitle.cxx new file mode 100644 index 0000000000000000000000000000000000000000..934bb033b245288bfd02998096a461d456e01770 --- /dev/null +++ b/src/Charting/ChartTwoTitle.cxx @@ -0,0 +1,275 @@ + +/*LICENSE_START*/ +/* + * Copyright (C) 2017 Washington University School of Medicine + * + * 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 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ +/*LICENSE_END*/ + +#define __CHART_TWO_TITLE_DECLARE__ +#include "ChartTwoTitle.h" +#undef __CHART_TWO_TITLE_DECLARE__ + +#include "CaretAssert.h" +#include "SceneClass.h" +#include "SceneClassAssistant.h" + +using namespace caret; + + + +/** + * \class caret::ChartTwoTitle + * \brief Title and its attributes for charts. + * \ingroup Charting + */ + +/** + * Constructor. + */ +ChartTwoTitle::ChartTwoTitle() +: CaretObjectTracksModification() +{ + + m_sceneAssistant = std::unique_ptr(new SceneClassAssistant()); + + reset(); + + m_sceneAssistant->add("m_displayed", + &m_displayed); + m_sceneAssistant->add("m_paddingSize", + &m_paddingSize); + m_sceneAssistant->add("m_text", + &m_text); + m_sceneAssistant->add("m_textSize", + &m_textSize); +} + +/** + * Destructor. + */ +ChartTwoTitle::~ChartTwoTitle() +{ +} + +/** + * Copy constructor. + * @param obj + * Object that is copied. + */ +ChartTwoTitle::ChartTwoTitle(const ChartTwoTitle& obj) +: CaretObjectTracksModification(obj), +SceneableInterface(obj) +{ + this->copyHelperChartTwoTitle(obj); +} + +/** + * Assignment operator. + * @param obj + * Data copied from obj to this. + * @return + * Reference to this object. + */ +ChartTwoTitle& +ChartTwoTitle::operator=(const ChartTwoTitle& obj) +{ + if (this != &obj) { + CaretObjectTracksModification::operator=(obj); + this->copyHelperChartTwoTitle(obj); + } + return *this; +} + +/** + * Reset this instance to its default values. + */ +void +ChartTwoTitle::reset() +{ + m_displayed = false; + m_paddingSize = 1.0f; + m_text = ""; + m_textSize = 5.0f; +} + + +/** + * Helps with copying an object of this type. + * @param obj + * Object that is copied. + */ +void +ChartTwoTitle::copyHelperChartTwoTitle(const ChartTwoTitle& obj) +{ + m_displayed = obj.m_displayed; + m_paddingSize = obj.m_paddingSize; + m_text = obj.m_text; + m_textSize = obj.m_textSize; +} + +/** + * @return Padding between title and edge of viewport + */ +float +ChartTwoTitle::getPaddingSize() const +{ + return m_paddingSize; +} + +/** + * Set Padding between title and edge of viewport + * + * @param paddingSize + * New value for Padding between title and edge of viewport + */ +void +ChartTwoTitle::setPaddingSize(const float paddingSize) +{ + if (paddingSize != m_paddingSize) { + m_paddingSize = paddingSize; + setModified(); + } +} + +/** + * @return Size of title characters when drawing + */ +float +ChartTwoTitle::getTextSize() const +{ + return m_textSize; +} + +/** + * Set Size of title characters when drawing + * + * @param textSize + * New value for Size of title characters when drawing + */ +void +ChartTwoTitle::setTextSize(const float textSize) +{ + if (textSize != m_textSize) { + m_textSize = textSize; + setModified(); + } +} + +/** + * @return text displayed as title + */ +AString +ChartTwoTitle::getText() const +{ + return m_text; +} + +/** + * Set text displayed as title + * + * @param text + * New value for text displayed as title + */ +void +ChartTwoTitle::setText(const AString& text) +{ + if (text != m_text) { + m_text = text; + setModified(); + } +} + +/** + * @return display status of title + */ +bool +ChartTwoTitle::isDisplayed() const +{ + return m_displayed; +} + +/** + * Set display status of title + * + * @param displayed + * New value for display status of title + */ +void +ChartTwoTitle::setDisplayed(const bool displayed) +{ + if (displayed != m_displayed) { + m_displayed = displayed; + setModified(); + } +} + +/** + * Save information specific to this type of model to the scene. + * + * @param sceneAttributes + * Attributes for the scene. Scenes may be of different types + * (full, generic, etc) and the attributes should be checked when + * saving the scene. + * + * @param instanceName + * Name of instance in the scene. + */ +SceneClass* +ChartTwoTitle::saveToScene(const SceneAttributes* sceneAttributes, + const AString& instanceName) +{ + SceneClass* sceneClass = new SceneClass(instanceName, + "ChartTwoTitle", + 1); + m_sceneAssistant->saveMembers(sceneAttributes, + sceneClass); + + // Uncomment if sub-classes must save to scene + //saveSubClassDataToScene(sceneAttributes, + // sceneClass); + + return sceneClass; +} + +/** + * Restore information specific to the type of model from the scene. + * + * @param sceneAttributes + * Attributes for the scene. Scenes may be of different types + * (full, generic, etc) and the attributes should be checked when + * restoring the scene. + * + * @param sceneClass + * sceneClass from which model specific information is obtained. + */ +void +ChartTwoTitle::restoreFromScene(const SceneAttributes* sceneAttributes, + const SceneClass* sceneClass) +{ + if (sceneClass == NULL) { + return; + } + + m_sceneAssistant->restoreMembers(sceneAttributes, + sceneClass); + + //Uncomment if sub-classes must restore from scene + //restoreSubClassDataFromScene(sceneAttributes, + // sceneClass); + +} + diff --git a/src/Charting/ChartTwoTitle.h b/src/Charting/ChartTwoTitle.h new file mode 100644 index 0000000000000000000000000000000000000000..72537ea038a5b50e80e9b6f70635978264391776 --- /dev/null +++ b/src/Charting/ChartTwoTitle.h @@ -0,0 +1,114 @@ +#ifndef __CHART_TWO_TITLE_H__ +#define __CHART_TWO_TITLE_H__ + +/*LICENSE_START*/ +/* + * Copyright (C) 2017 Washington University School of Medicine + * + * 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 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ +/*LICENSE_END*/ + + + +#include + +#include "CaretObjectTracksModification.h" + +#include "SceneableInterface.h" + + +namespace caret { + class SceneClassAssistant; + + class ChartTwoTitle : public CaretObjectTracksModification, public SceneableInterface { + + public: + ChartTwoTitle(); + + virtual ~ChartTwoTitle(); + + ChartTwoTitle(const ChartTwoTitle& obj); + + ChartTwoTitle& operator=(const ChartTwoTitle& obj); + + float getPaddingSize() const; + + void setPaddingSize(const float paddingSize); + + float getTextSize() const; + + void setTextSize(const float textSize); + + AString getText() const; + + void setText(const AString& text); + + bool isDisplayed() const; + + void setDisplayed(const bool displayed); + + void reset(); + + // ADD_NEW_METHODS_HERE + + virtual SceneClass* saveToScene(const SceneAttributes* sceneAttributes, + const AString& instanceName); + + virtual void restoreFromScene(const SceneAttributes* sceneAttributes, + const SceneClass* sceneClass); + + + + + + +// If there will be sub-classes of this class that need to save +// and restore data from scenes, these pure virtual methods can +// be uncommented to force their implementation by sub-classes. +// protected: +// virtual void saveSubClassDataToScene(const SceneAttributes* sceneAttributes, +// SceneClass* sceneClass) = 0; +// +// virtual void restoreSubClassDataFromScene(const SceneAttributes* sceneAttributes, +// const SceneClass* sceneClass) = 0; + + private: + void copyHelperChartTwoTitle(const ChartTwoTitle& obj); + + std::unique_ptr m_sceneAssistant; + + /** Padding between title and edge of viewport*/ + float m_paddingSize; + + /** Size of title characters when drawing*/ + float m_textSize; + + /** text displayed as title*/ + AString m_text; + + /** display status of title*/ + bool m_displayed; + + // ADD_NEW_MEMBERS_HERE + + }; + +#ifdef __CHART_TWO_TITLE_DECLARE__ + // +#endif // __CHART_TWO_TITLE_DECLARE__ + +} // namespace +#endif //__CHART_TWO_TITLE_H__ diff --git a/src/Charting/ChartingVersionEnum.cxx b/src/Charting/ChartingVersionEnum.cxx new file mode 100644 index 0000000000000000000000000000000000000000..a0315d5e12fbf8121d35c0fb18a60fa9f149cc88 --- /dev/null +++ b/src/Charting/ChartingVersionEnum.cxx @@ -0,0 +1,375 @@ + +/*LICENSE_START*/ +/* + * Copyright (C) 2016 Washington University School of Medicine + * + * 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 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ +/*LICENSE_END*/ + +#include +#define __CHARTING_VERSION_ENUM_DECLARE__ +#include "ChartingVersionEnum.h" +#undef __CHARTING_VERSION_ENUM_DECLARE__ + +#include "CaretAssert.h" + +using namespace caret; + + +/** + * \class caret::ChartingVersionEnum + * \brief Version of Charting Implementation + * + * + * + * Using this enumerated type in the GUI with an EnumComboBoxTemplate + * + * Header File (.h) + * Forward declare the data type: + * class EnumComboBoxTemplate; + * + * Declare the member: + * EnumComboBoxTemplate* m_chartingVersionEnumComboBox; + * + * Declare a slot that is called when user changes selection + * private slots: + * void chartingVersionEnumComboBoxItemActivated(); + * + * Implementation File (.cxx) + * Include the header files + * #include "EnumComboBoxTemplate.h" + * #include "ChartingVersionEnum.h" + * + * Instatiate: + * m_chartingVersionEnumComboBox = new EnumComboBoxTemplate(this); + * m_chartingVersionEnumComboBox->setup(); + * + * Get notified when the user changes the selection: + * QObject::connect(m_chartingVersionEnumComboBox, SIGNAL(itemActivated()), + * this, SLOT(chartingVersionEnumComboBoxItemActivated())); + * + * Update the selection: + * m_chartingVersionEnumComboBox->setSelectedItem(NEW_VALUE); + * + * Read the selection: + * const ChartingVersionEnum::Enum VARIABLE = m_chartingVersionEnumComboBox->getSelectedItem(); + * + */ + +/** + * Constructor. + * + * @param enumValue + * An enumerated value. + * @param name + * Name of enumerated value. + * + * @param guiName + * User-friendly name for use in user-interface. + */ +ChartingVersionEnum::ChartingVersionEnum(const Enum enumValue, + const AString& name, + const AString& guiName) +{ + this->enumValue = enumValue; + this->integerCode = integerCodeCounter++; + this->name = name; + this->guiName = guiName; +} + +/** + * Destructor. + */ +ChartingVersionEnum::~ChartingVersionEnum() +{ +} + +/** + * Initialize the enumerated metadata. + */ +void +ChartingVersionEnum::initialize() +{ + if (initializedFlag) { + return; + } + initializedFlag = true; + + enumData.push_back(ChartingVersionEnum(CHARTING_VERSION_ONE, + "CHARTING_VERSION_ONE", + "Charting Version One")); + + enumData.push_back(ChartingVersionEnum(CHARTING_VERSION_TWO, + "CHARTING_VERSION_TWO", + "Charting Version Two")); + +} + +/** + * Find the data for and enumerated value. + * @param enumValue + * The enumerated value. + * @return Pointer to data for this enumerated type + * or NULL if no data for type or if type is invalid. + */ +const ChartingVersionEnum* +ChartingVersionEnum::findData(const Enum enumValue) +{ + if (initializedFlag == false) initialize(); + + size_t num = enumData.size(); + for (size_t i = 0; i < num; i++) { + const ChartingVersionEnum* d = &enumData[i]; + if (d->enumValue == enumValue) { + return d; + } + } + + return NULL; +} + +/** + * Get a string representation of the enumerated type. + * @param enumValue + * Enumerated value. + * @return + * String representing enumerated value. + */ +AString +ChartingVersionEnum::toName(Enum enumValue) { + if (initializedFlag == false) initialize(); + + const ChartingVersionEnum* enumInstance = findData(enumValue); + return enumInstance->name; +} + +/** + * Get an enumerated value corresponding to its name. + * @param name + * Name of enumerated value. + * @param isValidOut + * If not NULL, it is set indicating that a + * enum value exists for the input name. + * @return + * Enumerated value. + */ +ChartingVersionEnum::Enum +ChartingVersionEnum::fromName(const AString& name, bool* isValidOut) +{ + if (initializedFlag == false) initialize(); + + bool validFlag = false; + Enum enumValue = ChartingVersionEnum::enumData[0].enumValue; + + for (std::vector::iterator iter = enumData.begin(); + iter != enumData.end(); + iter++) { + const ChartingVersionEnum& d = *iter; + if (d.name == name) { + enumValue = d.enumValue; + validFlag = true; + break; + } + } + + if (isValidOut != 0) { + *isValidOut = validFlag; + } + else if (validFlag == false) { + CaretAssertMessage(0, AString("Name " + name + "failed to match enumerated value for type ChartingVersionEnum")); + } + return enumValue; +} + +/** + * Get a GUI string representation of the enumerated type. + * @param enumValue + * Enumerated value. + * @return + * String representing enumerated value. + */ +AString +ChartingVersionEnum::toGuiName(Enum enumValue) { + if (initializedFlag == false) initialize(); + + const ChartingVersionEnum* enumInstance = findData(enumValue); + return enumInstance->guiName; +} + +/** + * Get an enumerated value corresponding to its GUI name. + * @param s + * Name of enumerated value. + * @param isValidOut + * If not NULL, it is set indicating that a + * enum value exists for the input name. + * @return + * Enumerated value. + */ +ChartingVersionEnum::Enum +ChartingVersionEnum::fromGuiName(const AString& guiName, bool* isValidOut) +{ + if (initializedFlag == false) initialize(); + + bool validFlag = false; + Enum enumValue = ChartingVersionEnum::enumData[0].enumValue; + + for (std::vector::iterator iter = enumData.begin(); + iter != enumData.end(); + iter++) { + const ChartingVersionEnum& d = *iter; + if (d.guiName == guiName) { + enumValue = d.enumValue; + validFlag = true; + break; + } + } + + if (isValidOut != 0) { + *isValidOut = validFlag; + } + else if (validFlag == false) { + CaretAssertMessage(0, AString("guiName " + guiName + "failed to match enumerated value for type ChartingVersionEnum")); + } + return enumValue; +} + +/** + * Get the integer code for a data type. + * + * @return + * Integer code for data type. + */ +int32_t +ChartingVersionEnum::toIntegerCode(Enum enumValue) +{ + if (initializedFlag == false) initialize(); + const ChartingVersionEnum* enumInstance = findData(enumValue); + return enumInstance->integerCode; +} + +/** + * Find the data type corresponding to an integer code. + * + * @param integerCode + * Integer code for enum. + * @param isValidOut + * If not NULL, on exit isValidOut will indicate if + * integer code is valid. + * @return + * Enum for integer code. + */ +ChartingVersionEnum::Enum +ChartingVersionEnum::fromIntegerCode(const int32_t integerCode, bool* isValidOut) +{ + if (initializedFlag == false) initialize(); + + bool validFlag = false; + Enum enumValue = ChartingVersionEnum::enumData[0].enumValue; + + for (std::vector::iterator iter = enumData.begin(); + iter != enumData.end(); + iter++) { + const ChartingVersionEnum& enumInstance = *iter; + if (enumInstance.integerCode == integerCode) { + enumValue = enumInstance.enumValue; + validFlag = true; + break; + } + } + + if (isValidOut != 0) { + *isValidOut = validFlag; + } + else if (validFlag == false) { + CaretAssertMessage(0, AString("Integer code " + AString::number(integerCode) + "failed to match enumerated value for type ChartingVersionEnum")); + } + return enumValue; +} + +/** + * Get all of the enumerated type values. The values can be used + * as parameters to toXXX() methods to get associated metadata. + * + * @param allEnums + * A vector that is OUTPUT containing all of the enumerated values. + */ +void +ChartingVersionEnum::getAllEnums(std::vector& allEnums) +{ + if (initializedFlag == false) initialize(); + + allEnums.clear(); + + for (std::vector::iterator iter = enumData.begin(); + iter != enumData.end(); + iter++) { + allEnums.push_back(iter->enumValue); + } +} + +/** + * Get all of the names of the enumerated type values. + * + * @param allNames + * A vector that is OUTPUT containing all of the names of the enumerated values. + * @param isSorted + * If true, the names are sorted in alphabetical order. + */ +void +ChartingVersionEnum::getAllNames(std::vector& allNames, const bool isSorted) +{ + if (initializedFlag == false) initialize(); + + allNames.clear(); + + for (std::vector::iterator iter = enumData.begin(); + iter != enumData.end(); + iter++) { + allNames.push_back(ChartingVersionEnum::toName(iter->enumValue)); + } + + if (isSorted) { + std::sort(allNames.begin(), allNames.end()); + } +} + +/** + * Get all of the GUI names of the enumerated type values. + * + * @param allNames + * A vector that is OUTPUT containing all of the GUI names of the enumerated values. + * @param isSorted + * If true, the names are sorted in alphabetical order. + */ +void +ChartingVersionEnum::getAllGuiNames(std::vector& allGuiNames, const bool isSorted) +{ + if (initializedFlag == false) initialize(); + + allGuiNames.clear(); + + for (std::vector::iterator iter = enumData.begin(); + iter != enumData.end(); + iter++) { + allGuiNames.push_back(ChartingVersionEnum::toGuiName(iter->enumValue)); + } + + if (isSorted) { + std::sort(allGuiNames.begin(), allGuiNames.end()); + } +} + diff --git a/src/Charting/ChartingVersionEnum.h b/src/Charting/ChartingVersionEnum.h new file mode 100644 index 0000000000000000000000000000000000000000..dd72f5120cf1501040f36605008a6f0706fe1dcb --- /dev/null +++ b/src/Charting/ChartingVersionEnum.h @@ -0,0 +1,104 @@ +#ifndef __CHARTING_VERSION_ENUM_H__ +#define __CHARTING_VERSION_ENUM_H__ + +/*LICENSE_START*/ +/* + * Copyright (C) 2016 Washington University School of Medicine + * + * 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 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ +/*LICENSE_END*/ + + +#include +#include +#include "AString.h" + +namespace caret { + +class ChartingVersionEnum { + +public: + /** + * Enumerated values. + */ + enum Enum { + /** */ + CHARTING_VERSION_ONE, + /** */ + CHARTING_VERSION_TWO + }; + + + ~ChartingVersionEnum(); + + static AString toName(Enum enumValue); + + static Enum fromName(const AString& name, bool* isValidOut); + + static AString toGuiName(Enum enumValue); + + static Enum fromGuiName(const AString& guiName, bool* isValidOut); + + static int32_t toIntegerCode(Enum enumValue); + + static Enum fromIntegerCode(const int32_t integerCode, bool* isValidOut); + + static void getAllEnums(std::vector& allEnums); + + static void getAllNames(std::vector& allNames, const bool isSorted); + + static void getAllGuiNames(std::vector& allGuiNames, const bool isSorted); + +private: + ChartingVersionEnum(const Enum enumValue, + const AString& name, + const AString& guiName); + + static const ChartingVersionEnum* findData(const Enum enumValue); + + /** Holds all instance of enum values and associated metadata */ + static std::vector enumData; + + /** Initialize instances that contain the enum values and metadata */ + static void initialize(); + + /** Indicates instance of enum values and metadata have been initialized */ + static bool initializedFlag; + + /** Auto generated integer codes */ + static int32_t integerCodeCounter; + + /** The enumerated type value for an instance */ + Enum enumValue; + + /** The integer code associated with an enumerated value */ + int32_t integerCode; + + /** The name, a text string that is identical to the enumerated value */ + AString name; + + /** A user-friendly name that is displayed in the GUI */ + AString guiName; +}; + +#ifdef __CHARTING_VERSION_ENUM_DECLARE__ +std::vector ChartingVersionEnum::enumData; +bool ChartingVersionEnum::initializedFlag = false; +int32_t ChartingVersionEnum::integerCodeCounter = 0; +#endif // __CHARTING_VERSION_ENUM_DECLARE__ + +} // namespace +#endif //__CHARTING_VERSION_ENUM_H__ diff --git a/src/Charting/EventChartTwoAttributesChanged.cxx b/src/Charting/EventChartTwoAttributesChanged.cxx new file mode 100644 index 0000000000000000000000000000000000000000..639e1c09792dba4c0b6eb2fa874a1a9c5d6db06d --- /dev/null +++ b/src/Charting/EventChartTwoAttributesChanged.cxx @@ -0,0 +1,191 @@ + +/*LICENSE_START*/ +/* + * Copyright (C) 2017 Washington University School of Medicine + * + * 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 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ +/*LICENSE_END*/ + +#define __EVENT_CHART_TWO_ATTRIBUTES_CHANGED_DECLARE__ +#include "EventChartTwoAttributesChanged.h" +#undef __EVENT_CHART_TWO_ATTRIBUTES_CHANGED_DECLARE__ + +#include "CaretAssert.h" +#include "EventTypeEnum.h" + +using namespace caret; + + + +/** + * \class caret::EventChartTwoAttributesChanged + * \brief Event for change in chart two attributes + * \ingroup Charting + */ + +/** + * Constructor. + */ +EventChartTwoAttributesChanged::EventChartTwoAttributesChanged() +: Event(EventTypeEnum::EVENT_CHART_TWO_ATTRIBUTES_CHANGED) +{ + +} + +/** + * Destructor. + */ +EventChartTwoAttributesChanged::~EventChartTwoAttributesChanged() +{ +} + +/** + * @return The mode. + */ +EventChartTwoAttributesChanged::Mode +EventChartTwoAttributesChanged::getMode() const +{ + return m_mode; +} + +/** + * Set for a change in a cartesian axis. + * + * @param yokingGroup + * Selected yoking group. + * @param chartTwoDataType + * Type of chart. + * @param cartesianAxis + * Cartesian axis that is changed. + */ +void +EventChartTwoAttributesChanged::setCartesianAxisChanged(const YokingGroupEnum::Enum yokingGroup, + const ChartTwoDataTypeEnum::Enum chartTwoDataType, + const ChartTwoCartesianAxis* cartesianAxis) +{ + CaretAssert(cartesianAxis); + m_mode = Mode::CARTESIAN_AXIS; + m_yokingGroup = yokingGroup; + m_chartTwoDataType = chartTwoDataType; + m_cartesianAxis = const_cast(cartesianAxis); +} + +/** + * Get for a change in a cartesian axis. + * + * @param yokingGroupOut + * Selected yoking group. + * @param chartTwoDataTypeOut + * Type of chart. + * @param cartesianAxisOut + * Cartesian axis that is changed. + */ +void +EventChartTwoAttributesChanged::getCartesianAxisChanged(YokingGroupEnum::Enum &yokingGroupOut, + ChartTwoDataTypeEnum::Enum &chartTwoDataTypeOut, + ChartTwoCartesianAxis* &cartesianAxisOut) const +{ + CaretAssert(m_mode == Mode::CARTESIAN_AXIS); + yokingGroupOut = m_yokingGroup; + chartTwoDataTypeOut = m_chartTwoDataType; + cartesianAxisOut = m_cartesianAxis; +} + +/** + * Set for a change in line thickness. + * + * @param yokingGroup + * Selected yoking group. + * @param chartTwoDataType + * Type of chart. + * @param lineThickness + * New line thickness + */ +void +EventChartTwoAttributesChanged::setLineThicknessChanged(const YokingGroupEnum::Enum yokingGroup, + const ChartTwoDataTypeEnum::Enum chartTwoDataType, + const float lineThickness) +{ + m_mode = Mode::LINE_THICKESS; + m_yokingGroup = yokingGroup; + m_chartTwoDataType = chartTwoDataType; + m_lineThickness = lineThickness; +} + +/** + * Get for a change in line thickness + * + * @param yokingGroupOut + * Selected yoking group. + * @param chartTwoDataTypeOut + * Type of chart. + * @param lineThicknessOut + * Output line thickness + */ +void +EventChartTwoAttributesChanged::getLineThicknessChanged(YokingGroupEnum::Enum &yokingGroupOut, + ChartTwoDataTypeEnum::Enum &chartTwoDataTypeOut, + float& lineThicknessOut) const +{ + CaretAssert(m_mode == Mode::LINE_THICKESS); + yokingGroupOut = m_yokingGroup; + chartTwoDataTypeOut = m_chartTwoDataType; + lineThicknessOut = m_lineThickness; +} + +/** + * Set for a change in a title. + * + * @param yokingGroup + * Selected yoking group. + * @param chartTwoDataType + * Type of chart. + * @parm chartTitle + * Chart title. + */ +void +EventChartTwoAttributesChanged::setTitleChanged(const YokingGroupEnum::Enum yokingGroup, + const ChartTwoDataTypeEnum::Enum chartTwoDataType, + const ChartTwoTitle* chartTitle) +{ + CaretAssert(chartTitle); + m_mode = Mode::TITLE; + m_yokingGroup = yokingGroup; + m_chartTwoDataType = chartTwoDataType; + m_chartTitle = const_cast(chartTitle); +} + +/** + * Get for a change in title. + * + * @param yokingGroupOut + * Selected yoking group. + * @param chartTwoDataTypeOut + * Type of chart. + * @parm chartTitleOut + * Chart title. + */ +void +EventChartTwoAttributesChanged::getTitleChanged(YokingGroupEnum::Enum &yokingGroupOut, + ChartTwoDataTypeEnum::Enum &chartTwoDataTypeOut, + ChartTwoTitle* &chartTitleOut) const +{ + CaretAssert(m_mode == Mode::TITLE); + yokingGroupOut = m_yokingGroup; + chartTwoDataTypeOut = m_chartTwoDataType; + chartTitleOut = m_chartTitle; +} + diff --git a/src/Charting/EventChartTwoAttributesChanged.h b/src/Charting/EventChartTwoAttributesChanged.h new file mode 100644 index 0000000000000000000000000000000000000000..993094619b56fbb6bdc3e10afb02996a33c0e803 --- /dev/null +++ b/src/Charting/EventChartTwoAttributesChanged.h @@ -0,0 +1,105 @@ +#ifndef __EVENT_CHART_TWO_ATTRIBUTES_CHANGED_H__ +#define __EVENT_CHART_TWO_ATTRIBUTES_CHANGED_H__ + +/*LICENSE_START*/ +/* + * Copyright (C) 2017 Washington University School of Medicine + * + * 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 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ +/*LICENSE_END*/ + + + +#include + +#include "ChartTwoDataTypeEnum.h" +#include "Event.h" +#include "YokingGroupEnum.h" + + +namespace caret { + + class ChartTwoCartesianAxis; + class ChartTwoTitle; + class EventChartTwoAttributesChanged : public Event { + + public: + enum class Mode { + INVALID, + CARTESIAN_AXIS, + LINE_THICKESS, + TITLE + }; + + EventChartTwoAttributesChanged(); + + ~EventChartTwoAttributesChanged(); + + Mode getMode() const; + + void setCartesianAxisChanged(const YokingGroupEnum::Enum yokingGroup, + const ChartTwoDataTypeEnum::Enum chartTwoDataType, + const ChartTwoCartesianAxis* cartesianAxis); + + void getCartesianAxisChanged(YokingGroupEnum::Enum &yokingGroupOut, + ChartTwoDataTypeEnum::Enum &chartTwoDataTypeOut, + ChartTwoCartesianAxis* &cartesianAxisOut) const; + + void setLineThicknessChanged(const YokingGroupEnum::Enum yokingGroup, + const ChartTwoDataTypeEnum::Enum chartTwoDataType, + const float lineThickness); + + void getLineThicknessChanged(YokingGroupEnum::Enum &yokingGroupOut, + ChartTwoDataTypeEnum::Enum &chartTwoDataTypeOut, + float& lineThicknessOut) const; + + void setTitleChanged(const YokingGroupEnum::Enum yokingGroup, + const ChartTwoDataTypeEnum::Enum chartTwoDataType, + const ChartTwoTitle* chartTitle); + + void getTitleChanged(YokingGroupEnum::Enum &yokingGroupOut, + ChartTwoDataTypeEnum::Enum &chartTwoDataTypeOut, + ChartTwoTitle* &chartTitleOut) const; + + // ADD_NEW_METHODS_HERE + + private: + EventChartTwoAttributesChanged(const EventChartTwoAttributesChanged&); + + EventChartTwoAttributesChanged& operator=(const EventChartTwoAttributesChanged&); + + Mode m_mode = Mode::INVALID; + + YokingGroupEnum::Enum m_yokingGroup = YokingGroupEnum::YOKING_GROUP_OFF; + + ChartTwoDataTypeEnum::Enum m_chartTwoDataType = ChartTwoDataTypeEnum::CHART_DATA_TYPE_INVALID; + + ChartTwoCartesianAxis* m_cartesianAxis = NULL; + + ChartTwoTitle* m_chartTitle = NULL; + + float m_lineThickness = 1.0f; + + // ADD_NEW_MEMBERS_HERE + + }; + +#ifdef __EVENT_CHART_TWO_ATTRIBUTES_CHANGED_DECLARE__ + // +#endif // __EVENT_CHART_TWO_ATTRIBUTES_CHANGED_DECLARE__ + +} // namespace +#endif //__EVENT_CHART_TWO_ATTRIBUTES_CHANGED_H__ diff --git a/src/Charting/EventChartTwoAxisGetDataRange.cxx b/src/Charting/EventChartTwoAxisGetDataRange.cxx new file mode 100644 index 0000000000000000000000000000000000000000..f34aff3a6cc8cbff86aef1913794c5aa7240864f --- /dev/null +++ b/src/Charting/EventChartTwoAxisGetDataRange.cxx @@ -0,0 +1,111 @@ + +/*LICENSE_START*/ +/* + * Copyright (C) 2017 Washington University School of Medicine + * + * 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 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ +/*LICENSE_END*/ + +#define __EVENT_CHART_TWO_AXIS_GET_DATA_RANGE_DECLARE__ +#include "EventChartTwoAxisGetDataRange.h" +#undef __EVENT_CHART_TWO_AXIS_GET_DATA_RANGE_DECLARE__ + +#include "CaretAssert.h" +#include "EventTypeEnum.h" + +using namespace caret; + + + +/** + * \class caret::EventChartTwoAxisGetDataRange + * \brief Event to get the range of data for a chart two axis + * \ingroup Charting + */ + +/** + * Constructor. + */ +EventChartTwoAxisGetDataRange::EventChartTwoAxisGetDataRange(const ChartTwoOverlaySet* chartOverlaySet, + const ChartAxisLocationEnum::Enum chartAxisLocation) +: Event(EventTypeEnum::EVENT_CHART_TWO_AXIS_GET_DATA_RANGE), +m_chartOverlaySet(chartOverlaySet), +m_chartAxisLocation(chartAxisLocation) +{ + +} + +/** + * Destructor. + */ +EventChartTwoAxisGetDataRange::~EventChartTwoAxisGetDataRange() +{ +} + +/** + * @return The chart overlay set that contains the axis. + */ +const ChartTwoOverlaySet* +EventChartTwoAxisGetDataRange::getChartOverlaySet() const +{ + return m_chartOverlaySet; +} + +/** + * @return The location of the axis. + */ +ChartAxisLocationEnum::Enum +EventChartTwoAxisGetDataRange::getChartAxisLocation() const +{ + return m_chartAxisLocation; +} + +/** + * Get the minimum and maximum values. + * + * @param minimumValue + * The minimum value. + * @param maximumValue + * The maximum value. + * @return + * True if the minimum and maximum are valid, else false. + */ +bool +EventChartTwoAxisGetDataRange::getMinimumAndMaximumValues(float& minimumValue, + float& maximumValue) const +{ + minimumValue = m_minimumValue; + maximumValue = m_maximumValue; + + return m_valuesValidFlag; +} + + +/** + * Set the minimum and maximum values. + * + * @param minimumValue + * The minimum value. + * @param maximumValue + * The maximum value. + */ +void EventChartTwoAxisGetDataRange::setMinimumAndMaximumValues(const float minimumValue, + const float maximumValue) +{ + m_minimumValue = minimumValue; + m_maximumValue = maximumValue; + m_valuesValidFlag = true; +} diff --git a/src/Charting/EventChartTwoAxisGetDataRange.h b/src/Charting/EventChartTwoAxisGetDataRange.h new file mode 100644 index 0000000000000000000000000000000000000000..207c13fc3594059d731010bb2da82f72e7330283 --- /dev/null +++ b/src/Charting/EventChartTwoAxisGetDataRange.h @@ -0,0 +1,79 @@ +#ifndef __EVENT_CHART_TWO_AXIS_GET_DATA_RANGE_H__ +#define __EVENT_CHART_TWO_AXIS_GET_DATA_RANGE_H__ + +/*LICENSE_START*/ +/* + * Copyright (C) 2017 Washington University School of Medicine + * + * 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 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ +/*LICENSE_END*/ + + + +#include + +#include "ChartAxisLocationEnum.h" +#include "Event.h" + +namespace caret { + + class ChartTwoOverlaySet; + + class EventChartTwoAxisGetDataRange : public Event { + + public: + EventChartTwoAxisGetDataRange(const ChartTwoOverlaySet* chartOverlaySet, + const ChartAxisLocationEnum::Enum chartAxisLocation); + + virtual ~EventChartTwoAxisGetDataRange(); + + const ChartTwoOverlaySet* getChartOverlaySet() const; + + ChartAxisLocationEnum::Enum getChartAxisLocation() const; + + bool getMinimumAndMaximumValues(float& minimumValue, + float& maximumValue) const; + + void setMinimumAndMaximumValues(const float minimumValue, + const float maximumValue); + + // ADD_NEW_METHODS_HERE + + private: + EventChartTwoAxisGetDataRange(const EventChartTwoAxisGetDataRange&); + + EventChartTwoAxisGetDataRange& operator=(const EventChartTwoAxisGetDataRange&); + + const ChartTwoOverlaySet* m_chartOverlaySet; + + const ChartAxisLocationEnum::Enum m_chartAxisLocation; + + float m_minimumValue = 0.0f; + + float m_maximumValue = 0.0f; + + bool m_valuesValidFlag = false; + + // ADD_NEW_MEMBERS_HERE + + }; + +#ifdef __EVENT_CHART_TWO_AXIS_GET_DATA_RANGE_DECLARE__ + // +#endif // __EVENT_CHART_TWO_AXIS_GET_DATA_RANGE_DECLARE__ + +} // namespace +#endif //__EVENT_CHART_TWO_AXIS_GET_DATA_RANGE_H__ diff --git a/src/Charting/EventChartTwoLoadLineSeriesData.cxx b/src/Charting/EventChartTwoLoadLineSeriesData.cxx new file mode 100644 index 0000000000000000000000000000000000000000..fbd2317f28b8602ad5ada5bca951fe899e1aef26 --- /dev/null +++ b/src/Charting/EventChartTwoLoadLineSeriesData.cxx @@ -0,0 +1,80 @@ + +/*LICENSE_START*/ +/* + * Copyright (C) 2017 Washington University School of Medicine + * + * 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 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ +/*LICENSE_END*/ + +#define __EVENT_CHART_TWO_LOAD_LINE_SERIES_DATA_DECLARE__ +#include "EventChartTwoLoadLineSeriesData.h" +#undef __EVENT_CHART_TWO_LOAD_LINE_SERIES_DATA_DECLARE__ + +#include "CaretAssert.h" +#include "EventTypeEnum.h" + +using namespace caret; + + + +/** + * \class caret::EventChartTwoLoadLineSeriesData + * \brief Event for loading chart two line series data + * \ingroup Charting + */ + +/** + * Constructor. + * + * @param validTabIndices + * Indices of valid tabs. + * @param mapFileDataSelector + * Map file data selector that provides data selection criteria. + */ +EventChartTwoLoadLineSeriesData::EventChartTwoLoadLineSeriesData(const std::vector& validTabIndices, + const MapFileDataSelector& mapFileDataSelector) +: Event(EventTypeEnum::EVENT_CHART_TWO_LOAD_LINE_SERIES_DATA), +m_validTabIndices(validTabIndices), +m_mapFileDataSelector(mapFileDataSelector) +{ + +} + +/** + * Destructor. + */ +EventChartTwoLoadLineSeriesData::~EventChartTwoLoadLineSeriesData() +{ +} + +/** + * @return The valid tab indices. + */ +std::vector +EventChartTwoLoadLineSeriesData::getValidTabIndices() const +{ + return m_validTabIndices; +} + +/** + * @return Map file data selector that provides data selection criteria. + */ +MapFileDataSelector +EventChartTwoLoadLineSeriesData::getMapFileDataSelector() const +{ + return m_mapFileDataSelector; +} + diff --git a/src/Charting/EventChartTwoLoadLineSeriesData.h b/src/Charting/EventChartTwoLoadLineSeriesData.h new file mode 100644 index 0000000000000000000000000000000000000000..e31a209a78d83de62c4e679f37c02d35ba829013 --- /dev/null +++ b/src/Charting/EventChartTwoLoadLineSeriesData.h @@ -0,0 +1,69 @@ +#ifndef __EVENT_CHART_TWO_LOAD_LINE_SERIES_DATA_H__ +#define __EVENT_CHART_TWO_LOAD_LINE_SERIES_DATA_H__ + +/*LICENSE_START*/ +/* + * Copyright (C) 2017 Washington University School of Medicine + * + * 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 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ +/*LICENSE_END*/ + + + +#include + +#include "Event.h" +#include "MapFileDataSelector.h" +#include "StructureEnum.h" + + +namespace caret { + + class EventChartTwoLoadLineSeriesData : public Event { + + public: + EventChartTwoLoadLineSeriesData(const std::vector& validTabIndices, + const MapFileDataSelector& mapFileDataSelector); +// EventChartTwoLoadLineSeriesData(const std::vector& validTabIndices, +// const MapFileDataSelector& mapFileDataSelector); + + virtual ~EventChartTwoLoadLineSeriesData(); + + MapFileDataSelector getMapFileDataSelector() const; + + std::vector getValidTabIndices() const; + + // ADD_NEW_METHODS_HERE + + private: + EventChartTwoLoadLineSeriesData(const EventChartTwoLoadLineSeriesData&); + + EventChartTwoLoadLineSeriesData& operator=(const EventChartTwoLoadLineSeriesData&); + + const std::vector m_validTabIndices; + + MapFileDataSelector m_mapFileDataSelector; + + // ADD_NEW_MEMBERS_HERE + + }; + +#ifdef __EVENT_CHART_TWO_LOAD_LINE_SERIES_DATA_DECLARE__ + // +#endif // __EVENT_CHART_TWO_LOAD_LINE_SERIES_DATA_DECLARE__ + +} // namespace +#endif //__EVENT_CHART_TWO_LOAD_LINE_SERIES_DATA_H__ diff --git a/src/Charting/MapFileDataSelector.cxx b/src/Charting/MapFileDataSelector.cxx new file mode 100644 index 0000000000000000000000000000000000000000..2f3db8d526ce5600c2898f50fadefc4c0ae503da --- /dev/null +++ b/src/Charting/MapFileDataSelector.cxx @@ -0,0 +1,537 @@ + +/*LICENSE_START*/ +/* + * Copyright (C) 2017 Washington University School of Medicine + * + * 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 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ +/*LICENSE_END*/ + +#define __MAP_FILE_DATA_SELECTOR_DECLARE__ +#include "MapFileDataSelector.h" +#undef __MAP_FILE_DATA_SELECTOR_DECLARE__ + +#include "CaretAssert.h" +#include "SceneClass.h" +#include "SceneClassArray.h" +#include "SceneClassAssistant.h" + +using namespace caret; + + + +/** + * \class caret::MapFileDataSelector + * \brief Identifies data for selection in a map file + * \ingroup Charting + */ + +/** + * Constructor. + */ +MapFileDataSelector::MapFileDataSelector() +: CaretObject() +{ + reset(); + + m_sceneAssistant = std::unique_ptr(new SceneClassAssistant()); + m_sceneAssistant->add("m_surfaceNumberOfVertices", + &m_surfaceNumberOfVertices); + m_sceneAssistant->add("m_surfaceStructure", + &m_surfaceStructure); + m_sceneAssistant->add("m_surfaceVertexIndex", + &m_surfaceVertexIndex); + m_sceneAssistant->addArray("m_voxelXYZ", m_voxelXYZ, 3, 0.0f); + m_sceneAssistant->add("m_columnIndex", + &m_columnIndex); + m_sceneAssistant->add("m_columnMapFileName", + &m_columnMapFileName); + m_sceneAssistant->add("m_rowIndex", + &m_rowIndex); + m_sceneAssistant->add("m_rowMapFileName", + &m_rowMapFileName); +} + +/** + * Destructor. + */ +MapFileDataSelector::~MapFileDataSelector() +{ +} + +/** + * Copy constructor. + * @param obj + * Object that is copied. + */ +MapFileDataSelector::MapFileDataSelector(const MapFileDataSelector& obj) +: CaretObject(obj), +SceneableInterface(obj) +{ + this->copyHelperMapFileDataSelector(obj); +} + +/** + * Assignment operator. + * @param obj + * Data copied from obj to this. + * @return + * Reference to this object. + */ +MapFileDataSelector& +MapFileDataSelector::operator=(const MapFileDataSelector& obj) +{ + if (this != &obj) { + CaretObject::operator=(obj); + this->copyHelperMapFileDataSelector(obj); + } + return *this; +} + +/** + * Helps with copying an object of this type. + * @param obj + * Object that is copied. + */ +void +MapFileDataSelector::copyHelperMapFileDataSelector(const MapFileDataSelector& obj) +{ + m_dataSelectionType = obj.m_dataSelectionType; + m_surfaceNumberOfVertices = obj.m_surfaceNumberOfVertices; + m_surfaceStructure = obj.m_surfaceStructure; + m_surfaceVertexAverageIndices = obj.m_surfaceVertexAverageIndices; + m_surfaceVertexIndex = obj.m_surfaceVertexIndex; + m_voxelXYZ[0] = obj.m_voxelXYZ[0]; + m_voxelXYZ[1] = obj.m_voxelXYZ[1]; + m_voxelXYZ[2] = obj.m_voxelXYZ[2]; + m_columnIndex = obj.m_columnIndex; + m_columnMapFile = obj.m_columnMapFile; + m_columnMapFileName = obj.m_columnMapFileName; + m_rowIndex = obj.m_rowIndex; + m_rowMapFile = obj.m_rowMapFile; + m_rowMapFileName = obj.m_rowMapFileName; +} + +/** + * @return The data selection type. + */ +MapFileDataSelector::DataSelectionType +MapFileDataSelector::getDataSelectionType() const +{ + return m_dataSelectionType; +} + +/** + * @return Name of the data selection type. + * + * @param dataSelectionType + * The data selection type. + */ +AString +MapFileDataSelector::getDataSelectionTypeName(const DataSelectionType dataSelectionType) +{ + AString dataSelectionTypeName; + switch (dataSelectionType) { + case DataSelectionType::INVALID: + dataSelectionTypeName = "INVALID"; + break; + case DataSelectionType::COLUMN_DATA: + dataSelectionTypeName = "COLUMN_DATA"; + break; + case DataSelectionType::ROW_DATA: + dataSelectionTypeName = "ROW_DATA"; + break; + case DataSelectionType::SURFACE_VERTEX: + dataSelectionTypeName = "SURFACE_VERTEX"; + break; + case DataSelectionType::SURFACE_VERTICES_AVERAGE: + dataSelectionTypeName = "SURFACE_VERTICES_AVERAGE"; + break; + case DataSelectionType::VOLUME_XYZ: + dataSelectionTypeName = "VOLUME_XYZ"; + break; + } + CaretAssert( ! dataSelectionTypeName.isEmpty()); + + return dataSelectionTypeName; +} + +/** + * Get data for a surface vertex. + * + * @param surfaceStructure + * The surface structure. + * @param surfaceNumberOfVertices + * Number of vertices in the surface. + * @param surfaceVertexIndex + * Index of the vertex. + */ +void +MapFileDataSelector::getSurfaceVertex(StructureEnum::Enum& surfaceStructure, + int32_t& surfaceNumberOfVertices, + int32_t& surfaceVertexIndex) const +{ + surfaceStructure = m_surfaceStructure; + surfaceNumberOfVertices = m_surfaceNumberOfVertices; + surfaceVertexIndex = m_surfaceVertexIndex; +} + +/** + * Set data for a surface vertex. + * + * @param surfaceStructure + * The surface structure. + * @param surfaceNumberOfVertices + * Number of vertices in the surface. + * @param surfaceVertexIndex + * Index of the vertex. + */ +void +MapFileDataSelector::setSurfaceVertex(const StructureEnum::Enum& surfaceStructure, + const int32_t surfaceNumberOfVertices, + const int32_t surfaceVertexIndex) +{ + reset(); + m_dataSelectionType = DataSelectionType::SURFACE_VERTEX; + m_surfaceStructure = surfaceStructure; + m_surfaceNumberOfVertices = surfaceNumberOfVertices; + m_surfaceVertexIndex = surfaceVertexIndex; +} + +/** + * Get data for a surface vertex average. + * + * @param surfaceStructure + * The surface structure. + * @param surfaceNumberOfVertices + * Number of vertices in the surface. + * @param surfaceVertexAverageIndices + * Indices of the vertex. + */ +void +MapFileDataSelector::getSurfaceVertexAverage(StructureEnum::Enum& surfaceStructure, + int32_t& surfaceNumberOfVertices, + std::vector& surfaceVertexAverageIndices) const +{ + surfaceStructure = m_surfaceStructure; + surfaceNumberOfVertices = m_surfaceNumberOfVertices; + surfaceVertexAverageIndices = m_surfaceVertexAverageIndices; +} + +/** + * Set data for a surface vertex average. + * + * @param surfaceStructure + * The surface structure. + * @param surfaceNumberOfVertices + * Number of vertices in the surface. + * @param surfaceVertexAverageIndices + * Indices of the vertex. + */ +void +MapFileDataSelector::setSurfaceVertexAverage(const StructureEnum::Enum& surfaceStructure, + const int32_t surfaceNumberOfVertices, + const std::vector& surfaceVertexAverageIndices) +{ + reset(); + m_dataSelectionType = DataSelectionType::SURFACE_VERTICES_AVERAGE; + m_surfaceStructure = surfaceStructure; + m_surfaceNumberOfVertices = surfaceNumberOfVertices; + m_surfaceVertexAverageIndices = surfaceVertexAverageIndices; +} + +/** + * Get the stereotaxic coordinate of the volume voxel. + * + * @param voxelXYZ + * The volume voxel XYZ. + */ +void +MapFileDataSelector::getVolumeVoxelXYZ(float voxelXYZ[3]) const +{ + voxelXYZ[0] = m_voxelXYZ[0]; + voxelXYZ[1] = m_voxelXYZ[1]; + voxelXYZ[2] = m_voxelXYZ[2]; +} + +/** + * Set the stereotaxic coordinate of the volume voxel. + * + * @param voxelXYZ + * The volume voxel XYZ. + */ +void +MapFileDataSelector::setVolumeVoxelXYZ(const float voxelXYZ[3]) +{ + reset(); + m_dataSelectionType = DataSelectionType::VOLUME_XYZ; + m_voxelXYZ[0] = voxelXYZ[0]; + m_voxelXYZ[1] = voxelXYZ[1]; + m_voxelXYZ[2] = voxelXYZ[2]; +} + +/** + * Get the column index. + * + * @param columnMapFile + * Data file for column loading. Could be an invalid pointer or NULL !!! + * @param columnMapFileName + * Name of column map file. + * @param columnIndex + * The column index. + */ +void +MapFileDataSelector::getColumnIndex(CaretMappableDataFile* &columnMapFile, + AString& columnMapFileName, + int32_t &columnIndex) const +{ + columnMapFile = m_columnMapFile; + columnMapFileName = m_columnMapFileName; + columnIndex = m_columnIndex; +} + +/** + * Set the column index. + * + * @param columnMapFile + * Data file for column loading. + * @param columnMapFileName + * Name of column map file. + * @param columnIndex + * The column index. + */ +void +MapFileDataSelector::setColumnIndex(CaretMappableDataFile* columnMapFile, + const AString& columnMapFileName, + const int32_t columnIndex) +{ + reset(); + m_dataSelectionType = DataSelectionType::COLUMN_DATA; + m_columnMapFile = columnMapFile; + m_columnMapFileName = columnMapFileName; + m_columnIndex = columnIndex; +} + +/** + * Get the row index. + * + * @param rowMapFile + * Data file for row loading. Could be an invalid pointer or NULL !!! + * @param rowMapFileName + * Name of row map file. + * @param rowIndex + * The row index. + */ +void +MapFileDataSelector::getRowIndex(CaretMappableDataFile* &rowMapFile, + AString& rowMapFileName, + int32_t &rowIndex) const +{ + rowIndex = m_rowIndex; + rowMapFile = m_rowMapFile; + rowMapFileName = m_rowMapFileName; +} + +/** + * Set the row index. + * + * @param rowMapFile + * Data file for row loading. + * @param rowMapFileName + * Name of row map file. + * @param rowIndex + * The row index. + */ +void +MapFileDataSelector::setRowIndex(CaretMappableDataFile* rowMapFile, + const AString& rowMapFileName, + const int32_t rowIndex) +{ + reset(); + m_dataSelectionType = DataSelectionType::ROW_DATA; + m_rowMapFile = rowMapFile; + m_rowMapFileName = rowMapFileName; + m_rowIndex = rowIndex; +} + +/** + * Get a description of this object's content. + * @return String describing this object's content. + */ +AString +MapFileDataSelector::toString() const +{ + AString s; + + switch (m_dataSelectionType) { + case DataSelectionType::INVALID: + break; + case DataSelectionType::COLUMN_DATA: + s = ("Column " + + AString::number(m_columnIndex)); + break; + case DataSelectionType::ROW_DATA: + s = ("Row " + + AString::number(m_rowIndex)); + break; + case DataSelectionType::SURFACE_VERTEX: + s = ("Vertex " + + StructureEnum::toGuiName(m_surfaceStructure) + + " " + + AString::number(m_surfaceVertexIndex + 1)); + break; + case DataSelectionType::SURFACE_VERTICES_AVERAGE: + s = ("Vertex Average" + + StructureEnum::toGuiName(m_surfaceStructure) + + " count=" + + AString::number(m_surfaceVertexAverageIndices.size())); + break; + case DataSelectionType::VOLUME_XYZ: + s = ("Voxel " + + AString::fromNumbers(m_voxelXYZ, 3, ",")); + break; + } + + return s; +} + +/** + * Reset to an invalid state. + */ +void +MapFileDataSelector::reset() +{ + m_dataSelectionType = DataSelectionType::INVALID; + m_surfaceStructure = StructureEnum::INVALID; + m_surfaceNumberOfVertices = -1; + m_surfaceVertexIndex = -1; + m_surfaceVertexAverageIndices.clear(); + m_voxelXYZ[0] = 0.0f; + m_voxelXYZ[1] = 0.0f; + m_voxelXYZ[2] = 0.0f; + m_columnIndex = -1; + m_columnMapFile = NULL; + m_columnMapFileName = ""; + m_rowIndex = -1; + m_rowMapFile = NULL; + m_rowMapFileName = ""; +} + +/** + * Save information specific to this type of model to the scene. + * + * @param sceneAttributes + * Attributes for the scene. Scenes may be of different types + * (full, generic, etc) and the attributes should be checked when + * saving the scene. + * + * @param instanceName + * Name of instance in the scene. + */ +SceneClass* +MapFileDataSelector::saveToScene(const SceneAttributes* sceneAttributes, + const AString& instanceName) +{ + SceneClass* sceneClass = new SceneClass(instanceName, + "MapFileDataSelector", + 1); + m_sceneAssistant->saveMembers(sceneAttributes, + sceneClass); + + sceneClass->addString("m_dataSelectionType", + getDataSelectionTypeName(m_dataSelectionType)); + + const int32_t numVertices = static_cast(m_surfaceVertexAverageIndices.size()); + if (numVertices > 0) { + sceneClass->addIntegerArray("m_surfaceVertexAverageIndices", + &m_surfaceVertexAverageIndices[0], + numVertices); + } + + // Uncomment if sub-classes must save to scene + //saveSubClassDataToScene(sceneAttributes, + // sceneClass); + + return sceneClass; +} + +/** + * Restore information specific to the type of model from the scene. + * + * @param sceneAttributes + * Attributes for the scene. Scenes may be of different types + * (full, generic, etc) and the attributes should be checked when + * restoring the scene. + * + * @param sceneClass + * sceneClass from which model specific information is obtained. + */ +void +MapFileDataSelector::restoreFromScene(const SceneAttributes* sceneAttributes, + const SceneClass* sceneClass) +{ + if (sceneClass == NULL) { + return; + } + + reset(); + + m_sceneAssistant->restoreMembers(sceneAttributes, + sceneClass); + + m_dataSelectionType = DataSelectionType::INVALID; + const AString dataSelectionTypeName = sceneClass->getStringValue("m_dataSelectionType", + "INVALID"); + if (dataSelectionTypeName == getDataSelectionTypeName(DataSelectionType::INVALID)) { + m_dataSelectionType = DataSelectionType::INVALID; + } + else if (dataSelectionTypeName == getDataSelectionTypeName(DataSelectionType::COLUMN_DATA)) { + m_dataSelectionType = DataSelectionType::COLUMN_DATA; + } + else if (dataSelectionTypeName == getDataSelectionTypeName(DataSelectionType::ROW_DATA)) { + m_dataSelectionType = DataSelectionType::ROW_DATA; + } + else if (dataSelectionTypeName == getDataSelectionTypeName(DataSelectionType::SURFACE_VERTEX)) { + m_dataSelectionType = DataSelectionType::SURFACE_VERTEX; + } + else if (dataSelectionTypeName == getDataSelectionTypeName(DataSelectionType::SURFACE_VERTICES_AVERAGE)) { + m_dataSelectionType = DataSelectionType::SURFACE_VERTICES_AVERAGE; + } + else if (dataSelectionTypeName ==getDataSelectionTypeName(DataSelectionType::VOLUME_XYZ)) { + m_dataSelectionType = DataSelectionType::VOLUME_XYZ; + } + else { + CaretAssertMessage(0, ("Invalid data selection type name \"" + + dataSelectionTypeName + + "\"")); + } + + const SceneClassArray* vertexArray = sceneClass->getClassArray("m_surfaceVertexAverageIndices"); + if (vertexArray != NULL) { + const int32_t numVertices = vertexArray->getNumberOfArrayElements(); + if (numVertices > 0) { + m_surfaceVertexAverageIndices.resize(numVertices); + sceneClass->getIntegerArrayValue("m_surfaceVertexAverageIndices", + &m_surfaceVertexAverageIndices[0], + numVertices); + } + } + + //Uncomment if sub-classes must restore from scene + //restoreSubClassDataFromScene(sceneAttributes, + // sceneClass); + +} + diff --git a/src/Charting/MapFileDataSelector.h b/src/Charting/MapFileDataSelector.h new file mode 100644 index 0000000000000000000000000000000000000000..c6ac3e95d3b1125b7c5b1ed76f15f619bfcf96e8 --- /dev/null +++ b/src/Charting/MapFileDataSelector.h @@ -0,0 +1,164 @@ +#ifndef __MAP_FILE_DATA_SELECTOR_H__ +#define __MAP_FILE_DATA_SELECTOR_H__ + +/*LICENSE_START*/ +/* + * Copyright (C) 2017 Washington University School of Medicine + * + * 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 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ +/*LICENSE_END*/ + + + +#include + +#include "CaretObject.h" + +#include "SceneableInterface.h" +#include "StructureEnum.h" + +namespace caret { + class CaretMappableDataFile; + class SceneClassAssistant; + + class MapFileDataSelector : public CaretObject, public SceneableInterface { + + public: + enum class DataSelectionType { + INVALID, + COLUMN_DATA, + ROW_DATA, + SURFACE_VERTEX, + SURFACE_VERTICES_AVERAGE, + VOLUME_XYZ + }; + + MapFileDataSelector(); + + virtual ~MapFileDataSelector(); + + MapFileDataSelector(const MapFileDataSelector& obj); + + MapFileDataSelector& operator=(const MapFileDataSelector& obj); + + DataSelectionType getDataSelectionType() const; + + static AString getDataSelectionTypeName(const DataSelectionType dataSelectionType); + + std::vector getValidTabIndices() const; + + void getSurfaceVertex(StructureEnum::Enum& surfaceStructure, + int32_t& surfaceNumberOfVertices, + int32_t& surfaceVertexIndex) const; + + void setSurfaceVertex(const StructureEnum::Enum& surfaceStructure, + const int32_t surfaceNumberOfVertices, + const int32_t surfaceVertexIndex); + + void getSurfaceVertexAverage(StructureEnum::Enum& surfaceStructure, + int32_t& surfaceNumberOfVertices, + std::vector& surfaceVertexIndices) const; + + void setSurfaceVertexAverage(const StructureEnum::Enum& surfaceStructure, + const int32_t surfaceNumberOfVertices, + const std::vector& surfaceVertexIndices); + + void getVolumeVoxelXYZ(float xyz[3]) const; + + void setVolumeVoxelXYZ(const float xyz[3]); + + void getColumnIndex(CaretMappableDataFile* &columnMapFile, + AString& columnMapFileName, + int32_t &columnIndex) const; + + void setColumnIndex(CaretMappableDataFile* columnMapFile, + const AString& columnMapFileName, + const int32_t columnIndex); + + void getRowIndex(CaretMappableDataFile* &rowMapFile, + AString& rowMapFileName, + int32_t &rowIndex) const; + + void setRowIndex(CaretMappableDataFile* rowMapFile, + const AString& rowMapFileName, + const int32_t rowIndex); + + void reset(); + + // ADD_NEW_METHODS_HERE + + virtual AString toString() const; + + virtual SceneClass* saveToScene(const SceneAttributes* sceneAttributes, + const AString& instanceName); + + virtual void restoreFromScene(const SceneAttributes* sceneAttributes, + const SceneClass* sceneClass); + + + + + + +// If there will be sub-classes of this class that need to save +// and restore data from scenes, these pure virtual methods can +// be uncommented to force their implementation by sub-classes. +// protected: +// virtual void saveSubClassDataToScene(const SceneAttributes* sceneAttributes, +// SceneClass* sceneClass) = 0; +// +// virtual void restoreSubClassDataFromScene(const SceneAttributes* sceneAttributes, +// const SceneClass* sceneClass) = 0; + + private: + void copyHelperMapFileDataSelector(const MapFileDataSelector& obj); + + std::unique_ptr m_sceneAssistant; + + DataSelectionType m_dataSelectionType = DataSelectionType::INVALID; + + int32_t m_surfaceNumberOfVertices = 0; + + StructureEnum::Enum m_surfaceStructure = StructureEnum::INVALID; + + std::vector m_surfaceVertexAverageIndices; + + int32_t m_surfaceVertexIndex = -1; + + float m_voxelXYZ[3]; + + int32_t m_columnIndex = -1; + + int32_t m_rowIndex = -1; + + CaretMappableDataFile* m_rowMapFile; + + CaretMappableDataFile* m_columnMapFile; + + AString m_rowMapFileName; + + AString m_columnMapFileName; + + // ADD_NEW_MEMBERS_HERE + + }; + +#ifdef __MAP_FILE_DATA_SELECTOR_DECLARE__ + // +#endif // __MAP_FILE_DATA_SELECTOR_DECLARE__ + +} // namespace +#endif //__MAP_FILE_DATA_SELECTOR_H__ diff --git a/src/Cifti/CMakeLists.txt b/src/Cifti/CMakeLists.txt index bfe3e8d338b718ecc22ac9a45affd3fbba6076cd..add65dfe6f79ea3aaf5726b709e88569d5eaa946 100644 --- a/src/Cifti/CMakeLists.txt +++ b/src/Cifti/CMakeLists.txt @@ -4,16 +4,18 @@ # PROJECT (Cifti) -# -# Need XML from Qt -# -SET(QT_DONT_USE_QTGUI) -SET(QT_USE_QTNETWORK TRUE) - # # Add QT for includes # -INCLUDE (${QT_USE_FILE}) +if(Qt5_FOUND) + include_directories(${Qt5Core_INCLUDE_DIRS}) + include_directories(${Qt5Network_INCLUDE_DIRS}) +endif() +IF (QT4_FOUND) + SET(QT_DONT_USE_QTGUI) + SET(QT_USE_QTNETWORK TRUE) + INCLUDE(${QT_USE_FILE}) +ENDIF () # # Create GIFTI Library @@ -52,6 +54,8 @@ CiftiSeriesMap.cxx CiftiVersion.cxx ) +TARGET_LINK_LIBRARIES(Cifti ${CARET_QT5_LINK}) + # # Find Headers # diff --git a/src/Cifti/CiftiBrainModelsMap.cxx b/src/Cifti/CiftiBrainModelsMap.cxx index 612de804125c003c17837927e5b136cc3d07a631..d099850d6586fb231eeab5f52d0552ea2298f327 100644 --- a/src/Cifti/CiftiBrainModelsMap.cxx +++ b/src/Cifti/CiftiBrainModelsMap.cxx @@ -20,7 +20,7 @@ #include "CiftiBrainModelsMap.h" -#include "CaretException.h" +#include "DataFileException.h" #include @@ -47,7 +47,7 @@ void CiftiBrainModelsMap::addSurfaceModel(const int64_t& numberOfNodes, const St { if (m_surfUsed.find(structure) != m_surfUsed.end()) { - throw CaretException("surface structures cannot be repeated in a brain models map"); + throw DataFileException("surface structures cannot be repeated in a brain models map"); } BrainModelPriv myModel; myModel.m_type = SURFACE; @@ -63,13 +63,13 @@ void CiftiBrainModelsMap::BrainModelPriv::setupSurface(const int64_t& start) { if (m_surfaceNumberOfNodes < 1) { - throw CaretException("surface must have at least 1 vertex"); + throw DataFileException("surface must have at least 1 vertex"); } m_modelStart = start; int64_t listSize = (int64_t)m_nodeIndices.size(); if (listSize == 0) { - throw CaretException("vertex list must have nonzero length");//NOTE: technically not required by Cifti-1, remove if problematic + throw DataFileException("vertex list must have nonzero length");//NOTE: technically not required by Cifti-1, remove if problematic } m_modelEnd = start + listSize;//one after last vector used(m_surfaceNumberOfNodes, false); @@ -78,15 +78,15 @@ void CiftiBrainModelsMap::BrainModelPriv::setupSurface(const int64_t& start) { if (m_nodeIndices[i] < 0) { - throw CaretException("vertex list contains negative index"); + throw DataFileException("vertex list contains negative index"); } if (m_nodeIndices[i] >= m_surfaceNumberOfNodes) { - throw CaretException("vertex list contains an index that don't exist in the surface"); + throw DataFileException("vertex list contains an index that don't exist in the surface"); } if (used[m_nodeIndices[i]]) { - throw CaretException("vertex list contains reused index"); + throw DataFileException("vertex list contains reused index"); } used[m_nodeIndices[i]] = true; m_nodeToIndexLookup[m_nodeIndices[i]] = start + i; @@ -97,16 +97,16 @@ void CiftiBrainModelsMap::addVolumeModel(const StructureEnum::Enum& structure, c { if (m_volUsed.find(structure) != m_volUsed.end()) { - throw CaretException("volume structures cannot be repeated in a brain models map"); + throw DataFileException("volume structures cannot be repeated in a brain models map"); } int64_t listSize = (int64_t)ijkList.size(); if (listSize == 0) { - throw CaretException("voxel list must have nonzero length");//NOTE: technically not required by Cifti-1, remove if problematic + throw DataFileException("voxel list must have nonzero length");//NOTE: technically not required by Cifti-1, remove if problematic } if (listSize % 3 != 0) { - throw CaretException("voxel list must have a length that is a multiple of 3"); + throw DataFileException("voxel list must have a length that is a multiple of 3"); } int64_t numElems = listSize / 3; const int64_t* dims = NULL; @@ -114,7 +114,7 @@ void CiftiBrainModelsMap::addVolumeModel(const StructureEnum::Enum& structure, c { if (!m_haveVolumeSpace) { - throw CaretException("you must set the volume space before adding volume models"); + throw DataFileException("you must set the volume space before adding volume models"); } dims = m_volSpace.getDims(); } @@ -125,18 +125,18 @@ void CiftiBrainModelsMap::addVolumeModel(const StructureEnum::Enum& structure, c int64_t index3 = index * 3; if (ijkList[index3] < 0 || ijkList[index3 + 1] < 0 || ijkList[index3 + 2] < 0) { - throw CaretException("found negative index in voxel list"); + throw DataFileException("found negative index in voxel list"); } if (!m_ignoreVolSpace && (ijkList[index3] >= dims[0] || ijkList[index3 + 1] >= dims[1] || ijkList[index3 + 2] >= dims[2])) { - throw CaretException("found invalid index triple in voxel list: (" + AString::number(ijkList[index3]) + ", " + throw DataFileException("found invalid index triple in voxel list: (" + AString::number(ijkList[index3]) + ", " + AString::number(ijkList[index3 + 1]) + ", " + AString::number(ijkList[index3 + 2]) + ")"); } if (tempLookup.find(ijkList[index3], ijkList[index3 + 1], ijkList[index3 + 2]) != NULL) { - throw CaretException("volume models may not reuse voxels, either internally or from other structures"); + throw DataFileException("volume models may not reuse voxels, either internally or from other structures"); } tempLookup.at(ijkList[index3], ijkList[index3 + 1], ijkList[index3 + 2]) = pair(nextStart + index, structure); } @@ -257,7 +257,7 @@ const vector& CiftiBrainModelsMap::getNodeList(const StructureEnum::Enu map::const_iterator iter = m_surfUsed.find(structure); if (iter == m_surfUsed.end()) { - throw CaretException("getNodeList called for nonexistant structure");//throw if it doesn't exist, because we don't have a reference to return - things should identify which structures exist before calling this + throw DataFileException("getNodeList called for nonexistant structure");//throw if it doesn't exist, because we don't have a reference to return - things should identify which structures exist before calling this } CaretAssertVectorIndex(m_modelsInfo, iter->second); return m_modelsInfo[iter->second].m_nodeIndices; @@ -269,7 +269,7 @@ vector CiftiBrainModelsMap::getSurfaceMap(const map::const_iterator iter = m_surfUsed.find(structure); if (iter == m_surfUsed.end()) { - throw CaretException("getSurfaceMap called for nonexistant structure");//also throw, for consistency + throw DataFileException("getSurfaceMap called for nonexistant structure");//also throw, for consistency } CaretAssertVectorIndex(m_modelsInfo, iter->second); const BrainModelPriv& myModel = m_modelsInfo[iter->second]; @@ -349,7 +349,7 @@ const VolumeSpace& CiftiBrainModelsMap::getVolumeSpace() const CaretAssert(!m_ignoreVolSpace); if (!m_haveVolumeSpace) { - throw CaretException("getVolumeSpace called when no volume space exists"); + throw DataFileException("getVolumeSpace called when no volume space exists"); } return m_volSpace; } @@ -375,7 +375,7 @@ vector CiftiBrainModelsMap::getVolumeStructureMa map::const_iterator iter = m_volUsed.find(structure); if (iter == m_volUsed.end()) { - throw CaretException("getVolumeStructureMap called for nonexistant structure");//also throw, for consistency + throw DataFileException("getVolumeStructureMap called for nonexistant structure");//also throw, for consistency } CaretAssertVectorIndex(m_modelsInfo, iter->second); const BrainModelPriv& myModel = m_modelsInfo[iter->second]; @@ -399,7 +399,7 @@ const vector& CiftiBrainModelsMap::getVoxelList(const StructureEnum::En map::const_iterator iter = m_volUsed.find(structure); if (iter == m_volUsed.end()) { - throw CaretException("getVoxelList called for nonexistant structure");//throw if it doesn't exist, because we don't have a reference to return - things should identify which structures exist before calling this + throw DataFileException("getVoxelList called for nonexistant structure");//throw if it doesn't exist, because we don't have a reference to return - things should identify which structures exist before calling this } CaretAssertVectorIndex(m_modelsInfo, iter->second); return m_modelsInfo[iter->second].m_voxelIndicesIJK; @@ -428,7 +428,7 @@ void CiftiBrainModelsMap::setVolumeSpace(const VolumeSpace& space) { if (!space.indexValid(myModel.m_voxelIndicesIJK[i3], myModel.m_voxelIndicesIJK[i3 + 1], myModel.m_voxelIndicesIJK[i3 + 2])) { - throw CaretException("invalid voxel found for volume space"); + throw DataFileException("invalid voxel found for volume space"); } } } @@ -474,6 +474,46 @@ bool CiftiBrainModelsMap::approximateMatch(const CiftiMappingType& rhs, QString* return true; } +/** + * @return EQUAL if 'this' and 'rhs' contain the same models + * SUBSET if each model in 'this' is contained in 'rhs' and 'rhs' contains models not in 'this' + * NO if 'this' contains a model not in 'rhs' + */ +CiftiBrainModelsMap::MatchResult CiftiBrainModelsMap::testMatch(const CiftiMappingType& rhs) const +{ + if (rhs.getType() != getType()) return CiftiBrainModelsMap::MatchResult::NO; + const CiftiBrainModelsMap& myrhs = dynamic_cast(rhs); + CaretAssert(!m_ignoreVolSpace && !myrhs.m_ignoreVolSpace);//these should only be true while in the process of parsing cifti-1, never otherwise + if (m_haveVolumeSpace + && ( ! myrhs.m_haveVolumeSpace)) return CiftiBrainModelsMap::MatchResult::NO; + if (m_haveVolumeSpace && (m_volSpace != myrhs.m_volSpace)) return CiftiBrainModelsMap::MatchResult::NO; + + for (const auto& modelInfo : m_modelsInfo) { + bool matched = false; + for (const auto& rhsModelInfo : myrhs.m_modelsInfo) { + if (modelInfo == rhsModelInfo) { + matched = true; + break; + } + } + if (! matched) { + return CiftiBrainModelsMap::MatchResult::NO; + } + } + + if (m_modelsInfo.size() == myrhs.m_modelsInfo.size()) { + /* + * A note in the equality operator indicates that models are + * sorted by index range so if 'this' and 'rhs' have the + * exact same models, they must be equal. The assertion + * will fail if this wrong. + */ + CaretAssert(*this == rhs); + return CiftiBrainModelsMap::MatchResult::EQUAL; + } + return CiftiBrainModelsMap::MatchResult::SUBSET; +} + bool CiftiBrainModelsMap::BrainModelPriv::operator==(const BrainModelPriv& rhs) const { if (m_brainStructure != rhs.m_brainStructure) return false; @@ -514,7 +554,7 @@ void CiftiBrainModelsMap::readXML1(QXmlStreamReader& xml) QStringRef name = xml.name(); if (name != "BrainModel") { - throw CaretException("unexpected element in brain models map: " + name.toString()); + throw DataFileException("unexpected element in brain models map: " + name.toString()); } ParseHelperModel thisModel; thisModel.parseBrainModel1(xml); @@ -536,9 +576,9 @@ void CiftiBrainModelsMap::readXML1(QXmlStreamReader& xml) { if (parsedModels[i].m_offset < curOffset) { - throw CaretException("models overlap at index " + QString::number(parsedModels[i].m_offset) + ", model " + QString::number(i)); + throw DataFileException("models overlap at index " + QString::number(parsedModels[i].m_offset) + ", model " + QString::number(i)); } else { - throw CaretException("index " + QString::number(curOffset) + " is not assigned to any model"); + throw DataFileException("index " + QString::number(curOffset) + " is not assigned to any model"); } } curOffset += parsedModels[i].m_count; @@ -579,14 +619,14 @@ void CiftiBrainModelsMap::readXML2(QXmlStreamReader& xml) } else if (name == "Volume") { if (m_haveVolumeSpace) { - throw CaretException("Volume specified more than once in Brain Models mapping type"); + throw DataFileException("Volume specified more than once in Brain Models mapping type"); } else { m_volSpace.readCiftiXML2(xml); if (xml.hasError()) return; m_haveVolumeSpace = true; } } else { - throw CaretException("unexpected element in brain models map: " + name.toString()); + throw DataFileException("unexpected element in brain models map: " + name.toString()); } break;//the readNext in the for will remove the BrainModel or Volume end element } @@ -604,9 +644,9 @@ void CiftiBrainModelsMap::readXML2(QXmlStreamReader& xml) { if (parsedModels[i].m_offset < curOffset) { - throw CaretException("models overlap at index " + QString::number(parsedModels[i].m_offset) + ", model " + QString::number(i)); + throw DataFileException("models overlap at index " + QString::number(parsedModels[i].m_offset) + ", model " + QString::number(i)); } else { - throw CaretException("index " + QString::number(curOffset) + " is not assigned to any model"); + throw DataFileException("index " + QString::number(curOffset) + " is not assigned to any model"); } } curOffset += parsedModels[i].m_count; @@ -631,7 +671,7 @@ void CiftiBrainModelsMap::ParseHelperModel::parseBrainModel1(QXmlStreamReader& x QXmlStreamAttributes attrs = xml.attributes(); if (!attrs.hasAttribute("ModelType")) { - throw CaretException("BrainModel missing required attribute ModelType"); + throw DataFileException("BrainModel missing required attribute ModelType"); } QStringRef value = attrs.value("ModelType"); if (value == "CIFTI_MODEL_TYPE_SURFACE") @@ -640,50 +680,50 @@ void CiftiBrainModelsMap::ParseHelperModel::parseBrainModel1(QXmlStreamReader& x } else if (value == "CIFTI_MODEL_TYPE_VOXELS") { m_type = VOXELS; } else { - throw CaretException("invalid value for ModelType: " + value.toString()); + throw DataFileException("invalid value for ModelType: " + value.toString()); } if (!attrs.hasAttribute("BrainStructure")) { - throw CaretException("BrainModel missing required attribute BrainStructure"); + throw DataFileException("BrainModel missing required attribute BrainStructure"); } value = attrs.value("BrainStructure"); bool ok = false; m_brainStructure = StructureEnum::fromCiftiName(value.toString(), &ok); if (!ok) { - throw CaretException("invalid value for BrainStructure: " + value.toString()); + throw DataFileException("invalid value for BrainStructure: " + value.toString()); } if (!attrs.hasAttribute("IndexOffset")) { - throw CaretException("BrainModel missing required attribute IndexOffset"); + throw DataFileException("BrainModel missing required attribute IndexOffset"); } value = attrs.value("IndexOffset"); m_offset = value.toString().toLongLong(&ok); if (!ok || m_offset < 0) { - throw CaretException("IndexOffset must be a non-negative integer"); + throw DataFileException("IndexOffset must be a non-negative integer"); } if (!attrs.hasAttribute("IndexCount")) { - throw CaretException("BrainModel missing required attribute IndexCount"); + throw DataFileException("BrainModel missing required attribute IndexCount"); } value = attrs.value("IndexCount"); m_count = value.toString().toLongLong(&ok); if (!ok || m_count < 1)//NOTE: not technically required by cifti-1, would need some rewriting to support empty brainmodels { - throw CaretException("IndexCount must be a positive integer"); + throw DataFileException("IndexCount must be a positive integer"); } if (m_type == SURFACE) { if (!attrs.hasAttribute("SurfaceNumberOfNodes")) { - throw CaretException("BrainModel missing required attribute SurfaceNumberOfNodes"); + throw DataFileException("BrainModel missing required attribute SurfaceNumberOfNodes"); } value = attrs.value("SurfaceNumberOfNodes"); m_surfaceNumberOfNodes = value.toString().toLongLong(&ok); if (!ok || m_surfaceNumberOfNodes < 1) { - throw CaretException("SurfaceNumberOfNodes must be a positive integer"); + throw DataFileException("SurfaceNumberOfNodes must be a positive integer"); } if (!xml.readNextStartElement())//special case in cifti-1 { @@ -695,7 +735,7 @@ void CiftiBrainModelsMap::ParseHelperModel::parseBrainModel1(QXmlStreamReader& x } else { if (xml.name() != "NodeIndices") { - throw CaretException("unexpected element in BrainModel of SURFACE type: " + xml.name().toString()); + throw DataFileException("unexpected element in BrainModel of SURFACE type: " + xml.name().toString()); } m_nodeIndices = readIndexArray(xml); xml.readNext();//remove the end element of NodeIndices @@ -703,26 +743,26 @@ void CiftiBrainModelsMap::ParseHelperModel::parseBrainModel1(QXmlStreamReader& x if (xml.hasError()) return; if ((int64_t)m_nodeIndices.size() != m_count) { - throw CaretException("number of vertex indices does not match IndexCount"); + throw DataFileException("number of vertex indices does not match IndexCount"); } } else { if (!xml.readNextStartElement()) { - throw CaretException("BrainModel requires a child element"); + throw DataFileException("BrainModel requires a child element"); } if (xml.name() != "VoxelIndicesIJK") { - throw CaretException("unexpected element in BrainModel of VOXELS type: " + xml.name().toString()); + throw DataFileException("unexpected element in BrainModel of VOXELS type: " + xml.name().toString()); } m_voxelIndicesIJK = readIndexArray(xml); if (xml.hasError()) return; if (m_voxelIndicesIJK.size() % 3 != 0) { - throw CaretException("number of voxel indices is not a multiple of 3"); + throw DataFileException("number of voxel indices is not a multiple of 3"); } if ((int64_t)m_voxelIndicesIJK.size() != m_count * 3) { - throw CaretException("number of voxel indices does not match IndexCount"); + throw DataFileException("number of voxel indices does not match IndexCount"); } xml.readNext();//remove the end element of VoxelIndicesIJK } @@ -731,7 +771,7 @@ void CiftiBrainModelsMap::ParseHelperModel::parseBrainModel1(QXmlStreamReader& x switch(xml.readNext()) { case QXmlStreamReader::StartElement: - throw CaretException("unexpected second element in BrainModel: " + xml.name().toString()); + throw DataFileException("unexpected second element in BrainModel: " + xml.name().toString()); default: break; } @@ -744,7 +784,7 @@ void CiftiBrainModelsMap::ParseHelperModel::parseBrainModel2(QXmlStreamReader& x QXmlStreamAttributes attrs = xml.attributes(); if (!attrs.hasAttribute("ModelType")) { - throw CaretException("BrainModel missing required attribute ModelType"); + throw DataFileException("BrainModel missing required attribute ModelType"); } QStringRef value = attrs.value("ModelType"); if (value == "CIFTI_MODEL_TYPE_SURFACE") @@ -753,84 +793,84 @@ void CiftiBrainModelsMap::ParseHelperModel::parseBrainModel2(QXmlStreamReader& x } else if (value == "CIFTI_MODEL_TYPE_VOXELS") { m_type = VOXELS; } else { - throw CaretException("invalid value for ModelType: " + value.toString()); + throw DataFileException("invalid value for ModelType: " + value.toString()); } if (!attrs.hasAttribute("BrainStructure")) { - throw CaretException("BrainModel missing required attribute BrainStructure"); + throw DataFileException("BrainModel missing required attribute BrainStructure"); } value = attrs.value("BrainStructure"); bool ok = false; m_brainStructure = StructureEnum::fromCiftiName(value.toString(), &ok); if (!ok) { - throw CaretException("invalid value for BrainStructure: " + value.toString()); + throw DataFileException("invalid value for BrainStructure: " + value.toString()); } if (!attrs.hasAttribute("IndexOffset")) { - throw CaretException("BrainModel missing required attribute IndexOffset"); + throw DataFileException("BrainModel missing required attribute IndexOffset"); } value = attrs.value("IndexOffset"); m_offset = value.toString().toLongLong(&ok); if (!ok || m_offset < 0) { - throw CaretException("IndexOffset must be a non-negative integer"); + throw DataFileException("IndexOffset must be a non-negative integer"); } if (!attrs.hasAttribute("IndexCount")) { - throw CaretException("BrainModel missing required attribute IndexCount"); + throw DataFileException("BrainModel missing required attribute IndexCount"); } value = attrs.value("IndexCount"); m_count = value.toString().toLongLong(&ok); if (!ok || m_count < 1) { - throw CaretException("IndexCount must be a positive integer"); + throw DataFileException("IndexCount must be a positive integer"); } if (m_type == SURFACE) { if (!attrs.hasAttribute("SurfaceNumberOfVertices")) { - throw CaretException("BrainModel missing required attribute SurfaceNumberOfVertices"); + throw DataFileException("BrainModel missing required attribute SurfaceNumberOfVertices"); } value = attrs.value("SurfaceNumberOfVertices"); m_surfaceNumberOfNodes = value.toString().toLongLong(&ok); if (!ok || m_surfaceNumberOfNodes < 1) { - throw CaretException("SurfaceNumberOfVertices must be a positive integer"); + throw DataFileException("SurfaceNumberOfVertices must be a positive integer"); } if (!xml.readNextStartElement()) { - throw CaretException("BrainModel requires a child element"); + throw DataFileException("BrainModel requires a child element"); } if (xml.name() != "VertexIndices") { - throw CaretException("unexpected element in BrainModel of SURFACE type: " + xml.name().toString()); + throw DataFileException("unexpected element in BrainModel of SURFACE type: " + xml.name().toString()); } m_nodeIndices = readIndexArray(xml); if (xml.hasError()) return; if ((int64_t)m_nodeIndices.size() != m_count) { - throw CaretException("number of vertex indices does not match IndexCount"); + throw DataFileException("number of vertex indices does not match IndexCount"); } xml.readNext();//remove the end element of NodeIndices } else { if (!xml.readNextStartElement()) { - throw CaretException("BrainModel requires a child element"); + throw DataFileException("BrainModel requires a child element"); } if (xml.name() != "VoxelIndicesIJK") { - throw CaretException("unexpected element in BrainModel of VOXELS type: " + xml.name().toString()); + throw DataFileException("unexpected element in BrainModel of VOXELS type: " + xml.name().toString()); } m_voxelIndicesIJK = readIndexArray(xml); if (xml.hasError()) return; if (m_voxelIndicesIJK.size() % 3 != 0) { - throw CaretException("number of voxel indices is not a multiple of 3"); + throw DataFileException("number of voxel indices is not a multiple of 3"); } if ((int64_t)m_voxelIndicesIJK.size() != m_count * 3) { - throw CaretException("number of voxel indices does not match IndexCount"); + throw DataFileException("number of voxel indices does not match IndexCount"); } xml.readNext();//remove the end element of VoxelIndicesIJK } @@ -839,7 +879,7 @@ void CiftiBrainModelsMap::ParseHelperModel::parseBrainModel2(QXmlStreamReader& x switch(xml.readNext()) { case QXmlStreamReader::StartElement: - throw CaretException("unexpected second element in BrainModel: " + xml.name().toString()); + throw DataFileException("unexpected second element in BrainModel: " + xml.name().toString()); default: break; } @@ -861,11 +901,11 @@ vector CiftiBrainModelsMap::ParseHelperModel::readIndexArray(QXmlStream ret.push_back(separated[i].toLongLong(&ok)); if (!ok) { - throw CaretException("found noninteger in index array: " + separated[i]); + throw DataFileException("found noninteger in index array: " + separated[i]); } if (ret.back() < 0) { - throw CaretException("found negative integer in index array: " + separated[i]); + throw DataFileException("found negative integer in index array: " + separated[i]); } } return ret; diff --git a/src/Cifti/CiftiBrainModelsMap.h b/src/Cifti/CiftiBrainModelsMap.h index cad305b77841ecde73828017a760581f5a0347a1..a76baaecb1b2f3c7cfa6ce8b50dc3ec89bfd43a8 100644 --- a/src/Cifti/CiftiBrainModelsMap.h +++ b/src/Cifti/CiftiBrainModelsMap.h @@ -41,6 +41,12 @@ namespace caret SURFACE, VOXELS }; + enum class MatchResult + { + EQUAL, + NO, + SUBSET + }; struct SurfaceMap { int64_t m_ciftiIndex; @@ -94,6 +100,7 @@ namespace caret int64_t getLength() const; bool operator==(const CiftiMappingType& rhs) const; bool approximateMatch(const CiftiMappingType& rhs, QString* explanation = NULL) const; + MatchResult testMatch(const CiftiMappingType& rhs) const; void readXML1(QXmlStreamReader& xml); void readXML2(QXmlStreamReader& xml); void writeXML1(QXmlStreamWriter& xml) const; diff --git a/src/Cifti/CiftiFile.cxx b/src/Cifti/CiftiFile.cxx index aead6aa807c384d5c6a4c9593be6bfbbc33ed3b4..dca8229c72594877d99138a15db0ae192716ade5 100644 --- a/src/Cifti/CiftiFile.cxx +++ b/src/Cifti/CiftiFile.cxx @@ -42,7 +42,8 @@ namespace CiftiXML m_xml;//because we need to parse it to set up the dimensions anyway public: CiftiOnDiskImpl(const QString& filename);//read-only - CiftiOnDiskImpl(const QString& filename, const CiftiXML& xml, const CiftiVersion& version, const bool& swapEndian);//make new empty file with read/write + CiftiOnDiskImpl(const QString& filename, const CiftiXML& xml, const CiftiVersion& version, const bool& swapEndian, + const int16_t& datatype, const bool& rescale, const double& minval, const double& maxval);//make new empty file with read/write void getRow(float* dataOut, const std::vector& indexSelect, const bool& tolerateShortRead) const; void getColumn(float* dataOut, const int64_t& index) const; const CiftiXML& getCiftiXML() const { return m_xml; } @@ -50,6 +51,7 @@ namespace bool isSwapped() const { return m_nifti.getHeader().isSwapped(); } void setRow(const float* dataIn, const std::vector& indexSelect); void setColumn(const float* dataIn, const int64_t& index); + void close(); }; class CiftiMemoryImpl : public CiftiFile::WriteImplInterface @@ -108,14 +110,13 @@ CiftiFile::WriteImplInterface::~WriteImplInterface() CiftiFile::CiftiFile(const QString& fileName) { m_endianPref = NATIVE; + setWritingDataTypeNoScaling();//default argument is float32 openFile(fileName); } void CiftiFile::openFile(const QString& fileName) { - m_writingImpl.grabNew(NULL); - m_readingImpl.grabNew(NULL);//to make sure it closes everything first, even if the open throws - m_dims.clear(); + close();//to make sure it closes everything first, even if the open throws CaretPointer newRead(new CiftiOnDiskImpl(FileInformation(fileName).getAbsoluteFilePath()));//this constructor opens existing file read-only m_readingImpl = newRead;//it should be noted that if the constructor throws (if the file isn't readable), new guarantees the memory allocated for the object will be freed m_xml = newRead->getCiftiXML(); @@ -126,9 +127,7 @@ void CiftiFile::openFile(const QString& fileName) void CiftiFile::openURL(const QString& url, const QString& user, const QString& pass) { - m_writingImpl.grabNew(NULL); - m_readingImpl.grabNew(NULL);//to make sure it closes everything first, even if the open throws - m_dims.clear(); + close();//to make sure it closes everything first, even if the open throws CaretPointer newRead(new CiftiXnatImpl(url, user, pass)); m_readingImpl = newRead; m_xml = newRead->getCiftiXML(); @@ -138,9 +137,7 @@ void CiftiFile::openURL(const QString& url, const QString& user, const QString& void CiftiFile::openURL(const QString& url) { - m_writingImpl.grabNew(NULL); - m_readingImpl.grabNew(NULL);//to make sure it closes everything first, even if the open throws - m_dims.clear(); + close();//to make sure it closes everything first, even if the open throws CaretPointer newRead(new CiftiXnatImpl(url)); m_readingImpl = newRead; m_xml = newRead->getCiftiXML(); @@ -157,6 +154,24 @@ void CiftiFile::setWritingFile(const QString& fileName, const CiftiVersion& writ m_endianPref = endian; } +void CiftiFile::setWritingDataTypeNoScaling(const int16_t& type) +{ + m_writingDataType = type;//could do some validation here + m_doWriteScaling = false; + m_minScalingVal = -1.0; + m_maxScalingVal = 1.0; + m_writingImpl.grabNew(NULL);//prevent writing to previous writing implementation, let the next set...() set up for writing +} + +void CiftiFile::setWritingDataTypeAndScaling(const int16_t& type, const double& minval, const double& maxval) +{ + m_writingDataType = type;//could do some validation here + m_doWriteScaling = true; + m_minScalingVal = minval; + m_maxScalingVal = maxval; + m_writingImpl.grabNew(NULL);//prevent writing to previous writing implementation, let the next set...() set up for writing +} + void CiftiFile::writeFile(const QString& fileName, const CiftiVersion& writingVersion, const ENDIAN& endian) { if (m_readingImpl == NULL || m_dims.empty()) throw DataFileException("writeFile called on uninitialized CiftiFile"); @@ -174,7 +189,8 @@ void CiftiFile::writeFile(const QString& fileName, const CiftiVersion& writingVe m_readingImpl = tempMemory;//we are about to make the old reading impl very unhappy, replace it so that if we get an error while writing, we hang onto the memory version m_writingImpl.grabNew(NULL);//and make it re-magic the writing implementation again if data is set } - CaretPointer tempWrite(new CiftiOnDiskImpl(myInfo.getAbsoluteFilePath(), m_xml, writingVersion, writeSwapped)); + CaretPointer tempWrite(new CiftiOnDiskImpl(myInfo.getAbsoluteFilePath(), m_xml, writingVersion, writeSwapped, + m_writingDataType, m_doWriteScaling, m_minScalingVal, m_maxScalingVal)); copyImplData(m_readingImpl, tempWrite, m_dims); if (collision)//if we rewrote the file, we need the handle to the new file, and to dump the temporary in-memory version { @@ -188,6 +204,23 @@ void CiftiFile::writeFile(const QString& fileName, const CiftiVersion& writingVe m_xml.clearMutablesModified(); } +void CiftiFile::close() +{ + if (m_writingImpl != NULL) + { + m_writingImpl->close();//only writing implementations should ever throw errors on close, and specifically only on-disk + } + m_writingImpl.grabNew(NULL); + m_readingImpl.grabNew(NULL); + m_dims.clear(); + m_xml = CiftiXML(); + m_writingFile = ""; + m_fileName = ""; + m_onDiskVersion = CiftiVersion();//for completeness, it gets reset on open anyway + m_endianPref = NATIVE;//reset things to defaults + setWritingDataTypeNoScaling();//default argument is float32 +} + void CiftiFile::convertToInMemory() { if (isInMemory()) return; @@ -226,9 +259,14 @@ void CiftiFile::getColumn(float* dataOut, const int64_t& index) const void CiftiFile::setCiftiXML(const CiftiXML& xml, const bool useOldMetadata) { + if (xml.getNumberOfDimensions() == 0) throw DataFileException("setCiftiXML called with 0-dimensional CiftiXML"); + vector xmlDims = xml.getDimensions(); + for (size_t i = 0; i < xmlDims.size(); ++i) + { + if (xmlDims[i] < 1) throw DataFileException("cifti xml dimensions must be greater than zero"); + } m_readingImpl.grabNew(NULL);//drop old implementation, as it is now invalid due to XML (and therefore matrix size) change m_writingImpl.grabNew(NULL); - if (xml.getNumberOfDimensions() == 0) throw DataFileException("setCiftiXML called with 0-dimensional CiftiXML"); if (useOldMetadata) { const GiftiMetaData* oldmd = m_xml.getFileMetaData(); @@ -248,11 +286,7 @@ void CiftiFile::setCiftiXML(const CiftiXML& xml, const bool useOldMetadata) } else { m_xml = xml; } - m_dims = m_xml.getDimensions(); - for (size_t i = 0; i < m_dims.size(); ++i) - { - if (m_dims[i] < 1) throw DataFileException("cifti xml dimensions must be greater than zero"); - } + m_dims = xmlDims; } void CiftiFile::setCiftiXML(const CiftiXMLOld& xml, const bool useOldMetadata) @@ -261,12 +295,12 @@ void CiftiFile::setCiftiXML(const CiftiXMLOld& xml, const bool useOldMetadata) xml.writeXML(xmlText); CiftiXML tempXML;//so that we can use the same code path tempXML.readXML(xmlText); - if (tempXML.getDimensionLength(CiftiXML::ALONG_ROW) < 1) + if (tempXML.getDimensionLength(CiftiXML::ALONG_ROW) < 0) { CiftiSeriesMap& tempMap = tempXML.getSeriesMap(CiftiXML::ALONG_ROW); tempMap.setLength(xml.getDimensionLength(CiftiXMLOld::ALONG_ROW)); } - if (tempXML.getDimensionLength(CiftiXML::ALONG_COLUMN) < 1) + if (tempXML.getDimensionLength(CiftiXML::ALONG_COLUMN) < 0) { CiftiSeriesMap& tempMap = tempXML.getSeriesMap(CiftiXML::ALONG_COLUMN); tempMap.setLength(xml.getDimensionLength(CiftiXMLOld::ALONG_COLUMN)); @@ -351,7 +385,8 @@ void CiftiFile::verifyWriteImpl() } } } - m_writingImpl.grabNew(new CiftiOnDiskImpl(m_writingFile, m_xml, m_onDiskVersion, shouldSwap(m_endianPref)));//this constructor makes new file for writing + m_writingImpl.grabNew(new CiftiOnDiskImpl(m_writingFile, m_xml, m_onDiskVersion, shouldSwap(m_endianPref), + m_writingDataType, m_doWriteScaling, m_minScalingVal, m_maxScalingVal));//this constructor makes new file for writing if (m_readingImpl != NULL) { copyImplData(m_readingImpl, m_writingImpl, m_dims); @@ -469,7 +504,7 @@ CiftiOnDiskImpl::CiftiOnDiskImpl(const QString& filename) if (m_xml.getNumberOfDimensions() + 4 != (int)dimCheck.size()) throw DataFileException("XML does not match number of nifti dimensions in file " + filename + "'"); for (int i = 4; i < (int)dimCheck.size(); ++i) { - if (m_xml.getDimensionLength(i - 4) < 1)//CiftiXML will only let this happen with cifti-1 + if (m_xml.getDimensionLength(i - 4) < 0)//CiftiXML will only let this happen with cifti-1 { m_xml.getSeriesMap(i - 4).setLength(dimCheck[i]);//and only in a series map } else { @@ -489,10 +524,19 @@ namespace int32_t intent_code = myXML.getIntentInfo(CiftiVersion(), junk);//use default writing version to check file extension, older version is missing some intent codes switch (intent_code) { + default: + CaretLogWarning("unhandled cifti type in extension warning check, tell the developers what you just tried to do"); + CaretAssert(0);//yes, let it fall through to "unknown" in release so that it at least looks for .nii case 3000://unknown if (!filename.contains(QRegExp("\\.[^.]*\\.nii$"))) { - CaretLogWarning("cifti file of nonstandard mapping combination '" + filename + "' should be saved ending in ..nii, see wb_command -cifti-help"); + CaretLogWarning("cifti file of nonstandard mapping combination '" + filename + "' should be saved ending in ..nii, " + + "but not an already used extension (don't use dtseries, dscalar, etc)."); + } + if (filename.contains(QRegExp("\\.(dconn|dtseries|pconn|ptseries|dscalar|dfan|fiberTEMP|dlabel|pscalar|pdconn|dpconn|pconnseries|pconnscalar)\\.nii$"))) + { + CaretLogWarning("cifti file of nonstandard mapping combination '" + filename + "' should NOT be saved using an already-used cifti extension, " + + "please choose a different, reasonable cifti extension ending in ..nii"); } break; case 3001: @@ -562,18 +606,25 @@ namespace CaretLogWarning("parcels by parcels by scalar cifti file '" + filename + "' should be saved ending in .pconnscalar.nii, see wb_command -cifti-help"); } break; - default: - CaretAssert(0); - throw CaretException("internal error, tell the developers what you just tried to do"); } } } -CiftiOnDiskImpl::CiftiOnDiskImpl(const QString& filename, const CiftiXML& xml, const CiftiVersion& version, const bool& swapEndian) +CiftiOnDiskImpl::CiftiOnDiskImpl(const QString& filename, const CiftiXML& xml, const CiftiVersion& version, const bool& swapEndian, + const int16_t& datatype, const bool& rescale, const double& minval, const double& maxval) {//starts writing new file warnForBadExtension(filename, xml); NiftiHeader outHeader; - outHeader.setDataType(NIFTI_TYPE_FLOAT32);//actually redundant currently, default is float32 + if (rescale) + { + outHeader.setDataTypeAndScaleRange(datatype, minval, maxval); + } else { + outHeader.setDataType(datatype); + } + if (outHeader.getNumComponents() != 1) + { + throw DataFileException("cifti cannot be written with multi-component nifti datatypes (i.e., complex, RGB)"); + } char intentName[16]; int32_t intentCode = xml.getIntentInfo(version, intentName); outHeader.setIntent(intentCode, intentName); @@ -607,6 +658,12 @@ CiftiOnDiskImpl::CiftiOnDiskImpl(const QString& filename, const CiftiXML& xml, c m_xml = xml; } +void CiftiOnDiskImpl::close() +{ + m_nifti.close();//lets this throw when there is a writing problem +}//don't bother resetting m_xml, this instance is about to be destroyed + + void CiftiOnDiskImpl::getRow(float* dataOut, const vector& indexSelect, const bool& tolerateShortRead) const { m_nifti.readData(dataOut, 5, indexSelect, tolerateShortRead);//5 means 4 reserved (space and time) plus the first cifti dimension diff --git a/src/Cifti/CiftiFile.h b/src/Cifti/CiftiFile.h index 3afe3f472300c1abbbc573029fc84348106ac211..62119ff1366b0a8153d13d5c221fbc89574ca6b1 100644 --- a/src/Cifti/CiftiFile.h +++ b/src/Cifti/CiftiFile.h @@ -26,6 +26,7 @@ #include "CiftiXML.h" #include "CiftiXMLOld.h" #include "MultiDimIterator.h" +#include "nifti1.h" #include @@ -45,13 +46,18 @@ namespace caret BIG }; - CiftiFile() { m_endianPref = NATIVE; } + CiftiFile() + { + m_endianPref = NATIVE; + setWritingDataTypeNoScaling();//default argument is float32 + } explicit CiftiFile(const QString &fileName);//calls openFile void openFile(const QString& fileName);//starts on-disk reading void openURL(const QString& url, const QString& user, const QString& pass);//open from XNAT void openURL(const QString& url);//same, without user/pass (or curently, reusing existing auth if the server matches void setWritingFile(const QString& fileName, const CiftiVersion& writingVersion = CiftiVersion(), const ENDIAN& endian = NATIVE);//starts on-disk writing void writeFile(const QString& fileName, const CiftiVersion& writingVersion = CiftiVersion(), const ENDIAN& endian = ANY);//leaves current state as-is, rewrites if already writing to that filename and version mismatch + void close();//closes the underlying file to flush it, so that exceptions can be thrown void convertToInMemory(); QString getFileName() const { return m_fileName; } @@ -69,6 +75,10 @@ namespace caret void setRow(const float* dataIn, const std::vector& indexSelect); void setColumn(const float* dataIn, const int64_t& index);//for 2D only, will be slow if on disk! + ///data type and scaling options - should be set before setRow, etc, to avoid rewriting of file + void setWritingDataTypeNoScaling(const int16_t& type = NIFTI_TYPE_FLOAT32); + void setWritingDataTypeAndScaling(const int16_t& type, const double& minval, const double& maxval); + void getRow(float* dataOut, const int64_t& index, const bool& tolerateShortRead) const;//backwards compatibility for old CiftiFile/CiftiInterface void getRow(float* dataOut, const int64_t& index) const; int64_t getNumberOfRows() const; @@ -90,6 +100,7 @@ namespace caret public: virtual void setRow(const float* dataIn, const std::vector& indexSelect) = 0; virtual void setColumn(const float* dataIn, const int64_t& index) = 0; + virtual void close() {} virtual ~WriteImplInterface(); }; private: @@ -100,6 +111,9 @@ namespace caret //CiftiXML m_xml;//uncomment when we drop CiftiInterface CiftiVersion m_onDiskVersion; ENDIAN m_endianPref; + bool m_doWriteScaling; + int16_t m_writingDataType; + double m_minScalingVal, m_maxScalingVal; void verifyWriteImpl(); static void copyImplData(const ReadImplInterface* from, WriteImplInterface* to, const std::vector& dims); diff --git a/src/Cifti/CiftiLabelsMap.cxx b/src/Cifti/CiftiLabelsMap.cxx index 633deb405914970e34efb9135c78434399b9ee08..164304b0a404e96711d338465530bf07fa31159b 100644 --- a/src/Cifti/CiftiLabelsMap.cxx +++ b/src/Cifti/CiftiLabelsMap.cxx @@ -21,7 +21,7 @@ #include "CiftiLabelsMap.h" #include "CaretAssert.h" -#include "CaretException.h" +#include "DataFileException.h" #include "CaretLogger.h" using namespace caret; @@ -124,7 +124,7 @@ void CiftiLabelsMap::readXML1(QXmlStreamReader& xml) { if (xml.name() != "NamedMap") { - throw CaretException("unexpected element in labels map: " + xml.name().toString()); + throw DataFileException("unexpected element in labels map: " + xml.name().toString()); } LabelMap tempMap; tempMap.readXML1(xml); @@ -149,7 +149,7 @@ void CiftiLabelsMap::readXML2(QXmlStreamReader& xml) { if (xml.name() != "NamedMap") { - throw CaretException("unexpected element in labels map: " + xml.name().toString()); + throw DataFileException("unexpected element in labels map: " + xml.name().toString()); } LabelMap tempMap; tempMap.readXML2(xml); @@ -177,7 +177,7 @@ void CiftiLabelsMap::LabelMap::readXML1(QXmlStreamReader& xml) { if (haveMetaData) { - throw CaretException("MetaData specified multiple times in one NamedMap"); + throw DataFileException("MetaData specified multiple times in one NamedMap"); } m_metaData.readCiftiXML1(xml); if (xml.hasError()) return; @@ -185,7 +185,7 @@ void CiftiLabelsMap::LabelMap::readXML1(QXmlStreamReader& xml) } else if (name == "LabelTable") { if (haveTable) { - throw CaretException("LabelTable specified multiple times in one NamedMap"); + throw DataFileException("LabelTable specified multiple times in one NamedMap"); } m_labelTable.readFromQXmlStreamReader(xml); if (xml.hasError()) return; @@ -193,13 +193,13 @@ void CiftiLabelsMap::LabelMap::readXML1(QXmlStreamReader& xml) } else if (name == "MapName") { if (haveName) { - throw CaretException("MapName specified multiple times in one NamedMap"); + throw DataFileException("MapName specified multiple times in one NamedMap"); } m_name = xml.readElementText();//raises error if element encountered if (xml.hasError()) return; haveName = true; } else { - throw CaretException("unexpected element in NamedMap: " + name.toString()); + throw DataFileException("unexpected element in NamedMap: " + name.toString()); } break; } @@ -209,11 +209,11 @@ void CiftiLabelsMap::LabelMap::readXML1(QXmlStreamReader& xml) } if (!haveName) { - throw CaretException("NamedMap missing required child element MapName"); + throw DataFileException("NamedMap missing required child element MapName"); } if (!haveTable) { - throw CaretException("NamedMap in labels mapping missing required child element LabelTable"); + throw DataFileException("NamedMap in labels mapping missing required child element LabelTable"); } } @@ -231,7 +231,7 @@ void CiftiLabelsMap::LabelMap::readXML2(QXmlStreamReader& xml) { if (haveMetaData) { - throw CaretException("MetaData specified multiple times in one NamedMap"); + throw DataFileException("MetaData specified multiple times in one NamedMap"); } m_metaData.readCiftiXML2(xml); if (xml.hasError()) return; @@ -239,7 +239,7 @@ void CiftiLabelsMap::LabelMap::readXML2(QXmlStreamReader& xml) } else if (name == "LabelTable") { if (haveTable) { - throw CaretException("LabelTable specified multiple times in one NamedMap"); + throw DataFileException("LabelTable specified multiple times in one NamedMap"); } m_labelTable.readFromQXmlStreamReader(xml); if (xml.hasError()) return; @@ -247,13 +247,13 @@ void CiftiLabelsMap::LabelMap::readXML2(QXmlStreamReader& xml) } else if (name == "MapName") { if (haveName) { - throw CaretException("MapName specified multiple times in one NamedMap"); + throw DataFileException("MapName specified multiple times in one NamedMap"); } m_name = xml.readElementText();//raises error if element encountered if (xml.hasError()) return; haveName = true; } else { - throw CaretException("unexpected element in NamedMap: " + name.toString()); + throw DataFileException("unexpected element in NamedMap: " + name.toString()); } break; } @@ -263,11 +263,11 @@ void CiftiLabelsMap::LabelMap::readXML2(QXmlStreamReader& xml) } if (!haveName) { - throw CaretException("NamedMap missing required child element MapName"); + throw DataFileException("NamedMap missing required child element MapName"); } if (!haveTable) { - throw CaretException("NamedMap in labels mapping missing required child element LabelTable"); + throw DataFileException("NamedMap in labels mapping missing required child element LabelTable"); } } diff --git a/src/Cifti/CiftiParcelsMap.cxx b/src/Cifti/CiftiParcelsMap.cxx index b8df159f0e6e144efe91e516dfd072f6f95c685d..555d1ae04a2681e9428d063382060e818afe8b5a 100644 --- a/src/Cifti/CiftiParcelsMap.cxx +++ b/src/Cifti/CiftiParcelsMap.cxx @@ -20,7 +20,7 @@ #include "CiftiParcelsMap.h" -#include "CaretException.h" +#include "DataFileException.h" #include "CaretLogger.h" #include @@ -36,7 +36,7 @@ void CiftiParcelsMap::addParcel(const CiftiParcelsMap::Parcel& parcel) { if (parcel.m_name == m_parcels[i].m_name) { - throw CaretException("cannot add parcel with duplicate name '" + parcel.m_name + "'");//NOTE: technically this restriction isn't in the standard, but that was probably an oversight + throw DataFileException("cannot add parcel with duplicate name '" + parcel.m_name + "'");//NOTE: technically this restriction isn't in the standard, but that was probably an oversight } } int64_t voxelListSize = (int64_t)parcel.m_voxelIndices.size(); @@ -48,7 +48,7 @@ void CiftiParcelsMap::addParcel(const CiftiParcelsMap::Parcel& parcel) { if (!m_haveVolumeSpace) { - throw CaretException("you must set the volume space before adding parcels that use voxels"); + throw DataFileException("you must set the volume space before adding parcels that use voxels"); } dims = m_volSpace.getDims(); } @@ -56,17 +56,17 @@ void CiftiParcelsMap::addParcel(const CiftiParcelsMap::Parcel& parcel) { if (iter->m_ijk[0] < 0 || iter->m_ijk[1] < 0 || iter->m_ijk[2] < 0) { - throw CaretException("found negative index triple in voxel list"); + throw DataFileException("found negative index triple in voxel list"); } if (!m_ignoreVolSpace && (iter->m_ijk[0] >= dims[0] || iter->m_ijk[1] >= dims[1] || iter->m_ijk[2] >= dims[2])) { - throw CaretException("found invalid index triple in voxel list"); + throw DataFileException("found invalid index triple in voxel list"); } if (tempLookup.find(iter->m_ijk) != NULL) { - throw CaretException("parcels may not overlap in voxels"); + throw DataFileException("parcels may not overlap in voxels"); } tempLookup.at(iter->m_ijk) = thisParcel; } @@ -76,26 +76,26 @@ void CiftiParcelsMap::addParcel(const CiftiParcelsMap::Parcel& parcel) map::const_iterator info = m_surfInfo.find(iter->first); if (info == m_surfInfo.end()) { - throw CaretException("you must set surfaces before adding parcels that use them"); + throw DataFileException("you must set surfaces before adding parcels that use them"); } const set& nodeSet = iter->second; if (nodeSet.size() == 0) { - throw CaretException("parcels may not include empty node lists");//NOTE: technically not required by Cifti, change if problematic, but probably never allow empty list in internal state + throw DataFileException("parcels may not include empty node lists");//NOTE: technically not required by Cifti, change if problematic, but probably never allow empty list in internal state } for (set::const_iterator iter2 = nodeSet.begin(); iter2 != nodeSet.end(); ++iter2) { if (*iter2 < 0) { - throw CaretException("found negative vertex in parcel"); + throw DataFileException("found negative vertex in parcel"); } if (*iter2 >= info->second.m_numNodes) { - throw CaretException("found invalid vertex in parcel"); + throw DataFileException("found invalid vertex in parcel"); } if (info->second.m_lookup[*iter2] != -1) { - throw CaretException("parcels may not overlap in vertices"); + throw DataFileException("parcels may not overlap in vertices"); } } } @@ -122,7 +122,7 @@ void CiftiParcelsMap::addSurface(const int64_t& numberOfNodes, const StructureEn map::const_iterator test = m_surfInfo.find(structure); if (test != m_surfInfo.end()) { - throw CaretException("parcel surface structures may not be used more than once"); + throw DataFileException("parcel surface structures may not be used more than once"); } SurfaceInfo tempInfo; tempInfo.m_numNodes = numberOfNodes; @@ -152,7 +152,7 @@ void CiftiParcelsMap::setVolumeSpace(const VolumeSpace& space) iter->m_ijk[1] >= dims[1] || iter->m_ijk[2] >= dims[2]) { - throw CaretException("parcels may not contain voxel indices outside the volume space"); + throw DataFileException("parcels may not contain voxel indices outside the volume space"); } } } @@ -223,7 +223,7 @@ const VolumeSpace& CiftiParcelsMap::getVolumeSpace() const CaretAssert(!m_ignoreVolSpace);//this should never be set except during parsing of cifti-1 if (!m_haveVolumeSpace) { - throw CaretException("getVolumeSpace called when no volume space exists"); + throw DataFileException("getVolumeSpace called when no volume space exists"); } return m_volSpace; } @@ -384,32 +384,32 @@ void CiftiParcelsMap::readXML1(QXmlStreamReader& xml) QXmlStreamAttributes attrs = xml.attributes(); if (!attrs.hasAttribute("BrainStructure")) { - throw CaretException("Surface element missing required attribute BrainStructure"); + throw DataFileException("Surface element missing required attribute BrainStructure"); } bool ok = false; StructureEnum::Enum tempStructure = StructureEnum::fromCiftiName(attrs.value("BrainStructure").toString(), &ok); if (!ok) { - throw CaretException("invalid value for BrainStructure: " + attrs.value("BrainStructure").toString()); + throw DataFileException("invalid value for BrainStructure: " + attrs.value("BrainStructure").toString()); } if (!attrs.hasAttribute("SurfaceNumberOfNodes")) { - throw CaretException("Surface element missing required attribute SurfaceNumberOfNodes"); + throw DataFileException("Surface element missing required attribute SurfaceNumberOfNodes"); } int64_t numNodes = attrs.value("SurfaceNumberOfNodes").toString().toLongLong(&ok); if (!ok || numNodes < 1) { - throw CaretException("invalid value for SurfaceNumberOfNodes: " + attrs.value("SurfaceNumberOfNodes").toString()); + throw DataFileException("invalid value for SurfaceNumberOfNodes: " + attrs.value("SurfaceNumberOfNodes").toString()); } addSurface(numNodes, tempStructure);//let the standard modification functions do error checking if (xml.readNextStartElement()) { - throw CaretException("unexpected element inside Surface: " + xml.name().toString()); + throw DataFileException("unexpected element inside Surface: " + xml.name().toString()); } } else if (name == "Parcel") { myParcels.push_back(readParcel1(xml)); } else { - throw CaretException("unexpected element in parcels map: " + name.toString()); + throw DataFileException("unexpected element in parcels map: " + name.toString()); } } int64_t numParcels = (int64_t)myParcels.size(); @@ -433,27 +433,27 @@ void CiftiParcelsMap::readXML2(QXmlStreamReader& xml) QXmlStreamAttributes attrs = xml.attributes(); if (!attrs.hasAttribute("BrainStructure")) { - throw CaretException("Surface element missing required attribute BrainStructure"); + throw DataFileException("Surface element missing required attribute BrainStructure"); } bool ok = false; StructureEnum::Enum tempStructure = StructureEnum::fromCiftiName(attrs.value("BrainStructure").toString(), &ok); if (!ok) { - throw CaretException("invalid value for BrainStructure: " + attrs.value("BrainStructure").toString()); + throw DataFileException("invalid value for BrainStructure: " + attrs.value("BrainStructure").toString()); } if (!attrs.hasAttribute("SurfaceNumberOfVertices")) { - throw CaretException("Surface element missing required attribute SurfaceNumberOfVertices"); + throw DataFileException("Surface element missing required attribute SurfaceNumberOfVertices"); } int64_t numNodes = attrs.value("SurfaceNumberOfVertices").toString().toLongLong(&ok); if (!ok || numNodes < 1) { - throw CaretException("invalid value for SurfaceNumberOfVertices: " + attrs.value("SurfaceNumberOfVertices").toString()); + throw DataFileException("invalid value for SurfaceNumberOfVertices: " + attrs.value("SurfaceNumberOfVertices").toString()); } addSurface(numNodes, tempStructure);//let the standard modification functions do error checking if (xml.readNextStartElement()) { - throw CaretException("unexpected element inside Surface: " + xml.name().toString()); + throw DataFileException("unexpected element inside Surface: " + xml.name().toString()); } } else if (name == "Parcel") { myParcels.push_back(readParcel2(xml)); @@ -461,14 +461,14 @@ void CiftiParcelsMap::readXML2(QXmlStreamReader& xml) } else if (name == "Volume") { if (m_haveVolumeSpace) { - throw CaretException("Volume specified more than once in Parcels mapping type"); + throw DataFileException("Volume specified more than once in Parcels mapping type"); } else { m_volSpace.readCiftiXML2(xml); if (xml.hasError()) return; m_haveVolumeSpace = true; } } else { - throw CaretException("unexpected element in parcels map: " + name.toString()); + throw DataFileException("unexpected element in parcels map: " + name.toString()); } } int64_t numParcels = (int64_t)myParcels.size(); @@ -486,7 +486,7 @@ CiftiParcelsMap::Parcel CiftiParcelsMap::readParcel1(QXmlStreamReader& xml) QXmlStreamAttributes attrs = xml.attributes(); if (!attrs.hasAttribute("Name")) { - throw CaretException("Parcel element missing required attribute Name"); + throw DataFileException("Parcel element missing required attribute Name"); } ret.m_name = attrs.value("Name").toString(); while (xml.readNextStartElement()) @@ -497,17 +497,17 @@ CiftiParcelsMap::Parcel CiftiParcelsMap::readParcel1(QXmlStreamReader& xml) QXmlStreamAttributes attrs1 = xml.attributes(); if (!attrs1.hasAttribute("BrainStructure")) { - throw CaretException("Nodes element missing required attribute BrainStructure"); + throw DataFileException("Nodes element missing required attribute BrainStructure"); } bool ok = false; StructureEnum::Enum myStructure = StructureEnum::fromCiftiName(attrs1.value("BrainStructure").toString(), &ok); if (!ok) { - throw CaretException("unrecognized value for BrainStructure: " + attrs1.value("BrainStructure").toString()); + throw DataFileException("unrecognized value for BrainStructure: " + attrs1.value("BrainStructure").toString()); } if (ret.m_surfaceNodes.find(myStructure) != ret.m_surfaceNodes.end()) { - throw CaretException("Nodes elements may not reuse a BrainStructure within a Parcel"); + throw DataFileException("Nodes elements may not reuse a BrainStructure within a Parcel"); } set& mySet = ret.m_surfaceNodes[myStructure]; vector array = readIndexArray(xml); @@ -517,34 +517,34 @@ CiftiParcelsMap::Parcel CiftiParcelsMap::readParcel1(QXmlStreamReader& xml) { if (mySet.find(array[i]) != mySet.end()) { - throw CaretException("Nodes elements may not reuse indices"); + throw DataFileException("Nodes elements may not reuse indices"); } mySet.insert(array[i]); } } else if (name == "VoxelIndicesIJK") { if (haveVoxels) { - throw CaretException("VoxelIndicesIJK may only appear once in a Parcel"); + throw DataFileException("VoxelIndicesIJK may only appear once in a Parcel"); } vector array = readIndexArray(xml); if (xml.hasError()) return ret; int64_t arraySize = (int64_t)array.size(); if (arraySize % 3 != 0) { - throw CaretException("number of indices in VoxelIndicesIJK must be a multiple of 3"); + throw DataFileException("number of indices in VoxelIndicesIJK must be a multiple of 3"); } for (int64_t index3 = 0; index3 < arraySize; index3 += 3) { VoxelIJK temp(array.data() + index3); if (ret.m_voxelIndices.find(temp) != ret.m_voxelIndices.end()) { - throw CaretException("VoxelIndicesIJK elements may not reuse voxels"); + throw DataFileException("VoxelIndicesIJK elements may not reuse voxels"); } ret.m_voxelIndices.insert(temp); } haveVoxels = true; } else { - throw CaretException("unexpected element in Parcel: " + name.toString()); + throw DataFileException("unexpected element in Parcel: " + name.toString()); } } CaretAssert(xml.isEndElement() && xml.name() == "Parcel"); @@ -558,7 +558,7 @@ CiftiParcelsMap::Parcel CiftiParcelsMap::readParcel2(QXmlStreamReader& xml) QXmlStreamAttributes attrs = xml.attributes(); if (!attrs.hasAttribute("Name")) { - throw CaretException("Parcel element missing required attribute Name"); + throw DataFileException("Parcel element missing required attribute Name"); } ret.m_name = attrs.value("Name").toString(); while (xml.readNextStartElement()) @@ -569,17 +569,17 @@ CiftiParcelsMap::Parcel CiftiParcelsMap::readParcel2(QXmlStreamReader& xml) QXmlStreamAttributes attrs1 = xml.attributes(); if (!attrs1.hasAttribute("BrainStructure")) { - throw CaretException("Vertices element missing required attribute BrainStructure"); + throw DataFileException("Vertices element missing required attribute BrainStructure"); } bool ok = false; StructureEnum::Enum myStructure = StructureEnum::fromCiftiName(attrs1.value("BrainStructure").toString(), &ok); if (!ok) { - throw CaretException("unrecognized value for BrainStructure: " + attrs1.value("BrainStructure").toString()); + throw DataFileException("unrecognized value for BrainStructure: " + attrs1.value("BrainStructure").toString()); } if (ret.m_surfaceNodes.find(myStructure) != ret.m_surfaceNodes.end()) { - throw CaretException("Vertices elements may not reuse a BrainStructure within a Parcel"); + throw DataFileException("Vertices elements may not reuse a BrainStructure within a Parcel"); } set& mySet = ret.m_surfaceNodes[myStructure]; vector array = readIndexArray(xml); @@ -589,34 +589,34 @@ CiftiParcelsMap::Parcel CiftiParcelsMap::readParcel2(QXmlStreamReader& xml) { if (mySet.find(array[i]) != mySet.end()) { - throw CaretException("Vertices elements may not reuse indices"); + throw DataFileException("Vertices elements may not reuse indices"); } mySet.insert(array[i]); } } else if (name == "VoxelIndicesIJK") { if (haveVoxels) { - throw CaretException("VoxelIndicesIJK may only appear once in a Parcel"); + throw DataFileException("VoxelIndicesIJK may only appear once in a Parcel"); } vector array = readIndexArray(xml); if (xml.hasError()) return ret; int64_t arraySize = (int64_t)array.size(); if (arraySize % 3 != 0) { - throw CaretException("number of indices in VoxelIndicesIJK must be a multiple of 3"); + throw DataFileException("number of indices in VoxelIndicesIJK must be a multiple of 3"); } for (int64_t index3 = 0; index3 < arraySize; index3 += 3) { VoxelIJK temp(array.data() + index3); if (ret.m_voxelIndices.find(temp) != ret.m_voxelIndices.end()) { - throw CaretException("VoxelIndicesIJK elements may not reuse voxels"); + throw DataFileException("VoxelIndicesIJK elements may not reuse voxels"); } ret.m_voxelIndices.insert(temp); } haveVoxels = true; } else { - throw CaretException("unexpected element in Parcel: " + name.toString()); + throw DataFileException("unexpected element in Parcel: " + name.toString()); } } CaretAssert(xml.isEndElement() && xml.name() == "Parcel"); @@ -637,7 +637,7 @@ vector CiftiParcelsMap::readIndexArray(QXmlStreamReader& xml) ret.push_back(separated[i].toLongLong(&ok)); if (!ok) { - throw CaretException("found noninteger in index array: " + separated[i]); + throw DataFileException("found noninteger in index array: " + separated[i]); } } return ret; diff --git a/src/Cifti/CiftiScalarsMap.cxx b/src/Cifti/CiftiScalarsMap.cxx index f122710144511459c5663933b774e3fd2920c41c..9166756229782904901823557dd99ce0eadc1d81 100644 --- a/src/Cifti/CiftiScalarsMap.cxx +++ b/src/Cifti/CiftiScalarsMap.cxx @@ -21,7 +21,7 @@ #include "CiftiScalarsMap.h" #include "CaretAssert.h" -#include "CaretException.h" +#include "DataFileException.h" #include "CaretLogger.h" //HACK: to compare metadata in a const function, we make a copy and remove the palette data - but metadata's copy intentionally breaks == because of the UUID, so we need to reset it #include "GiftiMetaDataXmlElements.h" @@ -143,6 +143,27 @@ bool CiftiScalarsMap::ScalarMap::operator==(const CiftiScalarsMap::ScalarMap& rh return (mytemp == rhstemp); } +CiftiScalarsMap::ScalarMap::ScalarMap(const CiftiScalarsMap::ScalarMap& rhs) +{ + m_name = rhs.m_name; + m_metaData = rhs.m_metaData; + if (rhs.m_palette != NULL) m_palette.grabNew(new PaletteColorMapping(*(rhs.m_palette)));//we need to copy the palette, because it may no longer match the metadata +} + +CiftiScalarsMap::ScalarMap& CiftiScalarsMap::ScalarMap::operator=(const CiftiScalarsMap::ScalarMap& rhs) +{ + if (&rhs == this) return *this; + m_name = rhs.m_name; + m_metaData = rhs.m_metaData; + if (rhs.m_palette == NULL) + { + m_palette.grabNew(NULL); + } else { + m_palette.grabNew(new PaletteColorMapping(*(rhs.m_palette)));//we need to copy the palette object, because it may no longer match the metadata + } + return *this; +} + void CiftiScalarsMap::readXML1(QXmlStreamReader& xml) { CaretLogFiner("parsing nonstandard scalars mapping type in cifti-1"); @@ -155,7 +176,7 @@ void CiftiScalarsMap::readXML1(QXmlStreamReader& xml) { if (xml.name() != "NamedMap") { - throw CaretException("unexpected element in scalars map: " + xml.name().toString()); + throw DataFileException("unexpected element in scalars map: " + xml.name().toString()); } m_maps.push_back(ScalarMap());//HACK: because operator= is deliberately broken by GiftiMetadata for UUID m_maps.back().readXML1(xml); @@ -180,7 +201,7 @@ void CiftiScalarsMap::readXML2(QXmlStreamReader& xml) { if (xml.name() != "NamedMap") { - throw CaretException("unexpected element in scalars map: " + xml.name().toString()); + throw DataFileException("unexpected element in scalars map: " + xml.name().toString()); } m_maps.push_back(ScalarMap());//HACK: because operator= is deliberately broken by GiftiMetadata for UUID m_maps.back().readXML2(xml); @@ -208,7 +229,7 @@ void CiftiScalarsMap::ScalarMap::readXML1(QXmlStreamReader& xml) { if (haveMetaData) { - throw CaretException("MetaData specified multiple times in one NamedMap"); + throw DataFileException("MetaData specified multiple times in one NamedMap"); } m_metaData.readCiftiXML1(xml); if (xml.hasError()) return; @@ -216,7 +237,7 @@ void CiftiScalarsMap::ScalarMap::readXML1(QXmlStreamReader& xml) } else if (name == "MapName") { if (haveName) { - throw CaretException("MapName specified multiple times in one NamedMap"); + throw DataFileException("MapName specified multiple times in one NamedMap"); } m_name = xml.readElementText();//raises error if element encountered if (xml.hasError()) return; @@ -226,7 +247,7 @@ void CiftiScalarsMap::ScalarMap::readXML1(QXmlStreamReader& xml) xml.readElementText(QXmlStreamReader::SkipChildElements);//accept some malformed Cifti-1 files if (xml.hasError()) return; } else { - throw CaretException("unexpected element in NamedMap: " + name.toString()); + throw DataFileException("unexpected element in NamedMap: " + name.toString()); } break; } @@ -236,7 +257,7 @@ void CiftiScalarsMap::ScalarMap::readXML1(QXmlStreamReader& xml) } if (!haveName) { - throw CaretException("NamedMap missing required child element MapName"); + throw DataFileException("NamedMap missing required child element MapName"); } CaretAssert(xml.isEndElement() && xml.name() == "NamedMap"); } @@ -255,7 +276,7 @@ void CiftiScalarsMap::ScalarMap::readXML2(QXmlStreamReader& xml) { if (haveMetaData) { - throw CaretException("MetaData specified multiple times in one NamedMap"); + throw DataFileException("MetaData specified multiple times in one NamedMap"); } m_metaData.readCiftiXML2(xml); if (xml.hasError()) return; @@ -263,13 +284,13 @@ void CiftiScalarsMap::ScalarMap::readXML2(QXmlStreamReader& xml) } else if (name == "MapName") { if (haveName) { - throw CaretException("MapName specified multiple times in one NamedMap"); + throw DataFileException("MapName specified multiple times in one NamedMap"); } m_name = xml.readElementText();//raises error if element encountered if (xml.hasError()) return; haveName = true; } else { - throw CaretException("unexpected element in NamedMap: " + name.toString()); + throw DataFileException("unexpected element in NamedMap: " + name.toString()); } break; } @@ -279,7 +300,7 @@ void CiftiScalarsMap::ScalarMap::readXML2(QXmlStreamReader& xml) } if (!haveName) { - throw CaretException("NamedMap missing required child element MapName"); + throw DataFileException("NamedMap missing required child element MapName"); } CaretAssert(xml.isEndElement() && xml.name() == "NamedMap"); } diff --git a/src/Cifti/CiftiScalarsMap.h b/src/Cifti/CiftiScalarsMap.h index c090683625e9d21e66aed378b7c6c227b00f32b9..61e4f9d300b1786d52f8fbd507fe323987ae17ef 100644 --- a/src/Cifti/CiftiScalarsMap.h +++ b/src/Cifti/CiftiScalarsMap.h @@ -69,6 +69,9 @@ namespace caret mutable GiftiMetaData m_metaData;//ditto mutable CaretPointer m_palette;//ditto - note, this actually gets written into the metadata PaletteColorMapping* getPalette() const; + ScalarMap() {}; + ScalarMap(const ScalarMap& rhs); + ScalarMap& operator=(const ScalarMap& rhs); bool operator==(const ScalarMap& rhs) const; void readXML1(QXmlStreamReader& xml); void readXML2(QXmlStreamReader& xml); diff --git a/src/Cifti/CiftiSeriesMap.cxx b/src/Cifti/CiftiSeriesMap.cxx index 981686ca41b1ab86b5d1fe24af50a123df5f2c79..8683eb280dbfa75a549a5dfc1afcd7d44d3acfec 100644 --- a/src/Cifti/CiftiSeriesMap.cxx +++ b/src/Cifti/CiftiSeriesMap.cxx @@ -21,7 +21,7 @@ #include "CiftiSeriesMap.h" #include "CaretAssert.h" -#include "CaretException.h" +#include "DataFileException.h" #include "CaretLogger.h" #include @@ -86,7 +86,7 @@ void CiftiSeriesMap::readXML1(QXmlStreamReader& xml) bool ok = false; if (!attrs.hasAttribute("TimeStepUnits")) { - throw CaretException("timepoints mapping is missing required attribute TimeStepUnits"); + throw DataFileException("timepoints mapping is missing required attribute TimeStepUnits"); } QStringRef unitString = attrs.value("TimeStepUnits"); if (unitString == "NIFTI_UNITS_SEC") @@ -97,28 +97,28 @@ void CiftiSeriesMap::readXML1(QXmlStreamReader& xml) } else if (unitString == "NIFTI_UNITS_USEC") { mult = 0.000001f; } else { - throw CaretException("unrecognized value for TimeStepUnits: " + unitString.toString()); + throw DataFileException("unrecognized value for TimeStepUnits: " + unitString.toString()); } if (attrs.hasAttribute("TimeStart"))//optional and nonstandard { newStart = mult * attrs.value("TimeStart").toString().toFloat(&ok); if (!ok) { - throw CaretException("unrecognized value for TimeStart: " + attrs.value("TimeStart").toString()); + throw DataFileException("unrecognized value for TimeStart: " + attrs.value("TimeStart").toString()); } } if (!attrs.hasAttribute("TimeStep")) { - throw CaretException("timepoints mapping is missing required attribute TimeStep"); + throw DataFileException("timepoints mapping is missing required attribute TimeStep"); } newStep = mult * attrs.value("TimeStep").toString().toFloat(&ok); if (!ok) { - throw CaretException("unrecognized value for TimeStep: " + attrs.value("TimeStep").toString()); + throw DataFileException("unrecognized value for TimeStep: " + attrs.value("TimeStep").toString()); } if (xml.readNextStartElement()) { - throw CaretException("unexpected element in timepoints map: " + xml.name().toString()); + throw DataFileException("unexpected element in timepoints map: " + xml.name().toString()); } m_length = -1;//cifti-1 doesn't know length in xml, must be set by checking the matrix m_start = newStart; @@ -136,7 +136,7 @@ void CiftiSeriesMap::readXML2(QXmlStreamReader& xml) bool ok = false; if (!attrs.hasAttribute("SeriesUnit")) { - throw CaretException("series mapping is missing required attribute SeriesUnit"); + throw DataFileException("series mapping is missing required attribute SeriesUnit"); } QStringRef unitString = attrs.value("SeriesUnit"); if (unitString == "HERTZ") @@ -149,52 +149,52 @@ void CiftiSeriesMap::readXML2(QXmlStreamReader& xml) } else if (unitString == "SECOND") { newUnit = SECOND; } else { - throw CaretException("unrecognized value for SeriesUnit: " + unitString.toString()); + throw DataFileException("unrecognized value for SeriesUnit: " + unitString.toString()); } if (!attrs.hasAttribute("SeriesExponent")) { - throw CaretException("series mapping is missing required attribute SeriesExponent"); + throw DataFileException("series mapping is missing required attribute SeriesExponent"); } int exponent = attrs.value("SeriesExponent").toString().toInt(&ok); if (!ok) { - throw CaretException("unrecognized value for SeriesExponent: " + attrs.value("SeriesExponent").toString()); + throw DataFileException("unrecognized value for SeriesExponent: " + attrs.value("SeriesExponent").toString()); } mult = pow(10.0f, exponent); if (!attrs.hasAttribute("SeriesStart")) { - throw CaretException("series mapping is missing required attribute SeriesStart"); + throw DataFileException("series mapping is missing required attribute SeriesStart"); } newStart = mult * attrs.value("SeriesStart").toString().toFloat(&ok); if (!ok) { - throw CaretException("unrecognized value for SeriesStart: " + attrs.value("SeriesStart").toString()); + throw DataFileException("unrecognized value for SeriesStart: " + attrs.value("SeriesStart").toString()); } if (!attrs.hasAttribute("SeriesStep")) { - throw CaretException("series mapping is missing required attribute SeriesStep"); + throw DataFileException("series mapping is missing required attribute SeriesStep"); } newStep = mult * attrs.value("SeriesStep").toString().toFloat(&ok); if (!ok) { - throw CaretException("unrecognized value for SeriesStep: " + attrs.value("SeriesStep").toString()); + throw DataFileException("unrecognized value for SeriesStep: " + attrs.value("SeriesStep").toString()); } if (!attrs.hasAttribute("NumberOfSeriesPoints")) { - throw CaretException("series mapping is missing required attribute NumberOfSeriesPoints"); + throw DataFileException("series mapping is missing required attribute NumberOfSeriesPoints"); } newLength = attrs.value("NumberOfSeriesPoints").toString().toLongLong(&ok); if (!ok) { - throw CaretException("unrecognized value for NumberOfSeriesPoints: " + attrs.value("NumberOfSeriesPoints").toString()); + throw DataFileException("unrecognized value for NumberOfSeriesPoints: " + attrs.value("NumberOfSeriesPoints").toString()); } if (newLength < 1) { - throw CaretException("NumberOfSeriesPoints must be positive"); + throw DataFileException("NumberOfSeriesPoints must be positive"); } if (xml.readNextStartElement()) { - throw CaretException("unexpected element in series map: " + xml.name().toString()); + throw DataFileException("unexpected element in series map: " + xml.name().toString()); } m_length = newLength; m_start = newStart; diff --git a/src/Cifti/CiftiVersion.cxx b/src/Cifti/CiftiVersion.cxx index 413cedaac140f2407b46fbfc1f54e8f381d77fde..451181381b34537846459901b965402e872e0c48 100644 --- a/src/Cifti/CiftiVersion.cxx +++ b/src/Cifti/CiftiVersion.cxx @@ -20,7 +20,7 @@ #include "CiftiVersion.h" -#include "CaretException.h" +#include "DataFileException.h" using namespace caret; @@ -44,13 +44,13 @@ CiftiVersion::CiftiVersion(const QString& versionString) { m_minor = 0; m_major = versionString.toShort(&ok); - if (!ok) throw CaretException("improperly formatted CIFTI version string: '" + versionString + "'"); + if (!ok) throw DataFileException("improperly formatted CIFTI version string: '" + versionString + "'"); } else { - if (result == 0) throw CaretException("improperly formatted CIFTI version string: '" + versionString + "'"); + if (result == 0) throw DataFileException("improperly formatted CIFTI version string: '" + versionString + "'"); m_major = versionString.mid(0, result).toShort(&ok); - if (!ok) throw CaretException("improperly formatted CIFTI version string: '" + versionString + "'"); + if (!ok) throw DataFileException("improperly formatted CIFTI version string: '" + versionString + "'"); m_minor = versionString.mid(result + 1).toShort(&ok); - if (!ok) throw CaretException("improperly formatted CIFTI version string: '" + versionString + "'"); + if (!ok) throw DataFileException("improperly formatted CIFTI version string: '" + versionString + "'"); } } diff --git a/src/Cifti/CiftiXML.cxx b/src/Cifti/CiftiXML.cxx index 400b13f8f66194194c7770c88609ea450a4f6609..53e2438ee0a3d0d8955a036bcb032c73bf368bd6 100644 --- a/src/Cifti/CiftiXML.cxx +++ b/src/Cifti/CiftiXML.cxx @@ -21,7 +21,7 @@ #include "CiftiXML.h" #include "CaretAssert.h" -#include "CaretException.h" +#include "DataFileException.h" #include "CaretLogger.h" #include "GiftiMetaData.h" #include "PaletteColorMapping.h" @@ -39,12 +39,12 @@ int CiftiXML::directionFromString(const QString& input) int converted = input.toInt(&ok); if (ok) { - if (converted < 1) throw CaretException("invalid integer direction, use 1 or greater"); + if (converted < 1) throw DataFileException("invalid integer direction, use 1 or greater"); return converted - 1;//use 1-indexed convention for input } if (input == "ROW") return ALONG_ROW; if (input == "COLUMN") return ALONG_COLUMN;//should we also allow STACK? integers seem cleaner - throw CaretException("unrecognized direction string, please use an integer, 'ROW', or 'COLUMN'"); + throw DataFileException("unrecognized direction string, please use an integer, 'ROW', or 'COLUMN'"); } QString CiftiXML::directionFromStringExplanation() @@ -251,7 +251,7 @@ void CiftiXML::setMap(const int& direction, const CiftiMappingType& mapIn) { if (i != direction && m_indexMaps[i] != NULL && m_indexMaps[i]->getType() == CiftiMappingType::LABELS) { - throw CaretException("Cifti XML cannot contain a label mapping on more than one dimension"); + throw DataFileException("Cifti XML cannot contain a label mapping on more than one dimension"); } } } @@ -323,7 +323,7 @@ int32_t CiftiXML::getIntentInfo(const CiftiVersion& writingVersion, char intentN break; } } else { - throw CaretException("unknown cifti version: " + writingVersion.toString()); + throw DataFileException("unknown cifti version: " + writingVersion.toString()); } int i; for (i = 0; i < 16 && name[i] != '\0'; ++i) intentNameOut[i] = name[i]; @@ -346,14 +346,14 @@ void CiftiXML::readXML(QXmlStreamReader& xml) { if (haveCifti) { - throw CaretException("CIFTI element may only be specified once"); + throw DataFileException("CIFTI element may only be specified once"); } QXmlStreamAttributes attributes = xml.attributes(); if(attributes.hasAttribute("Version")) { m_parsedVersion = CiftiVersion(attributes.value("Version").toString()); } else { - throw CaretException("Cifti XML missing Version attribute."); + throw DataFileException("Cifti XML missing Version attribute."); } if (m_parsedVersion == CiftiVersion(1, 0))//switch/case on major/minor would be much harder to read { @@ -367,24 +367,24 @@ void CiftiXML::readXML(QXmlStreamReader& xml) parseCIFTI2(xml); if (xml.hasError()) break; } else { - throw CaretException("unknown Cifti Version: '" + m_parsedVersion.toString()); + throw DataFileException("unknown Cifti Version: '" + m_parsedVersion.toString()); } haveCifti = true; } else { - throw CaretException("unexpected root element in Cifti XML: " + name.toString()); + throw DataFileException("unexpected root element in Cifti XML: " + name.toString()); } } } if (!xml.hasError() && !haveCifti) { - throw CaretException("CIFTI element not found"); + throw DataFileException("CIFTI element not found"); } } catch (CaretException& e) { - throw CaretException("Cifti XML error: " + e.whatString());//so we can throw on error instead of doing a bunch of dancing with xml.raiseError and xml.hasError + throw DataFileException("Cifti XML error: " + e.whatString());//so we can throw on error instead of doing a bunch of dancing with xml.raiseError and xml.hasError } if(xml.hasError()) { - throw CaretException("Cifti XML error: " + xml.errorString()); + throw DataFileException("Cifti XML error: " + xml.errorString()); } } @@ -395,10 +395,10 @@ void CiftiXML::parseCIFTI1(QXmlStreamReader& xml) { if (attributes.value("NumberOfMatrices") != "1") { - throw CaretException("attribute NumberOfMatrices in CIFTI is required to be 1 for CIFTI-1"); + throw DataFileException("attribute NumberOfMatrices in CIFTI is required to be 1 for CIFTI-1"); } } else { - throw CaretException("missing attribute NumberOfMatrices in CIFTI"); + throw DataFileException("missing attribute NumberOfMatrices in CIFTI"); } bool haveMatrix = false; while (!xml.atEnd()) @@ -411,13 +411,13 @@ void CiftiXML::parseCIFTI1(QXmlStreamReader& xml) { if (haveMatrix) { - throw CaretException("Matrix element may only be specified once"); + throw DataFileException("Matrix element may only be specified once"); } parseMatrix1(xml); if (xml.hasError()) return; haveMatrix = true; } else { - throw CaretException("unexpected element in CIFTI: " + name.toString()); + throw DataFileException("unexpected element in CIFTI: " + name.toString()); } } else if (xml.isEndElement()) { break; @@ -425,7 +425,7 @@ void CiftiXML::parseCIFTI1(QXmlStreamReader& xml) } if (!haveMatrix) { - throw CaretException("Matrix element not found in CIFTI"); + throw DataFileException("Matrix element not found in CIFTI"); } if (xml.hasError()) return; CaretAssert(xml.isEndElement() && xml.name() == "CIFTI"); @@ -445,13 +445,13 @@ void CiftiXML::parseCIFTI2(QXmlStreamReader& xml)//yes, these will often have la { if (haveMatrix) { - throw CaretException("Matrix element may only be specified once"); + throw DataFileException("Matrix element may only be specified once"); } parseMatrix2(xml); if (xml.hasError()) return; haveMatrix = true; } else { - throw CaretException("unexpected element in CIFTI: " + name.toString()); + throw DataFileException("unexpected element in CIFTI: " + name.toString()); } } else if (xml.isEndElement()) { break; @@ -459,7 +459,7 @@ void CiftiXML::parseCIFTI2(QXmlStreamReader& xml)//yes, these will often have la } if (!haveMatrix) { - throw CaretException("Matrix element not found in CIFTI"); + throw DataFileException("Matrix element not found in CIFTI"); } CaretAssert(xml.isEndElement() && xml.name() == "CIFTI"); } @@ -479,7 +479,7 @@ void CiftiXML::parseMatrix1(QXmlStreamReader& xml) { if (haveMetadata) { - throw CaretException("MetaData may only be specified once in Matrix"); + throw DataFileException("MetaData may only be specified once in Matrix"); } m_fileMetaData.readCiftiXML1(xml); if (xml.hasError()) return; @@ -490,7 +490,7 @@ void CiftiXML::parseMatrix1(QXmlStreamReader& xml) } else if (name == "Volume") { if (haveVolSpace) { - throw CaretException("Volume may only be specified once in Matrix"); + throw DataFileException("Volume may only be specified once in Matrix"); } fileVolSpace.readCiftiXML1(xml); if (xml.hasError()) return; @@ -499,7 +499,7 @@ void CiftiXML::parseMatrix1(QXmlStreamReader& xml) CaretLogFiner("skipping unused LabelTable element in Matrix in CIFTI-1"); xml.readElementText(QXmlStreamReader::SkipChildElements); } else { - throw CaretException("unexpected element in Matrix: " + name.toString()); + throw DataFileException("unexpected element in Matrix: " + name.toString()); } } else if (xml.isEndElement()) { break; @@ -511,7 +511,7 @@ void CiftiXML::parseMatrix1(QXmlStreamReader& xml) { int displaynum = i; if (displaynum < 2) displaynum = 1 - displaynum;//re-invert so that it shows the same number as the XML is missing - throw CaretException("missing mapping for dimension '" + QString::number(displaynum) + "'"); + throw DataFileException("missing mapping for dimension '" + QString::number(displaynum) + "'"); } switch (m_indexMaps[i]->getType()) { @@ -524,7 +524,7 @@ void CiftiXML::parseMatrix1(QXmlStreamReader& xml) { myMap.setVolumeSpace(fileVolSpace);//also does the needed checking of voxel indices } else { - throw CaretException("BrainModels map uses voxels, but no Volume element exists"); + throw DataFileException("BrainModels map uses voxels, but no Volume element exists"); } } break; @@ -538,7 +538,7 @@ void CiftiXML::parseMatrix1(QXmlStreamReader& xml) { myMap.setVolumeSpace(fileVolSpace);//ditto } else { - throw CaretException("Parcels map uses voxels, but no Volume element exists"); + throw DataFileException("Parcels map uses voxels, but no Volume element exists"); } } break; @@ -564,7 +564,7 @@ void CiftiXML::parseMatrix2(QXmlStreamReader& xml) { if (haveMetadata) { - throw CaretException("MetaData may only be specified once in Matrix"); + throw DataFileException("MetaData may only be specified once in Matrix"); } m_fileMetaData.readCiftiXML2(xml); if (xml.hasError()) return; @@ -573,7 +573,7 @@ void CiftiXML::parseMatrix2(QXmlStreamReader& xml) parseMatrixIndicesMap2(xml); if (xml.hasError()) return; } else { - throw CaretException("unexpected element in Matrix: " + name.toString()); + throw DataFileException("unexpected element in Matrix: " + name.toString()); } } else if (xml.isEndElement()) { break; @@ -583,7 +583,7 @@ void CiftiXML::parseMatrix2(QXmlStreamReader& xml) { if (m_indexMaps[i] == NULL) { - throw CaretException("missing mapping for dimension '" + QString::number(i) + "'"); + throw DataFileException("missing mapping for dimension '" + QString::number(i) + "'"); } } CaretAssert(xml.isEndElement() && xml.name() == "Matrix"); @@ -594,11 +594,11 @@ void CiftiXML::parseMatrixIndicesMap1(QXmlStreamReader& xml) QXmlStreamAttributes attributes = xml.attributes(); if (!attributes.hasAttribute("AppliesToMatrixDimension")) { - throw CaretException("missing attribute AppliesToMatrixDimension in MatrixIndicesMap"); + throw DataFileException("missing attribute AppliesToMatrixDimension in MatrixIndicesMap"); } if (!attributes.hasAttribute("IndicesMapToDataType")) { - throw CaretException("missing attribute IndicesMapToDataType in MatrixIndicesMap"); + throw DataFileException("missing attribute IndicesMapToDataType in MatrixIndicesMap"); } QStringList values = attributes.value("AppliesToMatrixDimension").toString().split(','); bool ok = false; @@ -608,12 +608,12 @@ void CiftiXML::parseMatrixIndicesMap1(QXmlStreamReader& xml) int parsed = values[i].toInt(&ok); if (!ok || parsed < 0) { - throw CaretException("bad value in AppliesToMatrixDimension list: " + values[i]); + throw DataFileException("bad value in AppliesToMatrixDimension list: " + values[i]); } if (parsed < 2) parsed = 1 - parsed;//in other words, 0 becomes 1 and 1 becomes 0, since cifti-1 had them reversed if (used.find(parsed) != used.end()) { - throw CaretException("AppliesToMatrixDimension contains repeated value: " + values[i]); + throw DataFileException("AppliesToMatrixDimension contains repeated value: " + values[i]); } used.insert(parsed); } @@ -631,7 +631,7 @@ void CiftiXML::parseMatrixIndicesMap1(QXmlStreamReader& xml) } else if (type == "CIFTI_INDEX_TYPE_SCALARS") { toRead = CaretPointer(new CiftiScalarsMap()); } else { - throw CaretException("invalid value for IndicesMapToDataType in CIFTI-1: " + type.toString()); + throw DataFileException("invalid value for IndicesMapToDataType in CIFTI-1: " + type.toString()); } toRead->readXML1(xml);//this will warn (with 'finer' log level?) if it is nonstandard if (xml.hasError()) return; @@ -655,11 +655,11 @@ void CiftiXML::parseMatrixIndicesMap2(QXmlStreamReader& xml) QXmlStreamAttributes attributes = xml.attributes(); if (!attributes.hasAttribute("AppliesToMatrixDimension")) { - throw CaretException("missing attribute AppliesToMatrixDimension in MatrixIndicesMap"); + throw DataFileException("missing attribute AppliesToMatrixDimension in MatrixIndicesMap"); } if (!attributes.hasAttribute("IndicesMapToDataType")) { - throw CaretException("missing attribute IndicesMapToDataType in MatrixIndicesMap"); + throw DataFileException("missing attribute IndicesMapToDataType in MatrixIndicesMap"); } QStringList values = attributes.value("AppliesToMatrixDimension").toString().split(','); bool ok = false; @@ -669,11 +669,11 @@ void CiftiXML::parseMatrixIndicesMap2(QXmlStreamReader& xml) int parsed = values[i].toInt(&ok); if (!ok || parsed < 0) { - throw CaretException("bad value in AppliesToMatrixDimension list: " + values[i]); + throw DataFileException("bad value in AppliesToMatrixDimension list: " + values[i]); } if (used.find(parsed) != used.end()) { - throw CaretException("AppliesToMatrixDimension contains repeated value: " + values[i]); + throw DataFileException("AppliesToMatrixDimension contains repeated value: " + values[i]); } used.insert(parsed); } @@ -691,7 +691,7 @@ void CiftiXML::parseMatrixIndicesMap2(QXmlStreamReader& xml) } else if (type == "CIFTI_INDEX_TYPE_SERIES") { toRead = CaretPointer(new CiftiSeriesMap()); } else { - throw CaretException("invalid value for IndicesMapToDataType in CIFTI-1: " + type.toString()); + throw DataFileException("invalid value for IndicesMapToDataType in CIFTI-1: " + type.toString()); } toRead->readXML2(xml); if (xml.hasError()) return; @@ -746,7 +746,7 @@ void CiftiXML::writeXML(QXmlStreamWriter& xml, const CiftiVersion& writingVersio } else if (writingVersion == CiftiVersion(2, 0)) { writeMatrix2(xml); } else { - throw CaretException("unknown Cifti writing version: '" + writingVersion.toString() + "'"); + throw DataFileException("unknown Cifti writing version: '" + writingVersion.toString() + "'"); } xml.writeEndElement(); } @@ -758,7 +758,7 @@ void CiftiXML::writeMatrix1(QXmlStreamWriter& xml) const VolumeSpace volSpace; for (int i = 0; i < numDims; ++i) { - if (m_indexMaps[i] == NULL) throw CaretException("dimension " + QString::number(i) + " was not given a mapping"); + if (m_indexMaps[i] == NULL) throw DataFileException("dimension " + QString::number(i) + " was not given a mapping"); switch (m_indexMaps[i]->getType()) { case CiftiMappingType::BRAIN_MODELS: @@ -770,7 +770,7 @@ void CiftiXML::writeMatrix1(QXmlStreamWriter& xml) const { if (myMap.getVolumeSpace() != volSpace) { - throw CaretException("cannot write different volume spaces for different dimensions in CIFTI-1"); + throw DataFileException("cannot write different volume spaces for different dimensions in CIFTI-1"); } } else { haveVolData = true; @@ -788,7 +788,7 @@ void CiftiXML::writeMatrix1(QXmlStreamWriter& xml) const { if (myMap.getVolumeSpace() != volSpace) { - throw CaretException("cannot write different volume spaces for different dimensions in CIFTI-1"); + throw DataFileException("cannot write different volume spaces for different dimensions in CIFTI-1"); } } else { haveVolData = true; @@ -847,7 +847,7 @@ void CiftiXML::writeMatrix2(QXmlStreamWriter& xml) const int numDims = (int)m_indexMaps.size(); for (int i = 0; i < numDims; ++i) { - if (m_indexMaps[i] == NULL) throw CaretException("dimension " + QString::number(i) + " was not given a mapping"); + if (m_indexMaps[i] == NULL) throw DataFileException("dimension " + QString::number(i) + " was not given a mapping"); } xml.writeStartElement("Matrix"); if (m_filePalette != NULL) diff --git a/src/CommandLine/CMakeLists.txt b/src/CommandLine/CMakeLists.txt index c221625a9923394883bc40053602a20c9fcfa21d..65a1ee92870fe994bd61643ae695aa7c6147886a 100644 --- a/src/CommandLine/CMakeLists.txt +++ b/src/CommandLine/CMakeLists.txt @@ -6,17 +6,19 @@ PROJECT (CommandLine) #cmake_policy(SET CMP0015 OLD) -# -# Need XML from Qt -# -#SET(QT_DONT_USE_QTGUI TRUE) -SET(QT_USE_QTXML TRUE) -SET(QT_USE_QTNETWORK TRUE) # # Add QT for includes # -INCLUDE (${QT_USE_FILE}) +if(Qt5_FOUND) + include_directories(${Qt5Core_INCLUDE_DIRS}) +# include_directories(${Qt5Network_INCLUDE_DIRS}) +endif() +IF (QT4_FOUND) + SET(QT_USE_QTXML TRUE) + SET(QT_USE_QTNETWORK TRUE) + INCLUDE(${QT_USE_FILE}) +ENDIF () # # Name of executable @@ -26,7 +28,14 @@ set (EXE_NAME wb_command) # # Resources # -QT4_ADD_RESOURCES(IMAGE_RCS_SRCS ../Resources/GeneralResources/general_resources.qrc) +SET (RESOURCES_QRC_FILE ../Resources/General/general_resources.qrc) +IF (Qt5_FOUND) + QT5_ADD_RESOURCES(IMAGE_RCS_SRCS ${RESOURCES_QRC_FILE}) +ELSE (Qt5_FOUND) + IF (QT4_FOUND) + QT4_ADD_RESOURCES(IMAGE_RCS_SRCS ${RESOURCES_QRC_FILE}) + ENDIF() +ENDIF (Qt5_FOUND) # # Create the executable @@ -46,10 +55,37 @@ ELSE (APPLE) ENDIF (APPLE) +if(Qt5_FOUND) + set(QT5_LINK_LIBS + Qt5::Concurrent + Qt5::Core + Qt5::Gui + Qt5::Network +# Qt5::OpenGL +# Qt5::PrintSupport + Qt5::Test +# Qt5::Widgets + Qt5::Xml) +endif() + +SET (MESA_OR_OPENGL_LIBRARIES "") +IF (OSMESA_FOUND) + SET(MESA_OR_OPENGL_LIBRARIES + ${OSMESA_OFFSCREEN_LIBRARY} + ${OSMESA_GL_LIBRARY} + ${OSMESA_GLU_LIBRARY}) +ELSE() + IF (OPENGL_FOUND) + SET(MESA_OR_OPENGL_LIBRARIES ${OPENGL_LIBRARIES}) + ELSE() + MESSAGE(SEND_ERROR "Must have either OpenGL or Mesa3D for command line") + ENDIF() +ENDIF() + # # Libraries that are linked -# Note: OSMESA library variables will be empty and have no effect -# if OSMESA is not available +# Note: GLEW_LIBRARIES and OSMESA library variables will be empty and +# have no effect if not available # TARGET_LINK_LIBRARIES(${EXE_NAME} Commands @@ -57,8 +93,9 @@ Operations Algorithms OperationsBase Brain -${FTGL_LIBRARIES} Files +Graphics +${FTGL_LIBRARIES} Annotations Palette Gifti @@ -72,15 +109,17 @@ Common ${QUAZIP_LIBRARIES} ${FREETYPE_LIBRARIES} ${QT_LIBRARIES} -${OSMESA_OFFSCREEN_LIBRARY} -${OSMESA_GL_LIBRARY} -${OSMESA_GLU_LIBRARY} +${QT5_LINK_LIBS} +${GLEW_LIBRARIES} +${MESA_OR_OPENGL_LIBRARIES} ${ZLIB_LIBRARIES} ${LIBS}) -INSTALL(TARGETS ${EXE_NAME} DESTINATION bin) -INSTALL(PROGRAMS wb_shortcuts DESTINATION bin) -INSTALL(PROGRAMS bashcomplete_wb_command DESTINATION /usr/share/bash-completion/completions RENAME wb_command) +INCLUDE(GNUInstallDirs) + +INSTALL(TARGETS ${EXE_NAME} DESTINATION ${CMAKE_INSTALL_BINDIR}) +INSTALL(PROGRAMS wb_shortcuts DESTINATION ${CMAKE_INSTALL_BINDIR}) +INSTALL(FILES bashcomplete_wb_command DESTINATION ${CMAKE_INSTALL_DATAROOTDIR}/bash-completion/completions RENAME wb_command) # # Find Headers diff --git a/src/CommandLine/bashcomplete_wb_command b/src/CommandLine/bashcomplete_wb_command index adfd1a2f1d34d197dd3d3ff63176f82f6898d466..c3b0d0173cb3e8afacfaa0635979f29b9d948e46 100644 --- a/src/CommandLine/bashcomplete_wb_command +++ b/src/CommandLine/bashcomplete_wb_command @@ -21,7 +21,7 @@ # TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE # SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. -#put this file in (or link to it from) /etc/bash_completion.d or copy contents to ~/.bash_completion +#put this file in (or link to it from) /etc/bash_completion.d or /usr/share/bash-completion/completions or copy contents to ~/.bash_completion function _wb_command() { @@ -64,10 +64,10 @@ function _wb_command() fileglob) #bash_completion itself uses -f -X for file extension matching #using -G requires manually adding $cur before the pattern - COMPREPLY+=(`compgen -f -X "!${exereply[$((i + 1))]}" -- "$cur"`) + COMPREPLY+=(`compgen -o plusdirs -f -X "!${exereply[$((i + 1))]}" -- "$cur"`) ;; wordlist) - #for the things accepted as bool, maybe other uses - happens to function like "option" for now + #for the things accepted as bool, logging levels, maybe other uses COMPREPLY+=(`compgen -W "${exereply[$((i + 1))]}" -- "$cur"`) ;; esac @@ -94,7 +94,8 @@ function _wb_shortcuts () fi local -a switches readarray -t switches < <(wb_shortcuts -list-functions | awk '{print $1}') - COMPREPLY=(`compgen -W "${switches[*]}" -- "$cur"`) + local infoswitches=(-help -version -list-functions -all-functions-help) + COMPREPLY=(`compgen -W "${switches[*]} ${infoswitches[*]}" -- "$cur"`) } complete -o filenames -o bashdefault -o default -F _wb_shortcuts wb_shortcuts diff --git a/src/CommandLine/wb_command.cxx b/src/CommandLine/wb_command.cxx index 8a1864c94b3a988cacc16ce64c7ea7083ccb64e9..9e618ffa73b1284e57a4e8bbc30fa494a601659b 100644 --- a/src/CommandLine/wb_command.cxx +++ b/src/CommandLine/wb_command.cxx @@ -18,7 +18,7 @@ */ /*LICENSE_END*/ -#include +//#include #include #include diff --git a/src/CommandLine/wb_shortcuts b/src/CommandLine/wb_shortcuts index a249ef934bce189feca5aec6b4f8ffd01c053868..08511599f00c0b1250f7958d2d6cf0fce5cd9c84 100755 --- a/src/CommandLine/wb_shortcuts +++ b/src/CommandLine/wb_shortcuts @@ -119,7 +119,6 @@ function switch_to_func_name () #ADDING FUNCTIONS: #the function to call is automatically generated from the command switch (see above functions) #the function MUST follow the same naming convention -#the short description (second argument to create_function) MUST NOT be empty, or it will think the operation doesn't exist, and run a partial match search #you can add names of temporary files before they are created by any command, to ensure that no extra files get left behind on failure #if the temporaries could be large, feel free to both add them to the list, and delete them manually @@ -133,12 +132,18 @@ function border_file_concatenate () echo "`switch_to_descrip $function_switch`" echo " $global_script_name $function_switch" echo " " - echo " " + echo " | -from-file [-big-text] " echo " ..." echo #wrap guide for 80 columns | echo " Additional input files may be specified after the mandatory input file." echo + echo " Any input argument can be replaced with a -from-file option followed by a" + echo " text file. Each text file must have one data file per line. The" + echo " -big-text option allows the use of very large text files (>1MB). This" + echo " is otherwise considered an error, in order to prevent accidentally" + echo " loading a data file into shell memory as a text file." + echo return 0 fi if (($# < 2)) @@ -148,7 +153,55 @@ function border_file_concatenate () local -a merge_arg_array for ((i = 2; i <= $#; ++i)) do - merge_arg_array+=("-border" "${!i}") + if [[ "${!i}" == -* ]] + then + case "${!i}" in + (-from-file) + i=$((i + 1)) + if ((i > $#)) + then + error "-from-file option requires an argument" + fi + local checksize=1 + if [[ "${!i}" == -big-text ]] + then + checksize=0 + i=$((i + 1)) + fi + local filename="${!i}" + local nexti=$((i + 1)) + #allow the option after the filename, too, because that is the easy place to put it + if ((nexti <= $#)) && [[ "${!nexti}" == -big-text ]] + then + checksize=0 + i=$((i + 1)) + fi + if ((checksize == 1)) + then + local filesize=`wc -c "$filename" | cut -d' ' -f1` + if ((filesize > 1000000)) + then + error "file '${!i}' seems too large to be a text file" + fi + fi + local -a filecontents + readarray -t filecontents < "$filename" + for ((j = 0; j < ${#filecontents[@]}; ++j)) + do + #ignore empty lines + if [[ "${filecontents[$j]}" != "" ]] + then + merge_arg_array+=("-border" "${filecontents[$j]}") + fi + done + ;; + (*) + error "unrecognized option '${!i}'" + ;; + esac + else + merge_arg_array+=("-border" "${!i}") + fi done wb_command -border-merge "$1" "${merge_arg_array[@]}" } @@ -165,13 +218,21 @@ function cifti_concatenate () echo " [-map ] - use only the specified map from each input" echo " file" echo " " - echo " " + echo " | -from-file [-big-text] " echo " ..." echo #wrap guide for 80 columns | echo " Additional input files may be specified after the mandatory input file." - echo " The -map option takes either a 1-based index or a map name, and causes" - echo " the operation to use only one map from each input file." + echo + echo " Any input argument can be replaced with a -from-file option followed by a" + echo " text file. Each text file must have one data file per line. The" + echo " -big-text option allows the use of very large text files (>1MB). This" + echo " is otherwise considered an error, in order to prevent accidentally" + echo " loading a data file into shell memory as a text file." + echo + echo " The -map option takes either a 1-based" + echo " index or a map name, and causes the" + echo " operation to use only one map from each input file." echo return 0 fi @@ -192,7 +253,59 @@ function cifti_concatenate () local -a merge_arg_array for ((i = 2; i <= $#; ++i)) do - merge_arg_array+=("-cifti" "${!i}" ${maparg[@]+"${maparg[@]}"}) + if [[ "${!i}" == -* ]] + then + case "${!i}" in + (-from-file) + i=$((i + 1)) + if ((i > $#)) + then + error "-from-file option requires an argument" + fi + local checksize=1 + if [[ "${!i}" == -big-text ]] + then + checksize=0 + i=$((i + 1)) + fi + local filename="${!i}" + local nexti=$((i + 1)) + #allow the option after the filename, too, because that is the easy place to put it + if ((nexti <= $#)) && [[ "${!nexti}" == -big-text ]] + then + checksize=0 + i=$((i + 1)) + fi + if ((checksize == 1)) + then + local filesize=`wc -c "$filename" | cut -d' ' -f1` + if ((filesize > 1000000)) + then + error "file '${!i}' seems too large to be a text file" + fi + fi + local -a filecontents + readarray -t filecontents < "$filename" + for ((j = 0; j < ${#filecontents[@]}; ++j)) + do + #ignore empty lines + if [[ "${filecontents[$j]}" != "" ]] + then + merge_arg_array+=("-cifti" "${filecontents[$j]}" ${maparg[@]+"${maparg[@]}"}) + fi + done + ;; + (*) + error "unrecognized option '${!i}'" + ;; + esac + else + #we INTENTIONALLY expand an empty array to 0 elements, but set -u doesn't like this, and there is no simple syntax to tell it to allow it + #so, use complicated syntax to tell it to allow it + #this syntax is apparently UNDOCUMENTED, but it came from stackoverflow, and it works, so it must be right + #http://stackoverflow.com/questions/7577052/bash-empty-array-expansion-with-set-u + merge_arg_array+=("-cifti" "${!i}" ${maparg[@]+"${maparg[@]}"}) + fi done wb_command -cifti-merge "$1" "${merge_arg_array[@]}" } @@ -475,13 +588,21 @@ function label_concatenate () echo " [-map ] - use only the specified map from each input" echo " file" echo " " - echo " " + echo " | -from-file [-big-text] " echo " ..." echo #wrap guide for 80 columns | echo " Additional input files may be specified after the mandatory input file." - echo " The -map option takes either a 1-based index or a map name, and causes" - echo " the operation to use only one map from each input file." + echo + echo " Any input argument can be replaced with a -from-file option followed by a" + echo " text file. Each text file must have one data file per line. The" + echo " -big-text option allows the use of very large text files (>1MB). This" + echo " is otherwise considered an error, in order to prevent accidentally" + echo " loading a data file into shell memory as a text file." + echo + echo " The -map option takes either a 1-based" + echo " index or a map name, and causes the" + echo " operation to use only one map from each input file." echo return 0 fi @@ -503,7 +624,55 @@ function label_concatenate () local i for ((i = 2; i <= $#; ++i)) do - merge_arg_array+=("-label" "${!i}" ${maparg[@]+"${maparg[@]}"}) + if [[ "${!i}" == -* ]] + then + case "${!i}" in + (-from-file) + i=$((i + 1)) + if ((i > $#)) + then + error "-from-file option requires an argument" + fi + local checksize=1 + if [[ "${!i}" == -big-text ]] + then + checksize=0 + i=$((i + 1)) + fi + local filename="${!i}" + local nexti=$((i + 1)) + #allow the option after the filename, too, because that is the easy place to put it + if ((nexti <= $#)) && [[ "${!nexti}" == -big-text ]] + then + checksize=0 + i=$((i + 1)) + fi + if ((checksize == 1)) + then + local filesize=`wc -c "$filename" | cut -d' ' -f1` + if ((filesize > 1000000)) + then + error "file '${!i}' seems too large to be a text file" + fi + fi + local -a filecontents + readarray -t filecontents < "$filename" + for ((j = 0; j < ${#filecontents[@]}; ++j)) + do + #ignore empty lines + if [[ "${filecontents[$j]}" != "" ]] + then + merge_arg_array+=("-label" "${filecontents[$j]}" ${maparg[@]+"${maparg[@]}"}) + fi + done + ;; + (*) + error "unrecognized option '${!i}'" + ;; + esac + else + merge_arg_array+=("-label" "${!i}" ${maparg[@]+"${maparg[@]}"}) + fi done wb_command -label-merge "$1" "${merge_arg_array[@]}" } @@ -520,13 +689,21 @@ function metric_concatenate () echo " [-map ] - use only the specified map from each input" echo " file" echo " " - echo " " + echo " | -from-file [-big-text] " echo " ..." echo #wrap guide for 80 columns | echo " Additional input files may be specified after the mandatory input file." - echo " The -map option takes either a 1-based index or a map name, and causes" - echo " the operation to use only one map from each input file." + echo + echo " Any input argument can be replaced with a -from-file option followed by a" + echo " text file. Each text file must have one data file per line. The" + echo " -big-text option allows the use of very large text files (>1MB). This" + echo " is otherwise considered an error, in order to prevent accidentally" + echo " loading a data file into shell memory as a text file." + echo + echo " The -map option takes either a 1-based" + echo " index or a map name, and causes the" + echo " operation to use only one map from each input file." echo return 0 fi @@ -548,7 +725,55 @@ function metric_concatenate () local i for ((i = 2; i <= $#; ++i)) do - merge_arg_array+=("-metric" "${!i}" ${maparg[@]+"${maparg[@]}"}) + if [[ "${!i}" == -* ]] + then + case "${!i}" in + (-from-file) + i=$((i + 1)) + if ((i > $#)) + then + error "-from-file option requires an argument" + fi + local checksize=1 + if [[ "${!i}" == -big-text ]] + then + checksize=0 + i=$((i + 1)) + fi + local filename="${!i}" + local nexti=$((i + 1)) + #allow the option after the filename, too, because that is the easy place to put it + if ((nexti <= $#)) && [[ "${!nexti}" == -big-text ]] + then + checksize=0 + i=$((i + 1)) + fi + if ((checksize == 1)) + then + local filesize=`wc -c "$filename" | cut -d' ' -f1` + if ((filesize > 1000000)) + then + error "file '${!i}' seems too large to be a text file" + fi + fi + local -a filecontents + readarray -t filecontents < "$filename" + for ((j = 0; j < ${#filecontents[@]}; ++j)) + do + #ignore empty lines + if [[ "${filecontents[$j]}" != "" ]] + then + merge_arg_array+=("-metric" "${filecontents[$j]}" ${maparg[@]+"${maparg[@]}"}) + fi + done + ;; + (*) + error "unrecognized option '${!i}'" + ;; + esac + else + merge_arg_array+=("-metric" "${!i}" ${maparg[@]+"${maparg[@]}"}) + fi done wb_command -metric-merge "$1" "${merge_arg_array[@]}" } @@ -563,12 +788,18 @@ function scene_file_concatenate () echo "`switch_to_descrip $function_switch`" echo " $global_script_name $function_switch" echo " " - echo " " + echo " | -from-file [-big-text] " echo " ..." echo #wrap guide for 80 columns | echo " Additional input files may be specified after the mandatory input file." echo + echo " Any input argument can be replaced with a -from-file option followed by a" + echo " text file. Each text file must have one data file per line. The" + echo " -big-text option allows the use of very large text files (>1MB). This" + echo " is otherwise considered an error, in order to prevent accidentally" + echo " loading a data file into shell memory as a text file." + echo return 0 fi if (($# < 2)) @@ -579,7 +810,55 @@ function scene_file_concatenate () local i for ((i = 2; i <= $#; ++i)) do - merge_arg_array+=("-scene-file" "${!i}") + if [[ "${!i}" == -* ]] + then + case "${!i}" in + (-from-file) + i=$((i + 1)) + if ((i > $#)) + then + error "-from-file option requires an argument" + fi + local checksize=1 + if [[ "${!i}" == -big-text ]] + then + checksize=0 + i=$((i + 1)) + fi + local filename="${!i}" + local nexti=$((i + 1)) + #allow the option after the filename, too, because that is the easy place to put it + if ((nexti <= $#)) && [[ "${!nexti}" == -big-text ]] + then + checksize=0 + i=$((i + 1)) + fi + if ((checksize == 1)) + then + local filesize=`wc -c "$filename" | cut -d' ' -f1` + if ((filesize > 1000000)) + then + error "file '${!i}' seems too large to be a text file" + fi + fi + local -a filecontents + readarray -t filecontents < "$filename" + for ((j = 0; j < ${#filecontents[@]}; ++j)) + do + #ignore empty lines + if [[ "${filecontents[$j]}" != "" ]] + then + merge_arg_array+=("-scene-file" "${filecontents[$j]}") + fi + done + ;; + (*) + error "unrecognized option '${!i}'" + ;; + esac + else + merge_arg_array+=("-scene-file" "${!i}") + fi done wb_command -scene-file-merge "$1" "${merge_arg_array[@]}" } @@ -596,13 +875,21 @@ function volume_concatenate () echo " [-map ] - use only the specified map from each input" echo " file" echo " " - echo " " + echo " | -from-file [-big-text] " echo " ..." echo #wrap guide for 80 columns | echo " Additional input files may be specified after the mandatory input file." - echo " The -map option takes either a 1-based index or a map name, and causes" - echo " the operation to use only one map from each input file." + echo + echo " Any input argument can be replaced with a -from-file option followed by a" + echo " text file. Each text file must have one data file per line. The" + echo " -big-text option allows the use of very large text files (>1MB). This" + echo " is otherwise considered an error, in order to prevent accidentally" + echo " loading a data file into shell memory as a text file." + echo + echo " The -map option takes either a 1-based" + echo " index or a map name, and causes the" + echo " operation to use only one map from each input file." echo return 0 fi @@ -622,13 +909,57 @@ function volume_concatenate () fi local -a merge_arg_array local i - #we INTENTIONALLY expand an empty array to 0 elements, but set -u doesn't like this, and there is no simple syntax to tell it to allow it - #so, use complicated syntax to tell it to allow it - #this syntax is apparently UNDOCUMENTED, but it came from stackoverflow, and it works, so it must be right - #http://stackoverflow.com/questions/7577052/bash-empty-array-expansion-with-set-u for ((i = 2; i <= $#; ++i)) do - merge_arg_array+=("-volume" "${!i}" ${maparg[@]+"${maparg[@]}"}) + if [[ "${!i}" == -* ]] + then + case "${!i}" in + (-from-file) + i=$((i + 1)) + if ((i > $#)) + then + error "-from-file option requires an argument" + fi + local checksize=1 + if [[ "${!i}" == -big-text ]] + then + checksize=0 + i=$((i + 1)) + fi + local filename="${!i}" + local nexti=$((i + 1)) + #allow the option after the filename, too, because that is the easy place to put it + if ((nexti <= $#)) && [[ "${!nexti}" == -big-text ]] + then + checksize=0 + i=$((i + 1)) + fi + if ((checksize == 1)) + then + local filesize=`wc -c "$filename" | cut -d' ' -f1` + if ((filesize > 1000000)) + then + error "file '${!i}' seems too large to be a text file" + fi + fi + local -a filecontents + readarray -t filecontents < "$filename" + for ((j = 0; j < ${#filecontents[@]}; ++j)) + do + #ignore empty lines + if [[ "${filecontents[$j]}" != "" ]] + then + merge_arg_array+=("-volume" "${filecontents[$j]}" ${maparg[@]+"${maparg[@]}"}) + fi + done + ;; + (*) + error "unrecognized option '${!i}'" + ;; + esac + else + merge_arg_array+=("-volume" "${!i}" ${maparg[@]+"${maparg[@]}"}) + fi done wb_command -volume-merge "$1" "${merge_arg_array[@]}" } diff --git a/src/Commands/CMakeLists.txt b/src/Commands/CMakeLists.txt index caf6e230182263f86c5576e41f95a7844e92ef1d..bee897a8d74ed8146206b02b5698a1e251e3c3c4 100644 --- a/src/Commands/CMakeLists.txt +++ b/src/Commands/CMakeLists.txt @@ -11,7 +11,13 @@ SET(QT_DONT_USE_QTGUI) # # Add QT for includes # -INCLUDE (${QT_USE_FILE}) +if(Qt5_FOUND) + include_directories(${Qt5Core_INCLUDE_DIRS}) +endif() +IF (QT4_FOUND) + INCLUDE(${QT_USE_FILE}) +ENDIF () + # # Create the brain library @@ -44,6 +50,8 @@ CommandParser.cxx CommandUnitTest.cxx ) +TARGET_LINK_LIBRARIES(Commands ${CARET_QT5_LINK}) + # # Find Headers # diff --git a/src/Commands/CommandClassAddMember.cxx b/src/Commands/CommandClassAddMember.cxx index 02be06a9a34bded4eb98a9aa165d97c68e13bfa5..04d7411246ba039b6ac30b6fbdf6474516a907ca 100644 --- a/src/Commands/CommandClassAddMember.cxx +++ b/src/Commands/CommandClassAddMember.cxx @@ -110,7 +110,7 @@ CommandClassAddMember::executeOperation(ProgramParameters& parameters) AString headerMethodCode = ""; AString implementationCode = ""; - const AString indentText = " "; + const AString indentText = " "; /* * Process parameters @@ -162,6 +162,7 @@ CommandClassAddMember::executeOperation(ProgramParameters& parameters) * is a class and use a pointer for the class. However, * treat AString and QString as primitive types. */ + //bool isEnum = false; bool isString = false; bool isClass = false; const QChar firstDataTypeChar = dataType[0]; @@ -170,6 +171,9 @@ CommandClassAddMember::executeOperation(ProgramParameters& parameters) || (dataType == "QString")) { isString = true; } + else if (dataType.endsWith("Enum")) { + //isEnum = true; + } else { isClass = true; } @@ -199,17 +203,25 @@ CommandClassAddMember::executeOperation(ProgramParameters& parameters) firstGetSetNameChar = firstGetSetNameChar.toUpper(); getSetName[0] = firstGetSetNameChar; } - const AString getterName = ("get" + getSetName); + const AString getterName = (((dataType == "bool") + ? "is" + : "get") + getSetName); const AString setterName = ("set" + getSetName); /* * Declare the member for the header file */ + if (isClass) { + headerMemberCode += ("#include \n" + "\n"); + } if (description.isEmpty() == false) { headerMemberCode += (indentText + "/** " + description + "*/\n"); } - headerMemberCode += (indentText - + returnType + headerMemberCode += (indentText + + (isClass ? "std::unique_ptr<" : "") + + dataType //returnType + + (isClass ? ">" : "") + " " + name + ";" @@ -220,6 +232,17 @@ CommandClassAddMember::executeOperation(ProgramParameters& parameters) const AString classColonName = (className + "::"); if (isClass) { + implementationCode += ("\n" + + indentText + + name + + " = std::unique_ptr<" + + dataType + + ">(new" + + dataType + + "());" + + "\n" + + "\n"); + /* * For class members, create only getter methods * that are both const and non-const @@ -248,7 +271,7 @@ CommandClassAddMember::executeOperation(ProgramParameters& parameters) + (isConstMethod ? " const" : "") + "\n" + "{\n" - + " return " + name + ";\n" + + " return " + name + ".get();\n" + "}\n" + "\n"); } @@ -291,6 +314,7 @@ CommandClassAddMember::executeOperation(ProgramParameters& parameters) implementationCode += ("/**\n" " * Set " + description + "\n" + " *\n" " * @param " + parameterName + "\n" " * New value for " + description + "\n" " */\n" diff --git a/src/Commands/CommandClassCreate.cxx b/src/Commands/CommandClassCreate.cxx index 3801c01c7396414763952b253587928af2dd95c2..19807a9f8f0d344fb21eb5cea92d21ead916dd0f 100644 --- a/src/Commands/CommandClassCreate.cxx +++ b/src/Commands/CommandClassCreate.cxx @@ -55,16 +55,22 @@ CommandClassCreate::getHelpInformation(const AString& /*programName*/) "\n" "Usage: \n" " [-copy] \n" + " [-equal] \n" " [-event-class ]\n" " [-event-listener] \n" " [-no-parent] \n" " [-parent ] \n" + " [-scene] \n" + " [-scene-sub-class] \n" "\n" "\n" "Options: \n" " -copy\n" " Adds copy constructor and assignment operator\n" " \n" + " -equal\n" + " Adds equality operator.\n" + " \n" " -event-class \n" " When creating an Event subclass, using this\n" " option will automatically set the parent\n" @@ -124,6 +130,7 @@ CommandClassCreate::executeOperation(ProgramParameters& parameters) AString eventTypeEnumName; bool hasCopyAndAssignment = false; + bool hasEqualityOperator = false; bool hasEventListener = false; bool hasScenes = false; bool hasScenesSubClass = false; @@ -133,6 +140,9 @@ CommandClassCreate::executeOperation(ProgramParameters& parameters) if (param == "-copy") { hasCopyAndAssignment = true; } + else if (param == "-equal") { + hasEqualityOperator = true; + } else if (param == "-event-class") { eventTypeEnumName = parameters.nextString("Event Type Enum Name"); if (eventTypeEnumName.contains("::") == false) { @@ -218,6 +228,7 @@ CommandClassCreate::executeOperation(ProgramParameters& parameters) ifndefName, ifdefNameStaticDeclarations, hasCopyAndAssignment, + hasEqualityOperator, hasEventListener, hasScenes, hasScenesSubClass); @@ -228,6 +239,7 @@ CommandClassCreate::executeOperation(ProgramParameters& parameters) eventTypeEnumName, ifdefNameStaticDeclarations, hasCopyAndAssignment, + hasEqualityOperator, hasEventListener, hasScenes, hasScenesSubClass); @@ -248,6 +260,8 @@ CommandClassCreate::executeOperation(ProgramParameters& parameters) * Name for "infdef" of static declarations. * @param hasCopyAndAssignment * Has copy constructor and assignment operator. + * @param hasEqualityOperator + * Class implements equality operator * @param hasEventListener * Class implements the EventListener interface * @param hasSceneInterface @@ -260,9 +274,10 @@ void CommandClassCreate::createHeaderFile(const AString& outputFileName, const AString& className, const AString& derivedFromClassName, - const AString& ifndefName, - const AString& ifdefNameStaticDeclaration, + const AString& ifndefName, + const AString& ifdefNameStaticDeclaration, const bool hasCopyAndAssignment, + const bool hasEqualityOperator, const bool hasEventListener, const bool hasSceneInterface, const bool hasSubClassSceneSaving) @@ -278,6 +293,11 @@ CommandClassCreate::createHeaderFile(const AString& outputFileName, t += ("#define " + ifndefName + "\n"); t += this->getCopyright(); t += ("\n"); + if (m_useUniquePtrFlag) { + t += ("\n"); + t += ("#include \n"); + t += ("\n"); + } t += (this->getIncludeDeclaration(derivedFromClassName) + "\n"); t += ("\n"); @@ -329,12 +349,15 @@ CommandClassCreate::createHeaderFile(const AString& outputFileName, t += (" \n"); } else { -// t += (" private:\n"); -// t += (" " + className + "(const " + className + "&);\n"); -// t += ("\n"); -// t += (" " + className + "& operator=(const " + className + "&);\n"); -// t += (" \n"); -// t += (" public:\n"); + t += (" " + className + "(const " + className + "&) = delete;\n"); + t += ("\n"); + t += (" " + className + "& operator=(const " + className + "&) = delete;\n"); + t += (" \n"); + } + + if (hasEqualityOperator) { + t += (" bool operator==(const " + className + "& obj) const;\n"); + t += (" \n"); } t += ("\n"); @@ -391,15 +414,14 @@ CommandClassCreate::createHeaderFile(const AString& outputFileName, t += (" void copyHelper" + className + "(const " + className + "& obj);\n"); t += ("\n"); } - else { - t += (" " + className + "(const " + className + "&);\n"); - t += ("\n"); - t += (" " + className + "& operator=(const " + className + "&);\n"); - t += (" \n"); - } if (hasSceneInterface || hasSubClassSceneSaving) { - t += (" SceneClassAssistant* m_sceneAssistant;\n"); + if (m_useUniquePtrFlag) { + t += (" std::unique_ptr m_sceneAssistant;\n"); + } + else { + t += (" SceneClassAssistant* m_sceneAssistant;\n"); + } t += ("\n"); } t += (" " + getNewMembersString() + "\n"); @@ -440,6 +462,8 @@ CommandClassCreate::createHeaderFile(const AString& outputFileName, * Name for "infdef" of static declarations. * @param hasCopyAndAssignment * Has copy constructor and assignment operator. + * @param hasEqualityOperator + * Class implements equality operator * @param hasEventListener * Class implements the EventListener interface * @param hasSceneInterface @@ -453,8 +477,9 @@ CommandClassCreate::createImplementationFile(const AString& outputFileName, const AString& className, const AString& derivedFromClassName, const AString& eventTypeEnumName, - const AString& ifdefNameStaticDeclaration, + const AString& ifdefNameStaticDeclaration, const bool hasCopyAndAssignment, + const bool hasEqualityOperator, const bool hasEventListener, const bool hasSceneInterface, const bool hasSubClassSceneSaving) @@ -513,7 +538,12 @@ CommandClassCreate::createImplementationFile(const AString& outputFileName, t += (" \n"); if (hasSceneInterface || hasSubClassSceneSaving) { - t += (" m_sceneAssistant = new SceneClassAssistant();\n"); + if (m_useUniquePtrFlag) { + t += (" m_sceneAssistant = std::unique_ptr(new SceneClassAssistant());\n"); + } + else { + t += (" m_sceneAssistant = new SceneClassAssistant();\n"); + } t += (" \n"); } if (hasEventListener) { @@ -531,7 +561,9 @@ CommandClassCreate::createImplementationFile(const AString& outputFileName, } if (hasSceneInterface || hasSubClassSceneSaving) { - t += (" delete m_sceneAssistant;\n"); + if ( ! m_useUniquePtrFlag) { + t += (" delete m_sceneAssistant;\n"); + } } t += ("}\n"); t += ("\n"); @@ -578,6 +610,28 @@ CommandClassCreate::createImplementationFile(const AString& outputFileName, t += ("\n"); } + if (hasEqualityOperator) { + t += ("/**\n"); + t += (" * Equality operator.\n"); + t += (" * @param obj\n"); + t += (" * Instance compared to this for equality.\n"); + t += (" * @return \n"); + t += (" * True if this instance and 'obj' instance are considered equal.\n"); + t += (" */\n"); + t += ("bool\n"); + t += ("" + className + "::operator==(const " + className + "& obj) const\n"); + t += ("{\n"); + t += (" if (this == &obj) {\n"); + t += (" return true; \n"); + t += (" }\n"); + t += ("\n"); + t += (" /* perform equality testing HERE and return true if equal ! */\n"); + t += ("\n"); + t += (" return false; \n"); + t += ("}\n"); + t += ("\n"); + } + if (derivedFromClassName == "CaretObject") { t += ("/**\n"); t += (" * Get a description of this object's content.\n"); diff --git a/src/Commands/CommandClassCreate.h b/src/Commands/CommandClassCreate.h index b935e28e1df8e9aef40bc7b559e2d41fc4243738..ec04160df8b14b0b40223470b549d7e3cc13c72c 100644 --- a/src/Commands/CommandClassCreate.h +++ b/src/Commands/CommandClassCreate.h @@ -57,6 +57,7 @@ namespace caret { const AString& ifdefName, const AString& ifdefNameStaticDeclaration, const bool hasCopyAndAssignment, + const bool hasEqualityOperator, const bool hasEventListener, const bool hasSceneInterface, const bool hasSubClassSceneSaving); @@ -67,9 +68,12 @@ namespace caret { const AString& eventTypeEnumName, const AString& ifdefNameStaticDeclaration, const bool hasCopyAndAssignment, + const bool hasEqualityOperator, const bool hasEventListener, const bool hasSceneInterface, const bool hasSubClassSceneSaving); + + const bool m_useUniquePtrFlag = true; }; } // namespace diff --git a/src/Commands/CommandOperation.cxx b/src/Commands/CommandOperation.cxx index 081883e04b049538fe68ca7a4d62d6655af1e864..9943c1c1df031798140d156fd77c07a494b4ad80 100644 --- a/src/Commands/CommandOperation.cxx +++ b/src/Commands/CommandOperation.cxx @@ -37,6 +37,7 @@ CommandOperation::CommandOperation(const AString& commandLineSwitch, { this->commandLineSwitch = commandLineSwitch; this->operationShortDescription = operationShortDescription; + CaretAssert(commandLineSwitch != "-command-switch");//catch failure to change things in the command template for (int i = 0; i < commandLineSwitch.length(); ++i)//release build should optimize out empty loops { CaretAssert(commandLineSwitch[i].unicode() < 128); @@ -73,6 +74,14 @@ void CommandOperation::disableProvenance() { } +void CommandOperation::setCiftiOutputDTypeAndScale(const int16_t&, const double&, const double&) +{ +} + +void CommandOperation::setCiftiOutputDTypeNoScale(const int16_t&) +{ +} + AString CommandOperation::doCompletion(ProgramParameters&, const bool&) { return ""; diff --git a/src/Commands/CommandOperation.h b/src/Commands/CommandOperation.h index ce8c8d9538d7753499c2aefd52f83778369140e0..0d54ef43509d2f66c0dbb820ce16dee9f14276ef 100644 --- a/src/Commands/CommandOperation.h +++ b/src/Commands/CommandOperation.h @@ -27,6 +27,8 @@ #include "ProgramParametersException.h" #include "AString.h" +#include "nifti1.h" + namespace caret { class ProgramParameters; @@ -39,6 +41,10 @@ namespace caret { void execute(ProgramParameters& parameters, const bool& preventProvenance); + virtual void setCiftiOutputDTypeAndScale(const int16_t& dtype, const double& minVal, const double& maxVal); + + virtual void setCiftiOutputDTypeNoScale(const int16_t& dtype); + virtual AString doCompletion(ProgramParameters& parameters, const bool& useExtGlob); protected: diff --git a/src/Commands/CommandOperationManager.cxx b/src/Commands/CommandOperationManager.cxx index 6f1057028a2aee7b10b48ae52b362c9f73dc3e4f..12830f91e3dc65c1e6c306e6ce48be539a7ff34d 100644 --- a/src/Commands/CommandOperationManager.cxx +++ b/src/Commands/CommandOperationManager.cxx @@ -43,6 +43,7 @@ #include "AlgorithmCiftiFindClusters.h" #include "AlgorithmCiftiGradient.h" #include "AlgorithmCiftiLabelAdjacency.h" +#include "AlgorithmCiftiLabelProbability.h" #include "AlgorithmCiftiLabelToBorder.h" #include "AlgorithmCiftiLabelToROI.h" #include "AlgorithmCiftiMergeDense.h" @@ -68,6 +69,7 @@ #include "AlgorithmLabelDilate.h" #include "AlgorithmLabelErode.h" #include "AlgorithmLabelModifyKeys.h" +#include "AlgorithmLabelProbability.h" #include "AlgorithmLabelResample.h" #include "AlgorithmLabelToBorder.h" #include "AlgorithmLabelToVolumeMapping.h" @@ -113,6 +115,7 @@ #include "AlgorithmVolumeAffineResample.h" #include "AlgorithmVolumeAllLabelsToROIs.h" #include "AlgorithmVolumeDilate.h" +#include "AlgorithmVolumeDistortion.h" #include "AlgorithmVolumeErode.h" #include "AlgorithmVolumeEstimateFWHM.h" #include "AlgorithmVolumeExtrema.h" @@ -132,6 +135,7 @@ #include "AlgorithmVolumeTFCE.h" #include "AlgorithmVolumeToSurfaceMapping.h" #include "AlgorithmVolumeVectorOperation.h" +#include "AlgorithmVolumeWarpfieldAffineRegression.h" #include "AlgorithmVolumeWarpfieldResample.h" #include "OperationAddToSpecFile.h" @@ -205,6 +209,7 @@ #include "OperationSurfaceGeodesicROIs.h" #include "OperationSurfaceInformation.h" #include "OperationSurfaceNormals.h" +#include "OperationSurfaceSetCoordinates.h" #include "OperationSurfaceVertexAreas.h" #include "OperationVolumeCapturePlane.h" #include "OperationVolumeCopyExtensions.h" @@ -241,6 +246,7 @@ #include "StructureEnum.h" #include +#include using namespace caret; using namespace std; @@ -297,6 +303,7 @@ CommandOperationManager::CommandOperationManager() this->commandOperations.push_back(new CommandParser(new AutoAlgorithmCiftiFindClusters())); this->commandOperations.push_back(new CommandParser(new AutoAlgorithmCiftiGradient())); this->commandOperations.push_back(new CommandParser(new AutoAlgorithmCiftiLabelAdjacency())); + this->commandOperations.push_back(new CommandParser(new AutoAlgorithmCiftiLabelProbability())); this->commandOperations.push_back(new CommandParser(new AutoAlgorithmCiftiLabelToBorder())); this->commandOperations.push_back(new CommandParser(new AutoAlgorithmCiftiLabelToROI())); this->commandOperations.push_back(new CommandParser(new AutoAlgorithmCiftiMergeDense())); @@ -323,6 +330,7 @@ CommandOperationManager::CommandOperationManager() this->commandOperations.push_back(new CommandParser(new AutoAlgorithmLabelDilate())); this->commandOperations.push_back(new CommandParser(new AutoAlgorithmLabelErode())); this->commandOperations.push_back(new CommandParser(new AutoAlgorithmLabelModifyKeys())); + this->commandOperations.push_back(new CommandParser(new AutoAlgorithmLabelProbability())); this->commandOperations.push_back(new CommandParser(new AutoAlgorithmLabelResample())); this->commandOperations.push_back(new CommandParser(new AutoAlgorithmLabelToBorder())); this->commandOperations.push_back(new CommandParser(new AutoAlgorithmLabelToVolumeMapping())); @@ -368,6 +376,7 @@ CommandOperationManager::CommandOperationManager() this->commandOperations.push_back(new CommandParser(new AutoAlgorithmVolumeAffineResample())); this->commandOperations.push_back(new CommandParser(new AutoAlgorithmVolumeAllLabelsToROIs())); this->commandOperations.push_back(new CommandParser(new AutoAlgorithmVolumeDilate())); + this->commandOperations.push_back(new CommandParser(new AutoAlgorithmVolumeDistortion())); this->commandOperations.push_back(new CommandParser(new AutoAlgorithmVolumeErode())); this->commandOperations.push_back(new CommandParser(new AutoAlgorithmVolumeEstimateFWHM())); this->commandOperations.push_back(new CommandParser(new AutoAlgorithmVolumeExtrema())); @@ -387,6 +396,7 @@ CommandOperationManager::CommandOperationManager() this->commandOperations.push_back(new CommandParser(new AutoAlgorithmVolumeTFCE())); this->commandOperations.push_back(new CommandParser(new AutoAlgorithmVolumeToSurfaceMapping())); this->commandOperations.push_back(new CommandParser(new AutoAlgorithmVolumeVectorOperation())); + this->commandOperations.push_back(new CommandParser(new AutoAlgorithmVolumeWarpfieldAffineRegression())); this->commandOperations.push_back(new CommandParser(new AutoAlgorithmVolumeWarpfieldResample())); this->commandOperations.push_back(new CommandParser(new AutoOperationAddToSpecFile())); @@ -455,6 +465,7 @@ CommandOperationManager::CommandOperationManager() this->commandOperations.push_back(new CommandParser(new AutoOperationSurfaceGeodesicROIs())); this->commandOperations.push_back(new CommandParser(new AutoOperationSurfaceInformation())); this->commandOperations.push_back(new CommandParser(new AutoOperationSurfaceNormals())); + this->commandOperations.push_back(new CommandParser(new AutoOperationSurfaceSetCoordinates())); this->commandOperations.push_back(new CommandParser(new AutoOperationSurfaceVertexAreas())); this->commandOperations.push_back(new CommandParser(new AutoOperationVolumeCapturePlane())); this->commandOperations.push_back(new CommandParser(new AutoOperationVolumeCopyExtensions())); @@ -509,6 +520,32 @@ CommandOperationManager::~CommandOperationManager() this->deprecatedOperations.clear(); } +namespace +{ + //quick hack to convert type argument to internal integer + int16_t stringToCiftiType(const AString& input) + { + map nameToCode; + nameToCode["INT8"] = NIFTI_TYPE_INT8; + nameToCode["UINT8"] = NIFTI_TYPE_UINT8; + nameToCode["INT16"] = NIFTI_TYPE_INT16; + nameToCode["UINT16"] = NIFTI_TYPE_UINT16; + nameToCode["INT32"] = NIFTI_TYPE_INT32; + nameToCode["UINT32"] = NIFTI_TYPE_UINT32; + nameToCode["INT64"] = NIFTI_TYPE_INT64; + nameToCode["UINT64"] = NIFTI_TYPE_UINT64; + nameToCode["FLOAT32"] = NIFTI_TYPE_FLOAT32; + nameToCode["FLOAT64"] = NIFTI_TYPE_FLOAT64; + nameToCode["FLOAT128"] = NIFTI_TYPE_FLOAT128; + map::iterator iter = nameToCode.find(input); + if (iter == nameToCode.end()) + { + throw CommandException("Unrecognized cifti datatype: '" + input + "'"); + } + return iter->second; + } +} + /** * Run a command. * @@ -540,6 +577,22 @@ CommandOperationManager::runCommand(ProgramParameters& parameters) CaretLogWarning("SIMD type '" + DotSIMDEnum::toName(impl) + "' not supported (could be cpu, compiler, or build options), using '" + DotSIMDEnum::toName(retval) + "'"); } } + int16_t ciftiDType = NIFTI_TYPE_FLOAT32; + bool ciftiScale = false; + double ciftiMin = -1.0, ciftiMax = -1.0; + if (getGlobalOption(parameters, "-cifti-output-datatype", 1, globalOptionArgs)) + { + ciftiDType = stringToCiftiType(globalOptionArgs[0]); + } + if (getGlobalOption(parameters, "-cifti-output-range", 2, globalOptionArgs)) + { + ciftiScale = true; + bool valid = false; + ciftiMin = globalOptionArgs[0].toDouble(&valid); + if (!valid) throw CommandException("non-numeric option to -cifti-output-range: '" + globalOptionArgs[0] + "'"); + ciftiMax = globalOptionArgs[1].toDouble(&valid); + if (!valid) throw CommandException("non-numeric option to -cifti-output-range: '" + globalOptionArgs[1] + "'"); + } const uint64_t numberOfCommands = this->commandOperations.size(); const uint64_t numberOfDeprecated = this->deprecatedOperations.size(); @@ -552,15 +605,21 @@ CommandOperationManager::runCommand(ProgramParameters& parameters) AString commandSwitch; commandSwitch = fixUnicode(parameters.nextString("Command Name"), false); + //hardcode program name, instead of taking it from the parameters, so that it doesn't include path or show wrapper script details + const AString myProgramName = "wb_command"; if (commandSwitch == "-help") { printHelpInfo(); } else if (commandSwitch == "-arguments-help") { - printArgumentsHelp("wb_command"); + printArgumentsHelp(myProgramName); + } else if (commandSwitch == "-global-options") { + printGlobalOptions(); } else if (commandSwitch == "-cifti-help") { - printCiftiHelp("wb_command"); + printCiftiHelp(); } else if (commandSwitch == "-gifti-help") { - printGiftiHelp("wb_command"); + printGiftiHelp(); + } else if (commandSwitch == "-parallel-help") { + printParallelHelp(myProgramName); } else if (commandSwitch == "-version") { printVersionInfo(); } else if (commandSwitch == "-list-commands") { @@ -568,7 +627,7 @@ CommandOperationManager::runCommand(ProgramParameters& parameters) } else if (commandSwitch == "-list-deprecated-commands") { printDeprecatedCommands(); } else if (commandSwitch == "-all-commands-help") { - printAllCommandsHelpInfo("wb_command"); + printAllCommandsHelpInfo(myProgramName); } else { CommandOperation* operation = NULL; @@ -603,8 +662,14 @@ CommandOperationManager::runCommand(ProgramParameters& parameters) } else { if (!parameters.hasNext() && operation->takesParameters()) { - cout << operation->getHelpInformation("wb_command") << endl; + cout << operation->getHelpInformation(myProgramName) << endl; } else { + if (ciftiScale) + { + operation->setCiftiOutputDTypeAndScale(ciftiDType, ciftiMin, ciftiMax); + } else { + operation->setCiftiOutputDTypeNoScale(ciftiDType); + } operation->execute(parameters, preventProvenance); } } @@ -641,12 +706,22 @@ AString CommandOperationManager::doCompletion(ProgramParameters& parameters, con } return ret; } - ret = "wordlist -disable-provenance\\ -logging\\ -simd";//we could prevent suggesting an already-provided global option, but that would be a bit surprising + OptionInfo ciftiDTypeInfo = parseGlobalOption(parameters, "-cifti-output-datatype", 1, globalOptionArgs, true); + if (ciftiDTypeInfo.specified && !ciftiDTypeInfo.complete) + { + return "wordlist INT8 UINT8 INT16 UINT16 INT32 UINT32 INT64 UINT64 FLOAT32 FLOAT64 FLOAT128"; + } + OptionInfo ciftiRangeInfo = parseGlobalOption(parameters, "-cifti-output-range", 2, globalOptionArgs, true); + if (ciftiRangeInfo.specified && !ciftiRangeInfo.complete) + {//can't tab complete a literal number + return ""; + } + ret = "wordlist -disable-provenance\\ -logging\\ -simd\\ -cifti-output-datatype\\ -cifti-output-range";//we could prevent suggesting an already-provided global option, but that would be a bit surprising const uint64_t numberOfCommands = this->commandOperations.size(); const uint64_t numberOfDeprecated = this->deprecatedOperations.size(); if (!parameters.hasNext()) {//suggest all commands, including deprecated and informational (order doesn't matter, bash sorts them before displaying) - ret += "\\ -help\\ -arguments-help\\ -cifti-help\\ -gifti-help\\ -version\\ -list-commands\\ -list-deprecated-commands\\ -all-commands-help"; + ret += "\\ -help\\ -arguments-help\\ -cifti-help\\ -gifti-help\\ -parallel-help\\ -version\\ -list-commands\\ -list-deprecated-commands\\ -all-commands-help"; for (uint64_t i = 0; i < numberOfCommands; i++) { ret += "\\ " + commandOperations[i]->getCommandLineSwitch(); @@ -893,6 +968,8 @@ void CommandOperationManager::printHelpInfo() cout << endl << "Information options:" << endl; cout << " -help show this help info" << endl; cout << " -arguments-help explain the format of subcommand help info" << endl; + cout << " -global-options display options that can be added to any command" << endl; + cout << " -parallel-help details on how wb_command uses parallelization" << endl; cout << " -cifti-help explain the cifti file format and related terms" << endl; cout << " -gifti-help explain the gifti file format (metric, surface)" << endl; cout << " -version show extended version information" << endl; @@ -900,27 +977,6 @@ void CommandOperationManager::printHelpInfo() cout << " -list-deprecated-commands list deprecated subcommands" << endl; cout << " -all-commands-help show all processing subcommands and their help" << endl; cout << " info - VERY LONG" << endl; - cout << endl << "Global options (can be added to any command):" << endl; - cout << " -disable-provenance don't generate provenance info in output files" << endl; - cout << " -logging set the logging level, valid values are:" << endl; - vector logLevels; - LogLevelEnum::getAllEnums(logLevels); - for (vector::iterator iter = logLevels.begin(); - iter != logLevels.end(); - iter++) { - cout << " " << LogLevelEnum::toName(*iter) << endl; - } - cout << endl;//add a line after the logging types for readability - //guide for wrap, assuming 80 columns: | - cout << " -simd set the SIMD implementation to use (currently" << endl; - cout << " used only for correlation, default AUTO which" << endl; - cout << " selects fastest supported), valid values are:" << endl; - vector simdTypes = DotSIMDEnum::getAllEnums(); - for (vector::iterator iter = simdTypes.begin(); - iter != simdTypes.end(); - iter++) { - cout << " " << DotSIMDEnum::toName(*iter) << endl; - } cout << endl; cout << "To get the help information of a processing subcommand, run it without any" << endl; cout << " additional arguments." << endl; @@ -948,7 +1004,7 @@ void CommandOperationManager::printArgumentsHelp(const AString& programName) cout << " [-fixnan] - replace NaN results with a value" << endl; cout << " - value to replace NaN with" << endl; cout << endl;//guide for wrap, assuming 80 columns: | - cout << " [-var] (repeatable) - repeatable - a volume file to use as a variable" << endl; + cout << " [-var] - repeatable - a volume file to use as a variable" << endl; cout << " - the name of the variable, as used in the expression" << endl; cout << " - the volume file to use as this variable" << endl; cout << endl;//guide for wrap, assuming 80 columns: | @@ -960,7 +1016,7 @@ void CommandOperationManager::printArgumentsHelp(const AString& programName) cout << endl;//guide for wrap, assuming 80 columns: | cout << " '' and '' denote mandatory parameters. '[-fixnan]'" << endl; cout << " denotes an option taking one mandatory parameter '', and" << endl; - cout << " '[-var] (repeatable)' denotes a repeatable option with mandatory parameters" << endl; + cout << " '[-var] - repeatable' denotes a repeatable option with mandatory parameters" << endl; cout << " '' and '', and two suboptions: '[-subvolume]', which has a" << endl; cout << " mandatory parameter '', and '[-repeat]', which takes no parameters." << endl; cout << " Commands also provide additional help info below the section in the example." << endl; @@ -991,17 +1047,71 @@ void CommandOperationManager::printArgumentsHelp(const AString& programName) cout << endl; } -void CommandOperationManager::printCiftiHelp(const AString& /*programName*/) +void CommandOperationManager::printGlobalOptions() +{ + cout << "Global options (can be added to any command):" << endl; + cout << endl; + //guide for wrap, assuming 80 columns: | + cout << " -disable-provenance don't generate provenance info in output" << endl; + cout << " files" << endl; + cout << endl; + cout << " -cifti-output-datatype write cifti output with the given" << endl; + cout << " datatype (default FLOAT32), note that" << endl; + cout << " calculation precision is only float32," << endl; + cout << " valid values are:" << endl; + cout << " INT8" << endl; + cout << " UINT8" << endl; + cout << " INT16" << endl; + cout << " UINT16" << endl; + cout << " INT32" << endl; + cout << " UINT32" << endl; + cout << " INT64" << endl; + cout << " UINT64" << endl; + cout << " FLOAT32" << endl; + cout << " FLOAT64" << endl; + cout << " FLOAT128" << endl; + cout << endl; + //guide for wrap, assuming 80 columns: | + cout << " -cifti-output-range write cifti output with scaling and offset" << endl; + cout << " header fields such that and " << endl; + cout << " are the most extreme values that can be" << endl; + cout << " represented, mostly useful with integer" << endl; + cout << " output datatypes (see above)" << endl; + cout << endl; + cout << " -logging set the logging level, valid values are:" << endl; + vector logLevels; + LogLevelEnum::getAllEnums(logLevels); + for (vector::iterator iter = logLevels.begin(); + iter != logLevels.end(); + iter++) { + cout << " " << LogLevelEnum::toName(*iter) << endl; + } + cout << endl;//add a line after the logging types for readability + //guide for wrap, assuming 80 columns: | + cout << " -simd set the SIMD implementation to use" << endl; + cout << " (currently used only for correlation," << endl; + cout << " default AUTO which selects fastest" << endl; + cout << " supported), valid values are:" << endl; + vector simdTypes = DotSIMDEnum::getAllEnums(); + for (vector::iterator iter = simdTypes.begin(); + iter != simdTypes.end(); + iter++) { + cout << " " << DotSIMDEnum::toName(*iter) << endl; + } + cout << endl; +} + +void CommandOperationManager::printCiftiHelp() { //guide for wrap, assuming 80 columns: | cout << " The CIFTI format is a new data file format intended to make it easier to" << endl; cout << " work with data from multiple disjoint structures at the same time - often" << endl; cout << " this means both hemispheres of cortex as surface data, and other structures" << endl; - cout << " as voxel data (amygdala, thalamus, hippocampus, etc). Additionally, it" << endl; - cout << " can exclude locations that are uninteresting for the task at hand (medial" << endl; - cout << " wall, white matter, csf), preventing them from taking up room in the data of" << endl; - cout << " the file. The set of structures and the locations in them that are used in" << endl; - cout << " a cifti file is referred to as 'brainordinates', or for the specific case of" << endl; + cout << " as voxel data (amygdala, thalamus, putamen, etc). Additionally, it can" << endl; + cout << " exclude locations that are uninteresting for the task at hand (medial wall," << endl; + cout << " white matter, csf), preventing them from taking up room in the data of the" << endl; + cout << " file. The set of structures and the locations in them that are used in a" << endl; + cout << " cifti file are referred to as 'brainordinates', or for the specific case of" << endl; cout << " 'all gray matter locations', 'grayordinates'." << endl; cout << endl;//guide for wrap, assuming 80 columns: | cout << " However, to explain the cifti format, it is easiest to work from the" << endl; @@ -1009,17 +1119,18 @@ void CommandOperationManager::printCiftiHelp(const AString& /*programName*/) cout << " single rectangular data matrix (usually 2 dimensions, but supports 3, and" << endl; cout << " may support more in the future), where each dimension is labeled with what" << endl; cout << " we call a 'mapping', each of which uses one of (currently) five possible" << endl; - cout << " 'mapping types'. It is these mapping types that give rise to the diverse" << endl; - cout << " types of cifti files. A single mapping of type 'brain models' (also known" << endl; - cout << " as 'dense') can represent both hemispheres and all subcortical structures" << endl; - cout << " simultaneously, meaning that only a single dimension is used to represent" << endl; - cout << " over a dozen structures, both surface-based and voxel-based. The mapping" << endl; - cout << " contains all information needed to figure out what every index along the" << endl; - cout << " dimension means. By putting a dense mapping along both dimensions in a 2D" << endl; - cout << " cifti file, you get a brainordinates by brainordinates matrix, frequently" << endl; - cout << " used for connectivity measures. Notably, even if two dimensions use the" << endl; - cout << " same mapping *type*, they can have different information in them, for" << endl; - cout << " example a connectivity matrix between two different parcellations." << endl; + cout << " 'mapping types'. It is the combinations of these mapping types that give" << endl; + cout << " rise to the diverse types of cifti files. A single mapping of type 'brain" << endl; + cout << " models' (also known as 'dense') can represent both hemispheres and all" << endl; + cout << " subcortical structures simultaneously, meaning that only a single matrix" << endl; + cout << " dimension is used to represent over a dozen structures, both surface-based" << endl; + cout << " and voxel-based. Each mapping contains all information needed to figure out" << endl; + cout << " what every index along the matrix dimension means. By putting a dense" << endl; + cout << " mapping along both dimensions in a 2D cifti file, you get a brainordinates" << endl; + cout << " by brainordinates matrix, which is used for connectivity measures. Notably," << endl; + cout << " even if two dimensions use the same mapping *type*, they can have different" << endl; + cout << " information in them, for example a connectivity matrix between two different" << endl; + cout << " parcellations." << endl; cout << endl;//guide for wrap, assuming 80 columns: | cout << " The other mapping types that currently may be used in a cifti file are:" << endl; cout << " Parcels: each index refers to a named subset of the brainordinates (i.e." << endl; @@ -1055,7 +1166,7 @@ void CommandOperationManager::printCiftiHelp(const AString& /*programName*/) cout << endl;//guide for wrap, assuming 80 columns: | } -void CommandOperationManager::printGiftiHelp(const AString& /*programName*/) +void CommandOperationManager::printGiftiHelp() { //guide for wrap, assuming 80 columns: | cout << " The GIFTI format is an established data file format intended for use with" << endl; @@ -1079,6 +1190,32 @@ void CommandOperationManager::printGiftiHelp(const AString& /*programName*/) cout << endl;//guide for wrap, assuming 80 columns: | } +void CommandOperationManager::printParallelHelp(const AString& programName) +{ + //guide for wrap, assuming 80 columns: | + cout << " Many processing commands make use of multithreading so that scripts can" << endl; + cout << " finish more quickly. By default, these commands will use all available" << endl; + cout << " cores on the system. Because we use OpenMP for the parallelization, this" << endl; + cout << " behavior can be controlled through various environment variables. In" << endl; + cout << " particular, 'OMP_NUM_THREADS' will set the maximum number of threads it will" << endl; + cout << " use, when exported as an environment variable. Additionally, some shells," << endl; + cout << " such as bash, have syntax that allows you to set an environment variable for" << endl; + cout << " a single command or script, for instance:" << endl; + cout << endl;//guide for wrap, assuming 80 columns: | + cout << "$ OMP_NUM_THREADS=4 "<< programName << " -volume-smoothing input.nii.gz 4 output.nii.gz" << endl; + cout << endl;//guide for wrap, assuming 80 columns: | + cout << " If you have a multi-socket system, be aware that the parallelization can be" << endl; + cout << " much slower when threads are on different sockets, and this interacts badly" << endl; + cout << " with the default behavior of using all available cores. It is advisable to" << endl; + cout << " use other tools to restrict the entire script to execute on a single socket," << endl; + cout << " especially if a queueing system is involved." << endl; + cout << endl;//guide for wrap, assuming 80 columns: | + cout << " Also note that wb_view contains a few features that use multithreading" << endl; + cout << " (dynamic connectivity, border optimize), which can be controlled by setting" << endl; + cout << " the same environment variables before launching wb_view." << endl; + cout << endl;//guide for wrap, assuming 80 columns: | +} + void CommandOperationManager::printVersionInfo() { ApplicationInformation myInfo; diff --git a/src/Commands/CommandOperationManager.h b/src/Commands/CommandOperationManager.h index d6588294a7fd9fc3bbed25ab1f761d1589680a4d..620e474650e9205c9c9f5ace157dca305a59f93a 100644 --- a/src/Commands/CommandOperationManager.h +++ b/src/Commands/CommandOperationManager.h @@ -67,9 +67,13 @@ namespace caret { void printArgumentsHelp(const AString& programName); - void printCiftiHelp(const AString& programName); + void printGlobalOptions(); - void printGiftiHelp(const AString& programName); + void printCiftiHelp(); + + void printGiftiHelp(); + + void printParallelHelp(const AString& programName); void printVersionInfo(); diff --git a/src/Commands/CommandParser.cxx b/src/Commands/CommandParser.cxx index 36efddabbdecfe5507b6a2c40cbedff24723b678..2f0c4226a0e79b3d575589e5812a2322fe28570a 100644 --- a/src/Commands/CommandParser.cxx +++ b/src/Commands/CommandParser.cxx @@ -55,6 +55,10 @@ CommandParser::CommandParser(AutoOperationInterface* myAutoOper) : OperationParserInterface(myAutoOper) { m_doProvenance = true; + m_ciftiScale = false; + m_ciftiDType = NIFTI_TYPE_FLOAT32; + m_ciftiMax = -1.0;//these values won't get used, but don't leave them uninitialized + m_ciftiMin = -1.0; } void CommandParser::disableProvenance() @@ -62,6 +66,22 @@ void CommandParser::disableProvenance() m_doProvenance = false; } +void CommandParser::setCiftiOutputDTypeAndScale(const int16_t& dtype, const double& minVal, const double& maxVal) +{ + m_ciftiDType = dtype; + m_ciftiMin = minVal; + m_ciftiMax = maxVal; + m_ciftiScale = true; +} + +void CommandParser::setCiftiOutputDTypeNoScale(const int16_t& dtype) +{ + m_ciftiDType = dtype; + m_ciftiMax = -1.0;//for sanity, don't keep any previous value around + m_ciftiMin = -1.0; + m_ciftiScale = false; +} + void CommandParser::executeOperation(ProgramParameters& parameters) { CaretPointer myAlgParams(m_autoOper->getParameters());//could be an autopointer, but this is safer @@ -103,7 +123,7 @@ void CommandParser::parseComponent(ParameterComponent* myComponent, ProgramParam for (int i = 0; i < (int)myComponent->m_paramList.size(); ++i) { bool hyphenReplaced = false; - //TSC: until someone complains, I say non-unicode dashes don't belong on the command line, EVER + //TSC: until someone complains, I say non-ascii dashes don't belong on the command line, EVER AString rawArg = parameters.nextString(myComponent->m_paramList[i]->m_shortName); AString nextArg = rawArg.fixUnicodeHyphens(&hyphenReplaced); if (hyphenReplaced) @@ -384,7 +404,7 @@ void CommandParser::parseComponent(ParameterComponent* myComponent, ProgramParam for (int i = 0; i < (int)myComponent->m_outputList.size(); ++i) {//parse the output options of this component bool hyphenReplaced = false; - //TSC: until someone complains, I say non-unicode dashes don't belong on the command line, EVER + //TSC: until someone complains, I say non-ascii dashes don't belong on the command line, EVER AString rawArg = parameters.nextString(myComponent->m_outputList[i]->m_shortName); AString nextArg = rawArg.fixUnicodeHyphens(&hyphenReplaced); if (hyphenReplaced) @@ -510,7 +530,7 @@ void CommandParser::parseRemainingOptions(ParameterComponent* myComponent, Progr while (parameters.hasNext()) { bool hyphenReplaced = false; - //TSC: until someone complains, I say non-unicode dashes don't belong on the command line, EVER + //TSC: until someone complains, I say non-ascii dashes don't belong on the command line, EVER AString rawArg = parameters.nextString("option"); AString nextArg = rawArg.fixUnicodeHyphens(&hyphenReplaced); if (hyphenReplaced) @@ -553,7 +573,7 @@ CommandParser::CompletionInfo CommandParser::completionComponent(ParameterCompon // if parameters remain after the option is completed, restart the iteration in order to be able take another option immediately if (parameters.hasNext()) { - //TSC: until someone complains, I say non-unicode dashes don't belong on the command line, EVER + //TSC: until someone complains, I say non-ascii dashes don't belong on the command line, EVER AString rawArg = parameters.nextString(myComponent->m_paramList[i]->m_shortName); AString nextArg = rawArg.fixUnicodeHyphens(); if (!nextArg.isEmpty() && nextArg[0] == '-') @@ -665,8 +685,8 @@ CommandParser::CompletionInfo CommandParser::completionComponent(ParameterCompon {//parse the output options of this component if (parameters.hasNext()) { - //TSC: until someone complains, I say non-unicode dashes don't belong on the command line, EVER - AString rawArg = parameters.nextString(myComponent->m_paramList[i]->m_shortName); + //TSC: until someone complains, I say non-ascii dashes don't belong on the command line, EVER + AString rawArg = parameters.nextString(myComponent->m_outputList[i]->m_shortName); AString nextArg = rawArg.fixUnicodeHyphens(); if (!nextArg.isEmpty() && nextArg[0] == '-') { @@ -785,7 +805,7 @@ CommandParser::CompletionInfo CommandParser::completionRemainingOptions(Paramete CompletionInfo prev; while (parameters.hasNext()) { - //TSC: until someone complains, I say non-unicode dashes don't belong on the command line, EVER + //TSC: until someone complains, I say non-ascii dashes don't belong on the command line, EVER AString rawArg = parameters.nextString("option"); AString nextArg = rawArg.fixUnicodeHyphens(); if (!nextArg.isEmpty() && nextArg[0] == '-') @@ -946,6 +966,12 @@ void CommandParser::makeOnDiskOutputs(const vector& outAssociation) myCiftiParam->m_parameter.grabNew(new CiftiFile()); myCiftiParam->m_parameter->setWritingFile(outAssociation[i].m_fileName); } + if (m_ciftiScale) + { + myCiftiParam->m_parameter->setWritingDataTypeAndScaling(m_ciftiDType, m_ciftiMin, m_ciftiMax); + } else { + myCiftiParam->m_parameter->setWritingDataTypeNoScaling(m_ciftiDType); + } break; } default: @@ -974,6 +1000,7 @@ void CommandParser::writeOutput(const vector& outAssociation) { CiftiFile* myFile = ((CiftiParameter*)myParam)->m_parameter;//we can't set metadata here because the XML is already on disk, see provenanceForOnDiskOutputs myFile->writeFile(outAssociation[i].m_fileName);//this is basically a noop unless outputs and inputs collide, we opened ON_DISK and set cache file to this name back in makeOnDiskOutputs + myFile->close();//if there is a problem flushing the file, let it throw here instead of doing a severe log message break; } case OperationParametersEnum::DOUBLE: diff --git a/src/Commands/CommandParser.h b/src/Commands/CommandParser.h index 0a46d7d6394a3f66b1024ffeceb380529f0a230d..9924a9c54a36d3e5c1415311d93b1d9887e7d623 100644 --- a/src/Commands/CommandParser.h +++ b/src/Commands/CommandParser.h @@ -27,6 +27,7 @@ #include "ProgramParameters.h" #include "CommandException.h" #include "ProgramParametersException.h" + #include #include @@ -36,7 +37,9 @@ namespace caret { { int m_minIndent, m_maxIndent, m_indentIncrement, m_maxWidth; AString m_provenance, m_parentProvenance, m_workingDir; - bool m_doProvenance; + bool m_doProvenance, m_ciftiScale; + double m_ciftiMin, m_ciftiMax; + int16_t m_ciftiDType; const static AString PROVENANCE_NAME, PARENT_PROVENANCE_NAME, PROGRAM_PROVENANCE_NAME, CWD_PROVENANCE_NAME;//TODO: put this elsewhere? std::map m_inputCiftiNames; struct OutputAssoc @@ -55,7 +58,7 @@ namespace caret { void parseRemainingOptions(ParameterComponent* myAlgParams, ProgramParameters& parameters, std::vector& outAssociation, bool debug); void provenanceBeforeOperation(const std::vector& outAssociation); void provenanceAfterOperation(const std::vector& outAssociation); - void makeOnDiskOutputs(const std::vector& outAssociation);//ensures on-disk inputs aren't used as on-disk outputs, converting outputs to in-memory when needed + void makeOnDiskOutputs(const std::vector& outAssociation);//ensures on-disk inputs aren't used as on-disk outputs, keeping outputs in-memory when needed void writeOutput(const std::vector& outAssociation); AString getIndentString(int desired); void addHelpComponent(AString& info, ParameterComponent* myComponent, int curIndent); @@ -71,6 +74,8 @@ namespace caret { public: CommandParser(AutoOperationInterface* myAutoOper); void disableProvenance(); + void setCiftiOutputDTypeAndScale(const int16_t& dtype, const double& minVal, const double& maxVal); + void setCiftiOutputDTypeNoScale(const int16_t& dtype); void executeOperation(ProgramParameters& parameters); void showParsedOperation(ProgramParameters& parameters); AString doCompletion(ProgramParameters& parameters, const bool& useExtGlob); diff --git a/src/Common/AString.cxx b/src/Common/AString.cxx index e993255b0579f908503acd47f166d68c021bb560..c97ea0bc8fbdf549f2baed9681a0eafe207f8914 100644 --- a/src/Common/AString.cxx +++ b/src/Common/AString.cxx @@ -21,6 +21,7 @@ #include #include "AString.h" +#include "CaretAssert.h" #include "CaretLogger.h" #include @@ -714,7 +715,7 @@ AString::replaceHtmlSpecialCharactersWithEscapeCharacters() const const int64_t length = this->count(); for (int64_t i = 0; i < length; i++) { const QChar ch = this->at(i); - switch (ch.toAscii()) { + switch (ch.toLatin1()) { case '&': htmlString.append("&"); break; @@ -913,4 +914,158 @@ AString::countMatchingCharactersFromEnd(const AString& rhs) const return matchCount; } +/** + * Find the longest common prefix in the given vector of strings. + * + * @param v + * A vector of strings + * @return + * Longest common prefix found (may be an empty string). + */ +AString +AString::findLongestCommonPrefix(const std::vector& stringVector) +{ + AString longestPrefix; + + const int32_t numStrings = static_cast(stringVector.size()); + if (numStrings == 1) { + CaretAssertVectorIndex(stringVector, 0); + longestPrefix = stringVector[0]; + } + else if (numStrings > 1) { + /* + * Find string with shortest name + */ + CaretAssertVectorIndex(stringVector, 0); + AString shortestString = stringVector[0]; + for (const auto name : stringVector) { + if (name.length() < shortestString.length()) { + shortestString = name; + } + } + + /* + * Loop through each character in the shortest string + * until a character does not match in any strings + */ + const int32_t numChars = static_cast(shortestString.length()); + for (int32_t ich = 0; ich < numChars; ich++) { + const QChar character = shortestString.at(ich); + + bool allMatchFlag = true; + for (const auto name : stringVector) { + CaretAssert(ich < name.length()); + if (name.at(ich) != character) { + allMatchFlag = false; + } + } + + if (allMatchFlag) { + longestPrefix.append(character); + } + else { + break; + } + } + } + + /* + * If the last character is a '/', remove it + */ + if (longestPrefix.length() > 1) { + if (longestPrefix.endsWith('/')) { + longestPrefix.resize(longestPrefix.length() - 1); + } + } + + const bool debugFlag = false; + if (debugFlag) { + std::cout << "Longest prefix: " << longestPrefix << std::endl; + for (const auto name : stringVector) { + std::cout << " " << name << std::endl; + } + } + + return longestPrefix; +} + +/** + * Convert a QStringList to an std::vector of AStrings. + * + * @param stringList + * The QStringList + * @return + * Vector with elements copied from the string list. + */ +std::vector +AString::stringListToVector(const QStringList& stringList) +{ + std::vector sv(stringList.begin(), + stringList.end()); + return sv; +} + +/** + * Join the elements from the string vector adding 'separator' + * between each of the elements. + * + * @param elements + * Elements joined to form a string. + * @param separtor + * Characters added between each pair of elements. + */ +AString +AString::join(const std::vector& elements, + const AString& separator) +{ + AString joinedString; + + const int32_t numElements = static_cast(elements.size()); + for (int32_t i = 0; i < numElements; i++) { + if (i > 0) { + joinedString.append(separator); + } + CaretAssertVectorIndex(elements, i); + joinedString.append(elements[i]); + } + + return joinedString; +} + +/** + * Count the number of corresponding matching elements from the two vectors. + * Compares first element in each, second element in each, etc and stops + * when the elements at the same index are different or no more + * elements in one of the vectors. + * + * @param v1 + * The first vector. + * @param v2 + * The second vector. + * @return + * Number of matching elements. + */ +int32_t +AString::matchingCount(const std::vector& v1, + const std::vector& v2) +{ + const int32_t count = std::min(v1.size(), + v2.size()); + if (count <= 0) { + return 0; + } + + int32_t matchCount = count; + + for (int32_t i = 0; i < count; i++) { + CaretAssertVectorIndex(v1, i); + CaretAssertVectorIndex(v2, i); + if (v1[i] != v2[i]) { + matchCount = i; + break; + } + } + + return matchCount; +} diff --git a/src/Common/AString.h b/src/Common/AString.h index 667259ea25d4d557c481dacac2c9381f1713a870..f7fdd49d2d66c367b01851b582bf093765366e35 100644 --- a/src/Common/AString.h +++ b/src/Common/AString.h @@ -56,7 +56,7 @@ namespace caret { operator std::string () {return this->toStdString(); } //char * compatibility - operator const char* () {return this->toAscii(); } + operator const char* () {return this->toLatin1(); } //double compatiblity operator double () {return this->toDouble(); } @@ -131,6 +131,17 @@ namespace caret { AString replaceHtmlSpecialCharactersWithEscapeCharacters() const; AString fixUnicodeHyphens(bool* hyphenReplaced = NULL, bool* hadOtherNonAscii = NULL, const bool& quiet = false) const; + + static AString findLongestCommonPrefix(const std::vector& v); + + static std::vector stringListToVector(const QStringList& stringList); + + static AString join(const std::vector& elements, + const AString& separator); + + static int32_t matchingCount(const std::vector& v1, + const std::vector& v2); + }; } diff --git a/src/Common/ApplicationInformation.cxx.in b/src/Common/ApplicationInformation.cxx.in index 294721872a4bfe81dfbf99d1871b42238e55a543..9f3d9de36ccfadb9df1e2db21f7d3910a8eb26ea 100644 --- a/src/Common/ApplicationInformation.cxx.in +++ b/src/Common/ApplicationInformation.cxx.in @@ -45,8 +45,24 @@ using namespace caret; ApplicationInformation::ApplicationInformation() : CaretObject() { - this->name = "Connectome Workbench"; - this->version = "1.2.3"; + this->workbenchSpecialVersion = WorkbenchSpecialVersionEnum::WORKBENCH_SPECIAL_VERSION_NO; + //this->workbenchSpecialVersion = WorkbenchSpecialVersionEnum::WORKBENCH_SPECIAL_VERSION_FIRST_CHARTING; + + this->name = "Connectome Workbench"; + this->nameForGuiLabel = this->name; + switch (this->workbenchSpecialVersion) { + case WorkbenchSpecialVersionEnum::WORKBENCH_SPECIAL_VERSION_NO: + /* use default naming */ + break; + case WorkbenchSpecialVersionEnum::WORKBENCH_SPECIAL_VERSION_FIRST_CHARTING: + this->name = "Connectome Workbench (First Charting Version)"; + this->nameForGuiLabel = ("" + "Connectome Workbench
" + "(First Charting Version)" + ""); + break; + } + this->version = "1.3.0"; this->commit = "Commit: @COMMIT@"; this->commitDate = "Commit Date: @COMMIT_DATE@"; @@ -94,6 +110,16 @@ ApplicationInformation::getName() const return this->name; } +/** + * @return Name of the application formatted + * specifically for use in a GUI QLabel. + */ +AString +ApplicationInformation::getNameForGuiLabel() const +{ + return this->nameForGuiLabel; +} + /** * @return Version of application. */ @@ -195,6 +221,18 @@ ApplicationInformation::getSummaryInformationInString(const AString& separator) return infoOut; } +/** + * @return The Workbench Special Version + * + * Used for versions of Workbench that may or may + * not be released and contains special functionality. + */ +WorkbenchSpecialVersionEnum::Enum +ApplicationInformation::getWorkbenchSpecialVersion() const +{ + return this->workbenchSpecialVersion; +} + /** * @return The type of application. */ diff --git a/src/Common/ApplicationInformation.h b/src/Common/ApplicationInformation.h index 62eb4d605a16e04955702bced8b66a1f7afb6bf0..93a1e72b3f07cafb2549f11475ad5fe24ccbc4b8 100644 --- a/src/Common/ApplicationInformation.h +++ b/src/Common/ApplicationInformation.h @@ -23,6 +23,7 @@ #include "ApplicationTypeEnum.h" #include "CaretObject.h" +#include "WorkbenchSpecialVersionEnum.h" namespace caret { @@ -35,6 +36,8 @@ namespace caret { AString getName() const; + AString getNameForGuiLabel() const; + AString getVersion() const; AString getCommit() const; @@ -47,6 +50,8 @@ namespace caret { AString getCompiledWithDebugStatus() const; + WorkbenchSpecialVersionEnum::Enum getWorkbenchSpecialVersion() const; + static ApplicationTypeEnum::Enum getApplicationType(); static void setApplicationType(const ApplicationTypeEnum::Enum applicationType); @@ -58,6 +63,8 @@ namespace caret { AString name; + AString nameForGuiLabel; + AString version; AString commit; @@ -70,6 +77,8 @@ namespace caret { AString compiledWithOpenMP; + WorkbenchSpecialVersionEnum::Enum workbenchSpecialVersion; + static ApplicationTypeEnum::Enum s_applicationType; }; diff --git a/src/Common/BackgroundAndForegroundColors.cxx b/src/Common/BackgroundAndForegroundColors.cxx index bc01cda5d2d4e4efa9af8c34b00c22587ea23c7f..062196b0011b8f7d297d11a7ef734a9c3883a3d1 100644 --- a/src/Common/BackgroundAndForegroundColors.cxx +++ b/src/Common/BackgroundAndForegroundColors.cxx @@ -87,15 +87,16 @@ void BackgroundAndForegroundColors::copyHelperBackgroundAndForegroundColors(const BackgroundAndForegroundColors& obj) { for (int32_t i = 0; i < 3; i++) { - m_colorForegroundAll[i] = obj.m_colorForegroundAll[i]; - m_colorBackgroundAll[i] = obj.m_colorBackgroundAll[i]; - m_colorForegroundChart[i] = obj.m_colorForegroundChart[i]; - m_colorBackgroundChart[i] = obj.m_colorBackgroundChart[i]; - m_colorForegroundSurface[i] = obj.m_colorForegroundSurface[i]; - m_colorBackgroundSurface[i] = obj.m_colorBackgroundSurface[i]; - m_colorForegroundVolume[i] = obj.m_colorForegroundVolume[i]; - m_colorBackgroundVolume[i] = obj.m_colorBackgroundVolume[i]; - m_colorChartMatrixGridLines[i] = obj.m_colorChartMatrixGridLines[i]; + m_colorForegroundAll[i] = obj.m_colorForegroundAll[i]; + m_colorBackgroundAll[i] = obj.m_colorBackgroundAll[i]; + m_colorForegroundChart[i] = obj.m_colorForegroundChart[i]; + m_colorBackgroundChart[i] = obj.m_colorBackgroundChart[i]; + m_colorForegroundSurface[i] = obj.m_colorForegroundSurface[i]; + m_colorBackgroundSurface[i] = obj.m_colorBackgroundSurface[i]; + m_colorForegroundVolume[i] = obj.m_colorForegroundVolume[i]; + m_colorBackgroundVolume[i] = obj.m_colorBackgroundVolume[i]; + m_colorChartMatrixGridLines[i] = obj.m_colorChartMatrixGridLines[i]; + m_colorChartHistogramThreshold[i] = obj.m_colorChartHistogramThreshold[i]; } } @@ -133,6 +134,11 @@ BackgroundAndForegroundColors::reset() const uint8_t chartGreen = 255; const uint8_t chartBlue = 255; setColor(m_colorChartMatrixGridLines, chartRed, chartGreen, chartBlue); + + const uint8_t threshRed = 100; + const uint8_t threshGreen = 100; + const uint8_t threshBlue = 255; + setColor(m_colorChartHistogramThreshold, threshRed, threshGreen, threshBlue); } /** @@ -389,6 +395,35 @@ BackgroundAndForegroundColors::setColorChartMatrixGridLines(const uint8_t colorC } } +/** + * Get the color for chart histogram threshold + * + * @param colorChartHistogramThreshold + * RGB color components ranging [0, 255]. + */ +void +BackgroundAndForegroundColors::getColorChartHistogramThreshold(uint8_t colorChartHistogramThreshold[3]) const +{ + for (int32_t i = 0; i < 3; i++) { + colorChartHistogramThreshold[i] = m_colorChartHistogramThreshold[i]; + } +} + +/** + * Set the color for chart histogram threshold + * + * @param colorChartHistogramThreshold + * RGB color components ranging [0, 255]. + */ +void +BackgroundAndForegroundColors::setColorChartHistogramThreshold(const uint8_t colorChartHistogramThreshold[3]) +{ + for (int32_t i = 0; i < 3; i++) { + m_colorChartHistogramThreshold[i] = colorChartHistogramThreshold[i]; + } +} + + /** * Set a color with the given color components that range 0 to 255. * diff --git a/src/Common/BackgroundAndForegroundColors.h b/src/Common/BackgroundAndForegroundColors.h index 35b00acf992f56e60513829705c5820f9e157ced..a772d6ad22e71fd304cf3e4c02c6d31c005165bf 100644 --- a/src/Common/BackgroundAndForegroundColors.h +++ b/src/Common/BackgroundAndForegroundColors.h @@ -81,6 +81,10 @@ namespace caret { void setColorChartMatrixGridLines(const uint8_t colorChartMatrixGridLines[3]); + void getColorChartHistogramThreshold(uint8_t colorChartMatrixGridLines[3]) const; + + void setColorChartHistogramThreshold(const uint8_t colorChartMatrixGridLines[3]); + private: void copyHelperBackgroundAndForegroundColors(const BackgroundAndForegroundColors& obj); @@ -107,6 +111,8 @@ namespace caret { uint8_t m_colorChartMatrixGridLines[3]; + uint8_t m_colorChartHistogramThreshold[3]; + // ADD_NEW_MEMBERS_HERE friend class BackgroundAndForegroundColorsSceneHelper; diff --git a/src/Common/CMakeLists.txt b/src/Common/CMakeLists.txt index b6949ed8875fe7a160f246a9ebf794451fe2fbdd..2f344da6006f3cf6bfb86092c014425a87a95877 100644 --- a/src/Common/CMakeLists.txt +++ b/src/Common/CMakeLists.txt @@ -12,14 +12,35 @@ SET(QT_USE_QTXML TRUE) SET(QT_DONT_USE_QTGUI TRUE) SET(QT_USE_QTNETWORK TRUE) -INCLUDE(${QT_USE_FILE}) +if(Qt5_FOUND) + include_directories(${Qt5Core_INCLUDE_DIRS}) + include_directories(${Qt5Network_INCLUDE_DIRS}) + include_directories(${Qt5Xml_INCLUDE_DIRS}) +endif() +IF (QT4_FOUND) + INCLUDE(${QT_USE_FILE}) +ENDIF () #message("QT VERSION: " ${QT_VERSION_MAJOR} ${QT_VERSION_MINOR} ${QT_VERSION_PATCH}) -SET(MOC_INPUT_HEADER_FILES -CaretHttpManager.h -) +# +# With AUTOMOC, do not need to specify files +# that contain Q_OBJECT macro for Qt to process with 'moc' +# (meta-object compiler). +# +IF(WORKBENCH_USE_CMAKE_AUTOMOC) + SET(CMAKE_AUTOMOC ON) +ELSE() + SET(MOC_INPUT_HEADER_FILES + CaretHttpManager.h + ) -QT4_WRAP_CPP(MOC_SOURCE_FILES ${MOC_INPUT_HEADER_FILES}) + IF(Qt5_FOUND) + QT5_WRAP_CPP(MOC_SOURCE_FILES ${MOC_INPUT_HEADER_FILES}) + ENDIF() + IF (QT4_FOUND) + QT4_WRAP_CPP(MOC_SOURCE_FILES ${MOC_INPUT_HEADER_FILES}) + ENDIF () +ENDIF() # # Create a library @@ -47,7 +68,6 @@ CaretException.h CaretFunctionName.h CaretHeap.h CaretHttpManager.h -CaretJsonObject.h CaretLogger.h CaretMathExpression.h CaretMutex.h @@ -60,6 +80,7 @@ CaretPreferences.h CaretTemporaryFile.h CaretUndoCommand.h CaretUndoStack.h +CaretUnitsTypeEnum.h CubicSpline.h DataCompressZLib.h DataFile.h @@ -73,18 +94,17 @@ DescriptiveStatistics.h DeveloperFlagsEnum.h DisplayGroupAndTabItemInterface.h DisplayGroupEnum.h -DrawnWithOpenGLTextureInfo.h -DrawnWithOpenGLTextureInterface.h ElapsedTimer.h Event.h EventAlertUser.h +EventBrowserTabDelete.h EventBrowserTabIndicesGetAll.h +EventCaretPreferencesGet.h EventGetViewportSize.h EventListenerInterface.h EventManager.h EventPaletteGetByName.h EventProgressUpdate.h -EventOpenGLTexture.h EventTypeEnum.h FastStatistics.h FileAdapter.h @@ -93,6 +113,7 @@ FloatMatrix.h Histogram.h HtmlStringBuilder.h ImageCaptureMethodEnum.h +JsonHelper.h Logger.h LogHandler.h LogHandlerStandardError.h @@ -130,9 +151,11 @@ TriStateSelectionStatusEnum.h Vector3D.h VectorOperation.h VoxelIJK.h +WorkbenchSpecialVersionEnum.h YokingGroupEnum.h ${MOC_SOURCE_FILES} + ${CMAKE_BINARY_DIR}/Common/ApplicationInformation.cxx ApplicationTypeEnum.cxx AString.cxx @@ -150,7 +173,6 @@ CaretColorEnum.cxx CaretCommandLine.cxx CaretException.cxx CaretHttpManager.cxx -CaretJsonObject.cxx CaretLogger.cxx CaretMathExpression.cxx CaretObject.cxx @@ -160,6 +182,7 @@ CaretPreferences.cxx CaretTemporaryFile.cxx CaretUndoCommand.cxx CaretUndoStack.cxx +CaretUnitsTypeEnum.cxx CubicSpline.cxx DataCompressZLib.cxx DataFile.cxx @@ -171,16 +194,17 @@ DescriptiveStatistics.cxx DeveloperFlagsEnum.cxx DisplayGroupAndTabItemInterface.cxx DisplayGroupEnum.cxx -DrawnWithOpenGLTextureInfo.cxx ElapsedTimer.cxx Event.cxx EventAlertUser.cxx +EventBrowserTabDelete.cxx EventBrowserTabIndicesGetAll.cxx +EventCaretPreferencesGet.cxx EventGetViewportSize.cxx +EventListenerInterface.cxx EventManager.cxx EventPaletteGetByName.cxx EventProgressUpdate.cxx -EventOpenGLTexture.cxx EventTypeEnum.cxx FastStatistics.cxx FileAdapter.cxx @@ -189,6 +213,7 @@ FloatMatrix.cxx Histogram.cxx HtmlStringBuilder.cxx ImageCaptureMethodEnum.cxx +JsonHelper.cxx Logger.cxx LogHandler.cxx LogHandlerStandardError.cxx @@ -219,6 +244,7 @@ TileTabsConfiguration.cxx TriStateSelectionStatusEnum.cxx Vector3D.cxx VectorOperation.cxx +WorkbenchSpecialVersionEnum.cxx YokingGroupEnum.cxx ) @@ -261,5 +287,8 @@ ENDIF(EXISTS ${GIT_REPOSITORY}) # Conditionally link the dot library to use the SIMD-based dot product implementation # IF (WORKBENCH_USE_SIMD AND CPUINFO_COMPILES) - TARGET_LINK_LIBRARIES(Common dot) -ENDIF() + TARGET_LINK_LIBRARIES(Common dot ${CARET_QT5_LINK}) +ELSE (WORKBENCH_USE_SIMD AND CPUINFO_COMPILES) + TARGET_LINK_LIBRARIES(Common ${CARET_QT5_LINK}) +ENDIF (WORKBENCH_USE_SIMD AND CPUINFO_COMPILES) + diff --git a/src/Common/CaretAssert.h b/src/Common/CaretAssert.h index d8a14a08903cdc9db9b06846382513c81a175854..6ad9c58cfbb2755bbbdfea48ec2942bab4b0d9a8 100644 --- a/src/Common/CaretAssert.h +++ b/src/Common/CaretAssert.h @@ -33,9 +33,51 @@ #define CaretAssertMessage(e, m) ((void)0) #define CaretAssertArrayIndex(a, n, i) ((void) 0) #define CaretAssertVectorIndex(v, i) ((void) 0) + #define CaretAssertStdArrayIndex(v, i) ((void) 0) + #define CaretUsedInDebugCompileOnly(e) ((void) 0) + #define CaretParameterUsedInDebugCompileOnly(e) #else // NDEBUG + + /** + * \def CaretUsedInDebugCompileOnly + * + * The code within the expression is compiled ONLY + * if the compiler's "debug" option is enabled. It + * can be used to eliminate "unused" compilation warnings + * when debug is off for variables that are only + * used in one of the CaretAssert() macros. + * + * Example: + * std::vector v; + * CaretUsedInDebugCompileOnly(const int index = 5 * x + y); + * CaretAssertVectorIndex(v, index); + * + * @param e + * Expression that is only present if compiled with debug on. + */ + #define CaretUsedInDebugCompileOnly(e) e + + /** + * \def CaretParameterUsedInDebugCompileOnly + * + * The code within the expression is compiled ONLY + * if the compiler's "debug" option is enabled. It + * is used when a function parameter is only used + * within a CaretAssert() macro. + * + * Example: + * void function(const float a, + * const int CaretParameterUsedInDebugCompileOnly(index)) { + * CaretAssertVectorIndex(v, index); + * } + * + * @param e + * Expression that is only present if compiled with debug on. + */ + #define CaretParameterUsedInDebugCompileOnly(e) e + /** * \def CaretAssert * @@ -137,6 +179,26 @@ ? caret::CaretAssertion::assertVectorIndexFailed(#v, (static_cast(v.size())), i, __FILE__, __LINE__) \ : (void)0) +/** + * \def CaretAssertStdArrayIndex + * + * If the std array index is out of bounds, a message is printed + * listing the std array name, the std array's number of elements, the + * invalid std array index, the name of the file, and the line + * number in the file. A call stack may also + * be printed. Lastly, abort() is called. + * @param a + * The vector. + * @param i + * The index into the vector. + */ +#define CaretAssertStdArrayIndex(a, i) \ +((((i) < 0) || ((i) >= (static_cast(a.size())))) \ +? caret::CaretAssertion::assertStdArrayIndexFailed(#a, (static_cast(a.size())), i, __FILE__, __LINE__) \ +: (void)0) + + + #endif // NDEBUG diff --git a/src/Common/CaretAssertion.cxx b/src/Common/CaretAssertion.cxx index defdf5319f5db0c07ec337bf8fda7f4634e640ee..9603ad0141fc6c9e43a757b4473e30439365d1b5 100644 --- a/src/Common/CaretAssertion.cxx +++ b/src/Common/CaretAssertion.cxx @@ -320,6 +320,65 @@ CaretAssertion::assertVectorIndexFailed(const char* vectorName, } } +/** + * Called when a std array index assertion has failed. + * The following events will occur: + * Prints the name of the array, the number of + * elements in the array, the invalid array index, + * the name of the file where the assertion failed, + * the line number in the file. If + * a callstack (backtrace) is available, it will + * also be printed. Lastly, abort() is called. + * + * + * @param arrayName + * Name of the array. + * @param arrayNumberOfElements + * Number of elements in the array. + * @param arrayIndex + * Invalid array index. + * @param filename + * Name of file in which assertion failed. + * @param lineNumber + * Line number where assertion failed. + */ +void +CaretAssertion::assertStdArrayIndexFailed(const char* arrayName, + const int64_t arrayNumberOfElements, + const int64_t arrayIndex, + const char* filename, + const int64_t lineNumber) +{ + std::cerr \ + << "Assertion of Std Array Bounds failed for array: " + << arrayName + << std::endl + << "File: " + << filename + << std::endl + << "Line number: " + << lineNumber + << std::endl; + + std::cerr + << "Index: " + << arrayIndex + << std::endl + << "Number of elements in array: " + << arrayNumberOfElements + << std::endl + << std::endl; + + const AString s = SystemUtilities::getBackTrace(); + if (s.isEmpty() == false) { + std::cerr << s << std::endl << std::endl; + } + + if (CaretAssertion::unitTestFlag == false) { + std::abort(); + } +} + /** * Unit testing of assertions. * diff --git a/src/Common/CaretAssertion.h b/src/Common/CaretAssertion.h index 4a1016dbb8acc268db84b3ca82ee8ddb51e73c3f..6cc278b12222c9b5ce55ff75daab1d50bf17591f 100644 --- a/src/Common/CaretAssertion.h +++ b/src/Common/CaretAssertion.h @@ -72,6 +72,12 @@ public: const char* filename, const int64_t lineNumber); + static void assertStdArrayIndexFailed(const char* arrayName, + const int64_t arrayNumberOfElements, + const int64_t arrayIndex, + const char* filename, + const int64_t lineNumber); + static void unitTest(std::ostream& stream, const bool isVerbose); diff --git a/src/Common/CaretBinaryFile.cxx b/src/Common/CaretBinaryFile.cxx index fdc8d6c2042343e881fa58a1f3340ec984cc1a21..d97b1fa829c9404d9c2cd47b8d030613da0187d5 100644 --- a/src/Common/CaretBinaryFile.cxx +++ b/src/Common/CaretBinaryFile.cxx @@ -52,6 +52,7 @@ namespace caret void close(); void seek(const int64_t& position); int64_t pos(); + int64_t size() { return -1; } void read(void* dataOut, const int64_t& count, int64_t* numRead); void write(const void* dataIn, const int64_t& count); ~ZFileImpl(); @@ -69,6 +70,7 @@ namespace caret void close(); void seek(const int64_t& position); int64_t pos(); + int64_t size() { return m_file.size(); } void read(void* dataOut, const int64_t& count, int64_t* numRead); void write(const void* dataIn, const int64_t& count); }; @@ -147,6 +149,12 @@ int64_t CaretBinaryFile::pos() return m_impl->pos(); } +int64_t CaretBinaryFile::size() +{ + if (m_curMode == NONE) throw DataFileException("file is not open, can't report size"); + return m_impl->size(); +} + void CaretBinaryFile::write(const void* dataIn, const int64_t& count) { CaretAssert(count >= 0);//not sure about allowing 0 @@ -296,9 +304,14 @@ void QFileImpl::open(const QString& filename, const CaretBinaryFile::OpenMode& o switch (m_file.error()) { case QFile::ResourceError://on linux at least, it never gives another code besides the unhelpful OpenError - throw DataFileException("failed to open file '" + filename + "', too many open files"); + throw DataFileException("failed to open file '" + filename + "', too many open files, try 'ulimit -n 4096'"); default: - throw DataFileException("failed to open file '" + filename + "'"); + if (opmode & CaretBinaryFile::WRITE) + { + throw DataFileException("failed to open file '" + filename + "' for writing"); + } else { + throw DataFileException("failed to open file '" + filename + "'"); + } } } } diff --git a/src/Common/CaretBinaryFile.h b/src/Common/CaretBinaryFile.h index b2396f33d1ba2d84b4b78737aa580522f2a88a03..db774dd6cbeee837f764638961f2a779063f56f9 100644 --- a/src/Common/CaretBinaryFile.h +++ b/src/Common/CaretBinaryFile.h @@ -55,6 +55,7 @@ namespace caret { int64_t pos(); void read(void* dataOut, const int64_t& count, int64_t* numRead = NULL);//throw if numRead is NULL and (error or end of file reached early) void write(const void* dataIn, const int64_t& count);//failure to complete write is always an exception + int64_t size();//may return -1 if size cannot be determined efficiently class ImplInterface { protected: @@ -65,6 +66,7 @@ namespace caret { const QString& getFilename() const { return m_fileName; } virtual void seek(const int64_t& position) = 0; virtual int64_t pos() = 0; + virtual int64_t size() = 0; virtual void read(void* dataOut, const int64_t& count, int64_t* numRead) = 0; virtual void write(const void* dataIn, const int64_t& count) = 0; virtual ~ImplInterface(); diff --git a/src/Common/CaretColorEnum.cxx b/src/Common/CaretColorEnum.cxx index ace88b77e4010ed64369d17b99c988f12193cc6e..49920402184c09675f356d63abfdc277b79a11b4 100644 --- a/src/Common/CaretColorEnum.cxx +++ b/src/Common/CaretColorEnum.cxx @@ -321,41 +321,43 @@ CaretColorEnum::toRGB(Enum enumValue) } /** - * Get the RGB components (ranging zero to one) of the enumerated type. + * Get the RGBA components (ranging zero to one) of the enumerated type. * @param enumValue * Enumerated value. - * @param rgbOut - * Output with RGB components [0.0, 1.0] + * @param rgbaOut + * Output with RGB components [0.0, 1.0] and Alpha=1.0 */ void -CaretColorEnum::toRGBFloat(Enum enumValue, - float rgbOut[3]) +CaretColorEnum::toRGBAFloat(Enum enumValue, + float rgbaOut[4]) { if (initializedFlag == false) initialize(); const CaretColorEnum* enumInstance = findData(enumValue); - rgbOut[0] = enumInstance->rgb[0]; - rgbOut[1] = enumInstance->rgb[1]; - rgbOut[2] = enumInstance->rgb[2]; + rgbaOut[0] = enumInstance->rgb[0]; + rgbaOut[1] = enumInstance->rgb[1]; + rgbaOut[2] = enumInstance->rgb[2]; + rgbaOut[3] = 1.0; } /** - * Get the RGB components (ranging zero to one) of the enumerated type. + * Get the RGBA components (ranging zero to one) of the enumerated type. * @param enumValue * Enumerated value. - * @param rgbOut - * Output with RGB components [0.0, 1.0] + * @param rgbaOut + * Output with RGB components [0, 255] and Alpha=255 */ void -CaretColorEnum::toRGBByte(Enum enumValue, - uint8_t rgbOut[3]) +CaretColorEnum::toRGBAByte(Enum enumValue, + uint8_t rgbaOut[4]) { - float rgbFloat[3]; - toRGBFloat(enumValue, rgbFloat); + float rgbaFloat[4]; + toRGBAFloat(enumValue, rgbaFloat); - rgbOut[0] = static_cast(rgbFloat[0] * 255.0); - rgbOut[1] = static_cast(rgbFloat[1] * 255.0); - rgbOut[2] = static_cast(rgbFloat[2] * 255.0); + rgbaOut[0] = static_cast(rgbaFloat[0] * 255.0); + rgbaOut[1] = static_cast(rgbaFloat[1] * 255.0); + rgbaOut[2] = static_cast(rgbaFloat[2] * 255.0); + rgbaOut[3] = 255; } @@ -484,6 +486,25 @@ CaretColorEnum::getColorEnums(std::vector& allColorEnums) OPTION_NO_OPTIONS); } +/** + * Get all of the enumerated type values THAT REPRESENT A COLOR except BLACK and WHITE. + * The values can be used as parameters to toXXX() methods to get associated metadata. + * + * @param allColorEnums + * A vector that is OUTPUT containing all of the VALID COLOR enumerated values. + * No optional enum values are included. + */ +void +CaretColorEnum::getColorEnumsNoBlackOrWhite(std::vector& allColorEnums) +{ + if (initializedFlag == false) initialize(); + + getColorAndOptionalEnums(allColorEnums, + OPTION_NO_OPTIONS); + std::remove(allColorEnums.begin(), allColorEnums.end(), CaretColorEnum::BLACK); + std::remove(allColorEnums.begin(), allColorEnums.end(), CaretColorEnum::WHITE); +} + /** * Get all of the enumerated type values THAT REPRESENT A COLOR and the specified * optional enums. The values can be used diff --git a/src/Common/CaretColorEnum.h b/src/Common/CaretColorEnum.h index c814de5f0d2c468a83855e07b08de7d67ad3b8f6..e9633ff7d8b3fe8b9daa4fbdb76b426874e1cf0a 100644 --- a/src/Common/CaretColorEnum.h +++ b/src/Common/CaretColorEnum.h @@ -97,6 +97,8 @@ public: static void getColorEnums(std::vector& allColorEnums); + static void getColorEnumsNoBlackOrWhite(std::vector& allColorEnums); + static void getColorAndOptionalEnums(std::vector& allEnums, const int64_t colorOptions); @@ -106,11 +108,11 @@ public: static const float* toRGB(Enum enumValue); - static void toRGBFloat(Enum enumValue, - float rgbOut[3]); + static void toRGBAFloat(Enum enumValue, + float rgbaOut[4]); - static void toRGBByte(Enum enumValue, - uint8_t rgbOut[3]); + static void toRGBAByte(Enum enumValue, + uint8_t rgbaOut[4]); private: CaretColorEnum(const Enum enumValue, diff --git a/src/Common/CaretHttpManager.cxx b/src/Common/CaretHttpManager.cxx index eca485db21bbe6e5eadfdd5c684b8ad0d5e1a343..593506b05b50c0eb473e9f21dcc777c3135309e6 100644 --- a/src/Common/CaretHttpManager.cxx +++ b/src/Common/CaretHttpManager.cxx @@ -24,6 +24,9 @@ #include "CaretLogger.h" #include "NetworkException.h" #include +#if QT_VERSION >= 0x050000 +#include +#endif // QT_VERSION using namespace caret; using namespace std; @@ -229,11 +232,23 @@ void CaretHttpManager::httpRequestPrivate(const CaretHttpRequest &request, Caret CaretLogInfo("NO AUTH FOUND for URL " + request.m_url); } QNetworkReply* myReply = NULL; +/* + * QUrl::addQueryItem() deprecated in Qt5: http://wiki.qt.io/Transition_from_Qt_4.x_to_Qt5 + */ QUrl myUrl = QUrl::fromUserInput(request.m_url); +#if QT_VERSION >= 0x050000 + QUrlQuery myUrlQuery(QUrl::fromUserInput(request.m_url)); + for (int32_t i = 0; i < (int32_t)request.m_queries.size(); ++i) + { + myUrlQuery.addQueryItem(request.m_queries[i].first, request.m_queries[i].second); + } + myUrl.setQuery(myUrlQuery); +#else // QT_VERSION for (int32_t i = 0; i < (int32_t)request.m_queries.size(); ++i) { myUrl.addQueryItem(request.m_queries[i].first, request.m_queries[i].second); } +#endif // QT_VERSION QNetworkAccessManager* myQNetMgr = &(myCaretMgr->m_netMgr); bool first = true; QByteArray postData; @@ -258,8 +273,11 @@ void CaretHttpManager::httpRequestPrivate(const CaretHttpRequest &request, Caret for (std::map::const_iterator headerIter = request.m_headers.begin(); headerIter != request.m_headers.end(); headerIter++) { - myRequest.setRawHeader(headerIter->first.toAscii(), headerIter->second.toAscii()); + myRequest.setRawHeader(headerIter->first.toLatin1(), headerIter->second.toLatin1()); } +#if QT_VERSION >= 0x050000 + //myUrl.setQuery(myUrlQuery); +#endif // QT_VERSION myRequest.setUrl(myUrl); myReply = myQNetMgr->post(myRequest, postData); CaretLogInfo("POST ARGUMENTS URL: " + myUrl.toString()); @@ -273,10 +291,13 @@ void CaretHttpManager::httpRequestPrivate(const CaretHttpRequest &request, Caret for (std::map::const_iterator headerIter = request.m_headers.begin(); headerIter != request.m_headers.end(); headerIter++) { - myRequest.setRawHeader(headerIter->first.toAscii(), headerIter->second.toAscii()); + myRequest.setRawHeader(headerIter->first.toLatin1(), headerIter->second.toLatin1()); // std::cout << "POST FILE header: " << qPrintable(headerIter->first) // << ": " << qPrintable(headerIter->second) << std::endl; } +#if QT_VERSION >= 0x050000 + myUrl.setQuery(myUrlQuery); +#endif // QT_VERSION myRequest.setUrl(myUrl); myReply = myQNetMgr->post(myRequest, postUploadFile); CaretLogInfo("POST FILE URL: " + myUrl.toString()); @@ -289,8 +310,15 @@ void CaretHttpManager::httpRequestPrivate(const CaretHttpRequest &request, Caret case GET: for (int32_t i = 0; i < (int32_t)request.m_arguments.size(); ++i) { +#if QT_VERSION >= 0x050000 + myUrlQuery.addQueryItem(request.m_arguments[i].first, request.m_arguments[i].second); +#else // QT_VERSION myUrl.addQueryItem(request.m_arguments[i].first, request.m_arguments[i].second); +#endif // QT_VERSION } +#if QT_VERSION >= 0x050000 + myUrl.setQuery(myUrlQuery); +#endif // QT_VERSION myRequest.setUrl(myUrl); CaretLogInfo("GET URL: " + myUrl.toString()); myReply = myQNetMgr->get(myRequest); @@ -298,8 +326,15 @@ void CaretHttpManager::httpRequestPrivate(const CaretHttpRequest &request, Caret case HEAD: for (int32_t i = 0; i < (int32_t)request.m_arguments.size(); ++i) { +#if QT_VERSION >= 0x050000 + myUrlQuery.addQueryItem(request.m_arguments[i].first, request.m_arguments[i].second); +#else // QT_VERSION myUrl.addQueryItem(request.m_arguments[i].first, request.m_arguments[i].second); +#endif // QT_VERSION } +#if QT_VERSION >= 0x050000 + myUrl.setQuery(myUrlQuery); +#endif // QT_VERSION myRequest.setUrl(myUrl); CaretLogInfo("HEAD URL: " + myUrl.toString()); myReply = myQNetMgr->head(myRequest); diff --git a/src/Common/CaretJsonObject.cxx b/src/Common/CaretJsonObject.cxx deleted file mode 100644 index a1a4c77df4d50faaa5867cf3b0b03cbf6b65b254..0000000000000000000000000000000000000000 --- a/src/Common/CaretJsonObject.cxx +++ /dev/null @@ -1,214 +0,0 @@ - -/*LICENSE_START*/ -/* - * Copyright (C) 2016 Washington University School of Medicine - * - * 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 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License along - * with this program; if not, write to the Free Software Foundation, Inc., - * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. - */ -/*LICENSE_END*/ - -#include - -#define __CARET_JSON_OBJECT_DECLARE__ -#include "CaretJsonObject.h" -#undef __CARET_JSON_OBJECT_DECLARE__ - -#include "CaretAssert.h" -#include "CaretLogger.h" -#include "MathFunctions.h" - -using namespace caret; - -static bool debugFlag = false; - -/** - * \class caret::CaretJsonObject - * \brief Supports parsing of simple json object consisting of key/value pairs until Qt 5 available. - * \ingroup Common - * - * Supports parsing of simple json object consisting of key/value pairs as - * returned by BALSA database. Some of API is based upon Qt5's QJsonObject. - */ - -/** - * Constructor that splits the given text into key/value pairs. - * - * Example: {"written":false,"fileName":"balsa_upload_to_balsa_dot_wustl_dot_edu-1.zip", - * "status":500, - * "statusText":"RB6l/balsa_upload_to_balsa_dot_wustl_dot_edu-1.zip (No such file or directory)"} - * - * @param jsonText - * Text containing a JSON object. - */ -CaretJsonObject::CaretJsonObject(const AString& jsonText) -: CaretObject() -{ - AString text(jsonText); - const int32_t firstBracket = text.indexOf("{"); - const int32_t lastBracket = text.lastIndexOf("}"); - - if ((firstBracket >= 0) - && (lastBracket > (firstBracket + 3))) { - const int32_t startIndex = firstBracket + 1; - const int32_t len = lastBracket - startIndex; - text = text.mid(startIndex, len); - if (debugFlag) std::cout << "Brackets removed: " << text << std::endl; - } - else { - CaretLogInfo("JSON missing opening and closing brackets."); - return; - } - - /* - * Iterate through string looking for commas. - * Note: Need to avoid commas in name value strings in double quotes) - */ - const int32_t textLen = text.length(); - std::vector commas; - int32_t doubleQuoteCounter = 0; - int32_t lastComma = -1; - for (int32_t i = 0; i < textLen; i++) { - const QChar ch = text[i]; - if (ch == ',') { - if ( ! MathFunctions::isOddNumber(doubleQuoteCounter)) { - const int32_t start = lastComma + 1; - const int32_t len = i - start; - const AString nameValuePair = text.mid(start, len); - if (debugFlag) std::cout << "Name/Value pair: " << nameValuePair << std::endl; - processNameValueString(nameValuePair); - lastComma = i; - } - } - else if (ch == '\"') { - doubleQuoteCounter++; - } - } - if ( ! MathFunctions::isOddNumber(doubleQuoteCounter)) { - const int32_t start = lastComma + 1; - const int32_t len = textLen - start; - if (len > 2) { - const AString nameValuePair = text.mid(start, len); - if (debugFlag) std::cout << "Name/Value pair: " << nameValuePair << std::endl; - processNameValueString(nameValuePair); - } - } -} - -/** - * Destructor. - */ -CaretJsonObject::~CaretJsonObject() -{ -} - -/** - * Process a name value string by splitting into name and value. - * - * @param nameValueString - * Name value string that is split, ie: "fileName":"balsa_upload_to_balsa_dot_wustl_dot_edu-1.zip" - */ -void -CaretJsonObject::processNameValueString(const AString& nameValueString) -{ - const int32_t colonIndex = nameValueString.indexOf(":"); - if (colonIndex > 0) { - AString name = nameValueString.left(colonIndex).trimmed(); - AString value = nameValueString.mid(colonIndex + 1).trimmed(); - - trimLeadingTrailingDoubleQuotes(name); - trimLeadingTrailingDoubleQuotes(value); - - m_data.insert(std::make_pair(name, value)); - - if (debugFlag) std::cout << " " << name << " -> " << value << std::endl; - } -} - -/** - * Trim the leading and trailing quotes from the given text. - * - * @param text - * In/Out text that has any leading and/or trailing quotes removed. - */ -void -CaretJsonObject::trimLeadingTrailingDoubleQuotes(AString& text) -{ - if (text.endsWith("\"")) { - text.resize(text.size() - 1); - } - if (text.startsWith("\"")) { - text = text.mid(1); - } -} - -/** - * @return Number of key/value pairs. - */ -int32_t -CaretJsonObject::count() const -{ - return m_data.size(); -} - -/** - * @return True if no key/value pairs. - */ -bool -CaretJsonObject::empty() const -{ - return m_data.empty(); -} - -/** - * @return The keys. - */ -std::vector -CaretJsonObject::keys() const -{ - std::vector keysOut; - - for (std::map::const_iterator iter = m_data.begin(); - iter != m_data.end(); - iter++) { - keysOut.push_back(iter->first); - } - - return keysOut; -} - -/** - * @return True if the key exists. - */ -bool -CaretJsonObject::hasKey(const AString& key) const -{ - const std::map::const_iterator iter = m_data.find(key); - return (iter != m_data.end()); -} - -/** - * @return Value for the given key. Empty string if key is missing. - */ -AString -CaretJsonObject::value(const AString& key) const -{ - const std::map::const_iterator iter = m_data.find(key); - if (iter != m_data.end()) { - return iter->second; - } - - return ""; -} - diff --git a/src/Common/CaretObject.cxx b/src/Common/CaretObject.cxx index 2082debed5a4eb68b43c87a6b7af7e5e89293682..292c9dfa588ad9f14614ac37fa01298105eeee5c 100644 --- a/src/Common/CaretObject.cxx +++ b/src/Common/CaretObject.cxx @@ -120,13 +120,18 @@ CaretObject::className() const return name; } +#ifdef NDEBUG + /** + * Do not print objects not deleted if NOT debug + */ + void CaretObject::printListOfObjectsNotDeleted(const bool) { } +#else /** * Print a list of CaretObjects that were not deleted. */ -void +void CaretObject::printListOfObjectsNotDeleted(const bool showCallStack) { -#ifndef NDEBUG int count = 0; if (CaretObject::allocatedObjects.empty() == false) { @@ -152,8 +157,8 @@ CaretObject::printListOfObjectsNotDeleted(const bool showCallStack) if (count > 0) { std::cout << std::dec << count << " objects were not deleted." << std::endl; } -#endif } +#endif /** * Constructor. diff --git a/src/Common/CaretPreferences.cxx b/src/Common/CaretPreferences.cxx index bcbd0581c936400451c82a9b3a432ebe1deac98b..0038e68826acd3241fbd8f33e54900160c13457e 100644 --- a/src/Common/CaretPreferences.cxx +++ b/src/Common/CaretPreferences.cxx @@ -614,6 +614,10 @@ CaretPreferences::setUserBackgroundAndForegroundColors(const BackgroundAndForegr writeUnsignedByteArray(NAME_COLOR_CHART_MATRIX_GRID_LINES, this->userColors.m_colorChartMatrixGridLines, 3); + + writeUnsignedByteArray(NAME_COLOR_CHART_HISTOGRAM_THRESHOLD, + this->userColors.m_colorChartHistogramThreshold, + 3); } /** @@ -1064,6 +1068,29 @@ CaretPreferences::setRemoteFileUserNameAndPassword(const AString& userName, this->qSettings->sync(); } +/** + * @return The BALSA username + */ +AString +CaretPreferences::getBalsaUserName() const +{ + return this->balsaUserName; +} + +/** + * Set the BALSA username + * + * @param userName + * New value for BALSA username. + */ +void +CaretPreferences::setBalsaUserName(const AString& userName) +{ + this->balsaUserName = userName; + this->setString(NAME_BALSA_USER_NAME, + userName); +} + /** * @return Are axes crosshairs displayed? */ @@ -1386,6 +1413,12 @@ CaretPreferences::readPreferences() 3); userColors.setColorChartMatrixGridLines(colorRGB); + userColors.getColorChartHistogramThreshold(colorRGB); + readUnsignedByteArray(NAME_COLOR_CHART_HISTOGRAM_THRESHOLD, + colorRGB, + 3); + userColors.setColorChartHistogramThreshold(colorRGB); + this->previousSpecFiles.clear(); const int numPrevSpec = this->qSettings->beginReadArray(NAME_PREVIOUS_SPEC_FILES); for (int i = 0; i < numPrevSpec; i++) { @@ -1487,6 +1520,8 @@ CaretPreferences::readPreferences() this->remoteFileLoginSaved = this->getBoolean(NAME_REMOTE_FILE_LOGIN_SAVED, false); + this->balsaUserName = this->getString(NAME_BALSA_USER_NAME); + this->showSurfaceIdentificationSymbols = this->getBoolean(NAME_SHOW_SURFACE_IDENTIFICATION_SYMBOLS, true); this->showVolumeIdentificationSymbols = this->getBoolean(NAME_SHOW_VOLUME_IDENTIFICATION_SYMBOLS, diff --git a/src/Common/CaretPreferences.h b/src/Common/CaretPreferences.h index 249ebf2b91baa86b32ec0ebc2addc91940993873..20db3f72b16c8d80e0a1f090b569e4a1e1f0dad1 100644 --- a/src/Common/CaretPreferences.h +++ b/src/Common/CaretPreferences.h @@ -159,6 +159,10 @@ namespace caret { void setRemoteFileUserNameAndPassword(const AString& userName, const AString& password); + AString getBalsaUserName() const; + + void setBalsaUserName(const AString& userName); + static void byteRgbToFloatRgb(const uint8_t byteRGB[3], float floatRGB[3]); @@ -287,9 +291,12 @@ namespace caret { AString remoteFilePassword; bool remoteFileLoginSaved; + AString balsaUserName; + SpecFileDialogViewFilesTypeEnum::Enum manageFilesViewFileType; static const AString NAME_ANIMATION_START_TIME; + static const AString NAME_BALSA_USER_NAME; static const AString NAME_VOLUME_AXES_CROSSHAIRS; static const AString NAME_VOLUME_AXES_LABELS; static const AString NAME_VOLUME_AXES_COORDINATE; @@ -306,6 +313,7 @@ namespace caret { static const AString NAME_COLOR_BACKGROUND_VOLUME; static const AString NAME_COLOR_FOREGROUND_VOLUME; static const AString NAME_COLOR_CHART_MATRIX_GRID_LINES; + static const AString NAME_COLOR_CHART_HISTOGRAM_THRESHOLD; static const AString NAME_DEVELOP_MENU; static const AString NAME_DYNAMIC_CONNECTIVITY_ON; static const AString NAME_IMAGE_CAPTURE_METHOD; @@ -330,6 +338,7 @@ namespace caret { #ifdef __CARET_PREFERENCES_DECLARE__ const AString CaretPreferences::NAME_ANIMATION_START_TIME = "animationStartTime"; + const AString CaretPreferences::NAME_BALSA_USER_NAME = "balsaUserName"; const AString CaretPreferences::NAME_VOLUME_AXES_CROSSHAIRS = "volumeAxesCrosshairs"; const AString CaretPreferences::NAME_VOLUME_AXES_LABELS = "volumeAxesLabels"; const AString CaretPreferences::NAME_VOLUME_AXES_COORDINATE = "volumeAxesCoordinates"; @@ -346,6 +355,7 @@ namespace caret { const AString CaretPreferences::NAME_COLOR_BACKGROUND_VOLUME = "colorBackgroundVolume"; const AString CaretPreferences::NAME_COLOR_FOREGROUND_VOLUME = "colorForegroundVolume"; const AString CaretPreferences::NAME_COLOR_CHART_MATRIX_GRID_LINES = "colorChartMatrixGridLines"; + const AString CaretPreferences::NAME_COLOR_CHART_HISTOGRAM_THRESHOLD = "colorChartHistogramThreshold"; const AString CaretPreferences::NAME_DEVELOP_MENU = "developMenu"; const AString CaretPreferences::NAME_DYNAMIC_CONNECTIVITY_ON = "dynamicConnectivityDefaultedOn"; const AString CaretPreferences::NAME_IMAGE_CAPTURE_METHOD = "imageCaptureMethod"; diff --git a/src/Common/CaretUnitsTypeEnum.cxx b/src/Common/CaretUnitsTypeEnum.cxx new file mode 100644 index 0000000000000000000000000000000000000000..c4e1196424757c9ff71082f340618c4aca1f9c72 --- /dev/null +++ b/src/Common/CaretUnitsTypeEnum.cxx @@ -0,0 +1,389 @@ + +/*LICENSE_START*/ +/* + * Copyright (C) 2017 Washington University School of Medicine + * + * 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 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ +/*LICENSE_END*/ + +#include +#define __CARET_UNITS_TYPE_ENUM_DECLARE__ +#include "CaretUnitsTypeEnum.h" +#undef __CARET_UNITS_TYPE_ENUM_DECLARE__ + +#include "CaretAssert.h" + +using namespace caret; + + +/** + * \class caret::CaretUnitsTypeEnum + * \brief Units Type + * + * Using this enumerated type in the GUI with an EnumComboBoxTemplate + * + * Header File (.h) + * Forward declare the data type: + * class EnumComboBoxTemplate; + * + * Declare the member: + * EnumComboBoxTemplate* m_caretUnitsTypeEnumComboBox; + * + * Declare a slot that is called when user changes selection + * private slots: + * void caretUnitsTypeEnumComboBoxItemActivated(); + * + * Implementation File (.cxx) + * Include the header files + * #include "EnumComboBoxTemplate.h" + * #include "CaretUnitsTypeEnum.h" + * + * Instatiate: + * m_caretUnitsTypeEnumComboBox = new EnumComboBoxTemplate(this); + * m_caretUnitsTypeEnumComboBox->setup(); + * + * Get notified when the user changes the selection: + * QObject::connect(m_caretUnitsTypeEnumComboBox, SIGNAL(itemActivated()), + * this, SLOT(caretUnitsTypeEnumComboBoxItemActivated())); + * + * Update the selection: + * m_caretUnitsTypeEnumComboBox->setSelectedItem(NEW_VALUE); + * + * Read the selection: + * const CaretUnitsTypeEnum::Enum VARIABLE = m_caretUnitsTypeEnumComboBox->getSelectedItem(); + * + */ + +/** + * Constructor. + * + * @param enumValue + * An enumerated value. + * @param name + * Name of enumerated value. + * + * @param guiName + * User-friendly name for use in user-interface. + */ +CaretUnitsTypeEnum::CaretUnitsTypeEnum(const Enum enumValue, + const AString& name, + const AString& guiName) +{ + this->enumValue = enumValue; + this->integerCode = integerCodeCounter++; + this->name = name; + this->guiName = guiName; +} + +/** + * Destructor. + */ +CaretUnitsTypeEnum::~CaretUnitsTypeEnum() +{ +} + +/** + * Initialize the enumerated metadata. + */ +void +CaretUnitsTypeEnum::initialize() +{ + if (initializedFlag) { + return; + } + initializedFlag = true; + + enumData.push_back(CaretUnitsTypeEnum(NONE, + "NONE", + "None")); + + enumData.push_back(CaretUnitsTypeEnum(HERTZ, + "HERTZ", + "Hertz")); + + enumData.push_back(CaretUnitsTypeEnum(METERS, + "METERS", + "Meters")); + + enumData.push_back(CaretUnitsTypeEnum(PARTS_PER_MILLION, + "PARTS_PER_MILLION", + "Parts Per Million")); + + enumData.push_back(CaretUnitsTypeEnum(RADIANS, + "RADIANS", + "Radians")); + + enumData.push_back(CaretUnitsTypeEnum(SECONDS, + "SECONDS", + "Seconds")); + +} + +/** + * Find the data for and enumerated value. + * @param enumValue + * The enumerated value. + * @return Pointer to data for this enumerated type + * or NULL if no data for type or if type is invalid. + */ +const CaretUnitsTypeEnum* +CaretUnitsTypeEnum::findData(const Enum enumValue) +{ + if (initializedFlag == false) initialize(); + + size_t num = enumData.size(); + for (size_t i = 0; i < num; i++) { + const CaretUnitsTypeEnum* d = &enumData[i]; + if (d->enumValue == enumValue) { + return d; + } + } + + return NULL; +} + +/** + * Get a string representation of the enumerated type. + * @param enumValue + * Enumerated value. + * @return + * String representing enumerated value. + */ +AString +CaretUnitsTypeEnum::toName(Enum enumValue) { + if (initializedFlag == false) initialize(); + + const CaretUnitsTypeEnum* enumInstance = findData(enumValue); + return enumInstance->name; +} + +/** + * Get an enumerated value corresponding to its name. + * @param name + * Name of enumerated value. + * @param isValidOut + * If not NULL, it is set indicating that a + * enum value exists for the input name. + * @return + * Enumerated value. + */ +CaretUnitsTypeEnum::Enum +CaretUnitsTypeEnum::fromName(const AString& name, bool* isValidOut) +{ + if (initializedFlag == false) initialize(); + + bool validFlag = false; + Enum enumValue = CaretUnitsTypeEnum::enumData[0].enumValue; + + for (std::vector::iterator iter = enumData.begin(); + iter != enumData.end(); + iter++) { + const CaretUnitsTypeEnum& d = *iter; + if (d.name == name) { + enumValue = d.enumValue; + validFlag = true; + break; + } + } + + if (isValidOut != 0) { + *isValidOut = validFlag; + } + else if (validFlag == false) { + CaretAssertMessage(0, AString("Name " + name + "failed to match enumerated value for type CaretUnitsTypeEnum")); + } + return enumValue; +} + +/** + * Get a GUI string representation of the enumerated type. + * @param enumValue + * Enumerated value. + * @return + * String representing enumerated value. + */ +AString +CaretUnitsTypeEnum::toGuiName(Enum enumValue) { + if (initializedFlag == false) initialize(); + + const CaretUnitsTypeEnum* enumInstance = findData(enumValue); + return enumInstance->guiName; +} + +/** + * Get an enumerated value corresponding to its GUI name. + * @param s + * Name of enumerated value. + * @param isValidOut + * If not NULL, it is set indicating that a + * enum value exists for the input name. + * @return + * Enumerated value. + */ +CaretUnitsTypeEnum::Enum +CaretUnitsTypeEnum::fromGuiName(const AString& guiName, bool* isValidOut) +{ + if (initializedFlag == false) initialize(); + + bool validFlag = false; + Enum enumValue = CaretUnitsTypeEnum::enumData[0].enumValue; + + for (std::vector::iterator iter = enumData.begin(); + iter != enumData.end(); + iter++) { + const CaretUnitsTypeEnum& d = *iter; + if (d.guiName == guiName) { + enumValue = d.enumValue; + validFlag = true; + break; + } + } + + if (isValidOut != 0) { + *isValidOut = validFlag; + } + else if (validFlag == false) { + CaretAssertMessage(0, AString("guiName " + guiName + "failed to match enumerated value for type CaretUnitsTypeEnum")); + } + return enumValue; +} + +/** + * Get the integer code for a data type. + * + * @return + * Integer code for data type. + */ +int32_t +CaretUnitsTypeEnum::toIntegerCode(Enum enumValue) +{ + if (initializedFlag == false) initialize(); + const CaretUnitsTypeEnum* enumInstance = findData(enumValue); + return enumInstance->integerCode; +} + +/** + * Find the data type corresponding to an integer code. + * + * @param integerCode + * Integer code for enum. + * @param isValidOut + * If not NULL, on exit isValidOut will indicate if + * integer code is valid. + * @return + * Enum for integer code. + */ +CaretUnitsTypeEnum::Enum +CaretUnitsTypeEnum::fromIntegerCode(const int32_t integerCode, bool* isValidOut) +{ + if (initializedFlag == false) initialize(); + + bool validFlag = false; + Enum enumValue = CaretUnitsTypeEnum::enumData[0].enumValue; + + for (std::vector::iterator iter = enumData.begin(); + iter != enumData.end(); + iter++) { + const CaretUnitsTypeEnum& enumInstance = *iter; + if (enumInstance.integerCode == integerCode) { + enumValue = enumInstance.enumValue; + validFlag = true; + break; + } + } + + if (isValidOut != 0) { + *isValidOut = validFlag; + } + else if (validFlag == false) { + CaretAssertMessage(0, AString("Integer code " + AString::number(integerCode) + "failed to match enumerated value for type CaretUnitsTypeEnum")); + } + return enumValue; +} + +/** + * Get all of the enumerated type values. The values can be used + * as parameters to toXXX() methods to get associated metadata. + * + * @param allEnums + * A vector that is OUTPUT containing all of the enumerated values. + */ +void +CaretUnitsTypeEnum::getAllEnums(std::vector& allEnums) +{ + if (initializedFlag == false) initialize(); + + allEnums.clear(); + + for (std::vector::iterator iter = enumData.begin(); + iter != enumData.end(); + iter++) { + allEnums.push_back(iter->enumValue); + } +} + +/** + * Get all of the names of the enumerated type values. + * + * @param allNames + * A vector that is OUTPUT containing all of the names of the enumerated values. + * @param isSorted + * If true, the names are sorted in alphabetical order. + */ +void +CaretUnitsTypeEnum::getAllNames(std::vector& allNames, const bool isSorted) +{ + if (initializedFlag == false) initialize(); + + allNames.clear(); + + for (std::vector::iterator iter = enumData.begin(); + iter != enumData.end(); + iter++) { + allNames.push_back(CaretUnitsTypeEnum::toName(iter->enumValue)); + } + + if (isSorted) { + std::sort(allNames.begin(), allNames.end()); + } +} + +/** + * Get all of the GUI names of the enumerated type values. + * + * @param allNames + * A vector that is OUTPUT containing all of the GUI names of the enumerated values. + * @param isSorted + * If true, the names are sorted in alphabetical order. + */ +void +CaretUnitsTypeEnum::getAllGuiNames(std::vector& allGuiNames, const bool isSorted) +{ + if (initializedFlag == false) initialize(); + + allGuiNames.clear(); + + for (std::vector::iterator iter = enumData.begin(); + iter != enumData.end(); + iter++) { + allGuiNames.push_back(CaretUnitsTypeEnum::toGuiName(iter->enumValue)); + } + + if (isSorted) { + std::sort(allGuiNames.begin(), allGuiNames.end()); + } +} + diff --git a/src/Common/CaretUnitsTypeEnum.h b/src/Common/CaretUnitsTypeEnum.h new file mode 100644 index 0000000000000000000000000000000000000000..7fee5353018c43cfc811723831bed742dac939cb --- /dev/null +++ b/src/Common/CaretUnitsTypeEnum.h @@ -0,0 +1,112 @@ +#ifndef __CARET_UNITS_TYPE_ENUM_H__ +#define __CARET_UNITS_TYPE_ENUM_H__ + +/*LICENSE_START*/ +/* + * Copyright (C) 2017 Washington University School of Medicine + * + * 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 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ +/*LICENSE_END*/ + + +#include +#include +#include "AString.h" + +namespace caret { + +class CaretUnitsTypeEnum { + +public: + /** + * Enumerated values. + */ + enum Enum { + /** none/unknown */ + NONE, + /** Hertz (frequency) */ + HERTZ, + /** Meters */ + METERS, + /** Parts per million */ + PARTS_PER_MILLION, + /** radians */ + RADIANS, + /** seconds */ + SECONDS + }; + + + ~CaretUnitsTypeEnum(); + + static AString toName(Enum enumValue); + + static Enum fromName(const AString& name, bool* isValidOut); + + static AString toGuiName(Enum enumValue); + + static Enum fromGuiName(const AString& guiName, bool* isValidOut); + + static int32_t toIntegerCode(Enum enumValue); + + static Enum fromIntegerCode(const int32_t integerCode, bool* isValidOut); + + static void getAllEnums(std::vector& allEnums); + + static void getAllNames(std::vector& allNames, const bool isSorted); + + static void getAllGuiNames(std::vector& allGuiNames, const bool isSorted); + +private: + CaretUnitsTypeEnum(const Enum enumValue, + const AString& name, + const AString& guiName); + + static const CaretUnitsTypeEnum* findData(const Enum enumValue); + + /** Holds all instance of enum values and associated metadata */ + static std::vector enumData; + + /** Initialize instances that contain the enum values and metadata */ + static void initialize(); + + /** Indicates instance of enum values and metadata have been initialized */ + static bool initializedFlag; + + /** Auto generated integer codes */ + static int32_t integerCodeCounter; + + /** The enumerated type value for an instance */ + Enum enumValue; + + /** The integer code associated with an enumerated value */ + int32_t integerCode; + + /** The name, a text string that is identical to the enumerated value */ + AString name; + + /** A user-friendly name that is displayed in the GUI */ + AString guiName; +}; + +#ifdef __CARET_UNITS_TYPE_ENUM_DECLARE__ +std::vector CaretUnitsTypeEnum::enumData; +bool CaretUnitsTypeEnum::initializedFlag = false; +int32_t CaretUnitsTypeEnum::integerCodeCounter = 0; +#endif // __CARET_UNITS_TYPE_ENUM_DECLARE__ + +} // namespace +#endif //__CARET_UNITS_TYPE_ENUM_H__ diff --git a/src/Common/DataFile.cxx b/src/Common/DataFile.cxx index cbc2ecc8980562041b498945adc12cfb2e7ada1e..22c115d661a9f3f198ce9e2c7b64b24f72995ef8 100644 --- a/src/Common/DataFile.cxx +++ b/src/Common/DataFile.cxx @@ -18,6 +18,9 @@ */ /*LICENSE_END*/ +#include + +#include "CaretLogger.h" #include "DataFile.h" #include "DataFileContentInformation.h" #include "DataFileException.h" @@ -78,8 +81,10 @@ DataFile::operator=(const DataFile& df) void DataFile::copyHelperDataFile(const DataFile& df) { - this->filename = df.filename; - modifiedFlag = false; + m_filename = df.m_filename; + m_fileReadWarnings = df.m_fileReadWarnings; + m_modifiedFlag = false; + m_timeOfLastReadOrWrite = QDateTime(); } /** @@ -88,8 +93,10 @@ DataFile::copyHelperDataFile(const DataFile& df) void DataFile::initializeMembersDataFile() { - this->filename = ""; - modifiedFlag = false; + m_filename = ""; + m_fileReadWarnings.clear(); + m_modifiedFlag = false; + m_timeOfLastReadOrWrite = QDateTime(); } /** @@ -107,7 +114,7 @@ DataFile::clear() AString DataFile::getFileName() const { - return this->filename; + return m_filename; } /** @@ -116,12 +123,14 @@ DataFile::getFileName() const AString DataFile::getFileNameNoPath() const { - FileInformation fileInfo(this->filename); + FileInformation fileInfo(m_filename); return fileInfo.getFileName(); } /** * Set the name of the data file. + * This method is virtual so NEVER call it from + * a constructor. Instead, use setFileNameProtected(). * * @param filename * New name of data file. @@ -129,8 +138,22 @@ DataFile::getFileNameNoPath() const void DataFile::setFileName(const AString& filename) { - if (this->filename != filename) { - this->filename = filename; + setFileNameProtected(filename); +} + +/** + * Set the name of the data file. + * This method is NOT virtual so it may be called + * from a constructor. + * + * @param filename + * New name of data file. + */ +void +DataFile::setFileNameProtected(const AString& filename) +{ + if (m_filename != filename) { + m_filename = filename; this->setModified(); } } @@ -153,7 +176,7 @@ DataFile::addToDataFileContentInformation(DataFileContentInformation& dataFileIn void DataFile::setModified() { - this->modifiedFlag = true; + m_modifiedFlag = true; } /** @@ -162,7 +185,13 @@ DataFile::setModified() void DataFile::clearModified() { - this->modifiedFlag = false; + m_modifiedFlag = false; + + /* + * This method, clearModified(), is called by all file + * types at the conclusion of readFile() and writeFile(). + */ + setTimeOfLastReadOrWrite(); } /** @@ -172,7 +201,17 @@ DataFile::clearModified() bool DataFile::isModified() const { - return this->modifiedFlag; + return m_modifiedFlag; +} + +/** + * @return True if a file with the current file name exists, else false. + */ +bool +DataFile::exists() const +{ + FileInformation fileInfo(getFileName()); + return fileInfo.exists(); } /** @@ -264,3 +303,114 @@ DataFile::checkFileWritability(const AString& filename) } } +/** + * Add a warning (non-fatal) message about some issue while reading file file. + * + * @param warning + * Warning message about some issue. + */ +void +DataFile::addFileReadWarning(const AString& warning) +{ + m_fileReadWarnings.appendWithNewLine(warning); +} + +/** + * @return Any warning message from reading the data file. + * + * NOTE: When this method is called, the warning message + * is cleared for this file. + */ +AString +DataFile::getFileReadWarnings() const +{ + const AString warningMessage = m_fileReadWarnings; + m_fileReadWarnings.clear(); + return warningMessage; +} + +/** + * Set the time this file was last read or written to the current time. + */ +void +DataFile::setTimeOfLastReadOrWrite() +{ + m_timeOfLastReadOrWrite = getLastModifiedTime(); +} + +/** + * @return True if this file been modified since it was last read or written. + * (modified by an external program)? + * + * If any of these conditions are met, false is returned: + * (1) The name is empty; (2) The file is on the network; + * (3) The file does not exist; (4) The modified time + * from the file system is invalid; (5) The last time this + * file was read or written is invalid. + */ +bool +DataFile::isModifiedSinceTimeOfLastReadOrWrite() const +{ + const QDateTime lastModTime = getLastModifiedTime(); + + if (lastModTime.isNull()) { + return false; + } + + if (m_timeOfLastReadOrWrite.isNull()) { + return false; + } + + CaretLogFine("Testing reload of " + + getFileNameNoPath() + + " last read/write: " + + m_timeOfLastReadOrWrite.toString(Qt::ISODate) + + " last modified: " + + lastModTime.toString(Qt::ISODate) + + ((m_timeOfLastReadOrWrite == lastModTime) ? " EQUAL" : " NOT EQUAL")); + + /* + * If last modified time is newer than + * time of file read or written + */ + if (lastModTime != m_timeOfLastReadOrWrite) { + return true; + } + + return false; +} + +/** + * @return True if this file been modified since it was last read or written. + * (modified by an external program)? + * + * If any of these conditions are met, a null (.isNull()) is returned: + * (1) The name is empty; (2) The file is on the network; + * (3) The file does not exist; (4) The modified time + * from the file system is invalid. + */ +QDateTime +DataFile::getLastModifiedTime() const +{ + QDateTime lastModTime; + + const AString name = getFileName(); + if (name.isEmpty()) { + return lastModTime; + } + + if (isFileOnNetwork(name)) { + return lastModTime; + } + + QFileInfo fileInfo(name); + if ( ! fileInfo.exists()) { + return lastModTime; + } + + lastModTime = fileInfo.lastModified(); + + return lastModTime; +} + + diff --git a/src/Common/DataFile.h b/src/Common/DataFile.h index f3e878538415fab8b046231e67859802ca51f30d..ed2ab2a07e657faa3dc3747b006a40ed7d0b12db 100644 --- a/src/Common/DataFile.h +++ b/src/Common/DataFile.h @@ -21,6 +21,7 @@ */ /*LICENSE_END*/ +#include #include @@ -45,6 +46,8 @@ namespace caret { DataFile& operator=(const DataFile&); + void setFileNameProtected(const AString& filename); + public: virtual AString getFileName() const; @@ -90,17 +93,33 @@ namespace caret { void checkFileWritability(const AString& filename); + bool exists() const; + + void addFileReadWarning(const AString& warning); + + AString getFileReadWarnings() const; + + bool isModifiedSinceTimeOfLastReadOrWrite() const; + private: void copyHelperDataFile(const DataFile& df); void initializeMembersDataFile(); + void setTimeOfLastReadOrWrite(); + + QDateTime getLastModifiedTime() const; + /** name of data file */ - AString filename; + AString m_filename; + + /** warnings set when file was read */ + mutable AString m_fileReadWarnings; /** modification status */ - bool modifiedFlag; + bool m_modifiedFlag; + QDateTime m_timeOfLastReadOrWrite; }; } // namespace diff --git a/src/Common/DataFileException.cxx b/src/Common/DataFileException.cxx index 41ebc7afc646c7797acd52cf36232152660e52b3..f753824302891684041a527cc25cf8a7a500e7de 100644 --- a/src/Common/DataFileException.cxx +++ b/src/Common/DataFileException.cxx @@ -81,14 +81,13 @@ DataFileException::DataFileException(const AString& dataFileName, AString msg = s; if ( ! dataFileName.isEmpty()) { - msg += "\n"; FileInformation fileInfo(dataFileName); const AString pathName = fileInfo.getPathName(); - msg.appendWithNewLine("File: " + fileInfo.getFileName()); + msg.appendWithNewLine(" Name: " + fileInfo.getFileName()); if ( ! pathName.isEmpty()) { if (pathName != ".") { - msg.appendWithNewLine("Path: " + pathName); + msg.appendWithNewLine(" Path: " + pathName); } } } diff --git a/src/Common/DataFileTypeEnum.cxx b/src/Common/DataFileTypeEnum.cxx index ae7a7457738b8f30c1c294f6f82ef7bb2432d4e6..368496e572348fb49c14ff1f42ed9d56c77fa56f 100644 --- a/src/Common/DataFileTypeEnum.cxx +++ b/src/Common/DataFileTypeEnum.cxx @@ -136,98 +136,98 @@ DataFileTypeEnum::initialize() enumData.push_back(DataFileTypeEnum(CONNECTIVITY_DENSE, "CONNECTIVITY_DENSE", - "Connectivity - Dense", + "CIFTI - Dense", "CONNECTIVITY", false, "dconn.nii")); enumData.push_back(DataFileTypeEnum(CONNECTIVITY_DENSE_DYNAMIC, "CONNECTIVITY_DENSE_DYNAMIC", - "Connectivity - Dense Dynamic", + "CIFTI - Dense Dynamic", "CONNECTIVITY DYNAMIC", false, "dynconn.nii")); enumData.push_back(DataFileTypeEnum(CONNECTIVITY_DENSE_LABEL, "CONNECTIVITY_DENSE_LABEL", - "Connectivity - Dense Label", + "CIFTI - Dense Label", "CIFTI LABELS", false, "dlabel.nii")); enumData.push_back(DataFileTypeEnum(CONNECTIVITY_DENSE_PARCEL, "CONNECTIVITY_DENSE_PARCEL", - "Connectivity - Dense Parcel", + "CIFTI - Dense Parcel", "CIFTI DENSE PARCEL", false, "dpconn.nii")); enumData.push_back(DataFileTypeEnum(CONNECTIVITY_DENSE_SCALAR, "CONNECTIVITY_DENSE_SCALAR", - "Connectivity - Dense Scalar", + "CIFTI - Dense Scalar", "CIFTI SCALARS", false, "dscalar.nii")); enumData.push_back(DataFileTypeEnum(CONNECTIVITY_DENSE_TIME_SERIES, "CONNECTIVITY_DENSE_TIME_SERIES", - "Connectivity - Dense Data Series", + "CIFTI - Dense Data Series", "DATA SERIES", false, "dtseries.nii")); enumData.push_back(DataFileTypeEnum(CONNECTIVITY_FIBER_ORIENTATIONS_TEMPORARY, "CONNECTIVITY_FIBER_ORIENTATIONS_TEMPORARY", - "Connectivity - Fiber Orientations TEMPORARY", + "CIFTI - Fiber Orientations TEMPORARY", "FIBER ORIENTATION TEMPORARY", false, "fiberTEMP.nii")); enumData.push_back(DataFileTypeEnum(CONNECTIVITY_FIBER_TRAJECTORY_TEMPORARY, "CONNECTIVITY_FIBER_TRAJECTORY_TEMPORARY", - "Connectivity - Fiber Trajectory TEMPORARY", + "CIFTI - Fiber Trajectory TEMPORARY", "FIBER TRAJECTORY TEMPORARY", false, "trajTEMP.wbsparse")); enumData.push_back(DataFileTypeEnum(CONNECTIVITY_PARCEL, "CONNECTIVITY_PARCEL", - "Connectivity - Parcel", + "CIFTI - Parcel", "CIFTI PARCEL", false, "pconn.nii")); enumData.push_back(DataFileTypeEnum(CONNECTIVITY_PARCEL_DENSE, "CONNECTIVITY_PARCEL_DENSE", - "Connectivity - Parcel Dense", + "CIFTI - Parcel Dense", "CIFTI PARCEL DENSE", false, "pdconn.nii")); enumData.push_back(DataFileTypeEnum(CONNECTIVITY_PARCEL_LABEL, "CONNECTIVITY_PARCEL_LABEL", - "Connectivity - Parcel Label", + "CIFTI - Parcel Label", "CIFTI PARCEL LABEL", false, "plabel.nii")); enumData.push_back(DataFileTypeEnum(CONNECTIVITY_PARCEL_SCALAR, "CONNECTIVITY_PARCEL_SCALAR", - "Connectivity - Parcel Scalar", + "CIFTI - Parcel Scalar", "CIFTI PARCEL SCALAR", false, "pscalar.nii")); enumData.push_back(DataFileTypeEnum(CONNECTIVITY_PARCEL_SERIES, "CONNECTIVITY_PARCEL_SERIES", - "Connectivity - Parcel Series", + "CIFTI - Parcel Series", "CIFTI PARCEL SERIES", false, "ptseries.nii")); enumData.push_back(DataFileTypeEnum(CONNECTIVITY_SCALAR_DATA_SERIES, "CONNECTIVITY_SCALAR_DATA_SERIES", - "Connectivity - Scalar Data Series", + "CIFTI - Scalar Data Series", "CIFTI SCALAR DATA SERIES", false, "sdseries.nii")); @@ -424,41 +424,34 @@ DataFileTypeEnum::toGuiName(Enum enumValue) { } /** - * Get an enumerated value corresponding to its GUI name. - * @param s - * Name of enumerated value. - * @param isValidOut - * If not NULL, it is set indicating that a - * enum value exists for the input name. - * @return + * Get a short GUI string representation of the enumerated type. + * Removes any "Connectivity - " or " TEMPORARY" from the toGuiName(). + * @param enumValue * Enumerated value. + * @return + * String representing enumerated value. */ -DataFileTypeEnum::Enum -DataFileTypeEnum::fromGuiName(const AString& guiName, bool* isValidOut) +AString +DataFileTypeEnum::toShortGuiName(Enum enumValue) { - if (initializedFlag == false) initialize(); + const AString typeName = DataFileTypeEnum::toGuiName(enumValue); - bool validFlag = false; - Enum enumValue = UNKNOWN; + const AString connectivityPrefix("Connectivity - "); + const int connectivityPrefixLength = connectivityPrefix.length(); - for (std::vector::iterator iter = enumData.begin(); - iter != enumData.end(); - iter++) { - const DataFileTypeEnum& d = *iter; - if (d.guiName == guiName) { - enumValue = d.enumValue; - validFlag = true; - break; - } - } + const AString temporarySuffix(" TEMPORARY"); + const int temporarySuffixLength = temporarySuffix.length(); - if (isValidOut != 0) { - *isValidOut = validFlag; + AString text = typeName; + if (text.startsWith(connectivityPrefix)) { + text = text.mid(connectivityPrefixLength); } - else if (validFlag == false) { - CaretAssertMessage(0, AString("guiName \"" + guiName + "\" failed to match enumerated value for type DataFileTypeEnum")); + + if (text.endsWith(temporarySuffix)) { + text = text.left(text.length() - temporarySuffixLength); } - return enumValue; + + return text; } /** diff --git a/src/Common/DataFileTypeEnum.h b/src/Common/DataFileTypeEnum.h index 50d272f9bc96ce4cd414f0e041860494953f043d..c1fda126f531891eeb4952ea0d97416ca30d7287 100644 --- a/src/Common/DataFileTypeEnum.h +++ b/src/Common/DataFileTypeEnum.h @@ -112,7 +112,7 @@ public: static AString toGuiName(Enum enumValue); - static Enum fromGuiName(const AString& guiName, bool* isValidOut); + static AString toShortGuiName(Enum enumValue); static AString toOverlayTypeName(Enum enumValue); diff --git a/src/Common/DeveloperFlagsEnum.cxx b/src/Common/DeveloperFlagsEnum.cxx index 2da2da5240b3798fa0b260d0db02b5bd06e2fa86..7892fd12e5ff0f139f7f61939ea969ac6dece8b7 100644 --- a/src/Common/DeveloperFlagsEnum.cxx +++ b/src/Common/DeveloperFlagsEnum.cxx @@ -95,6 +95,9 @@ DeveloperFlagsEnum::DeveloperFlagsEnum(const Enum enumValue, case DEVELOPER_FLAG_UNUSED: this->flagStatus = false; break; + case DEVELOPER_FLAG_FLIP_PALETTE_NOT_DATA: + this->flagStatus = false; + break; } } @@ -118,7 +121,10 @@ DeveloperFlagsEnum::initialize() enumData.push_back(DeveloperFlagsEnum(DEVELOPER_FLAG_UNUSED, "DEVELOPER_FLAG_UNUSED", - "Developer flag unused")); + "Developer Flag Unused")); + enumData.push_back(DeveloperFlagsEnum(DEVELOPER_FLAG_FLIP_PALETTE_NOT_DATA, + "DEVELOPER_FLAG_FLIP_PALETTE_NOT_DATA", + "Flip Palette Not Data")); } /** diff --git a/src/Common/DeveloperFlagsEnum.h b/src/Common/DeveloperFlagsEnum.h index 29de8d76c22426e2de114a1ecd6ac5dabceb909c..39ffae64f96809c79b1d4a6eb401c0aab8485b5f 100644 --- a/src/Common/DeveloperFlagsEnum.h +++ b/src/Common/DeveloperFlagsEnum.h @@ -35,7 +35,8 @@ public: * Enumerated values. */ enum Enum { - DEVELOPER_FLAG_UNUSED + DEVELOPER_FLAG_UNUSED, + DEVELOPER_FLAG_FLIP_PALETTE_NOT_DATA }; ~DeveloperFlagsEnum(); diff --git a/src/Common/DisplayGroupAndTabItemInterface.cxx b/src/Common/DisplayGroupAndTabItemInterface.cxx index 85166362253c82a7090807c3bb18c47d6c3ccaa7..a0b4a047f2995034c0cb6e6fcdbcbe65ad51af73 100644 --- a/src/Common/DisplayGroupAndTabItemInterface.cxx +++ b/src/Common/DisplayGroupAndTabItemInterface.cxx @@ -139,6 +139,22 @@ DisplayGroupAndTabItemInterface::setItemDisplaySelectedInAllGroups() } } +/** + * Set the display status to UNSELECTED for all groups (except TAB). + */ +void +DisplayGroupAndTabItemInterface::setItemDisplaySelectedInNoDisplayGroups() +{ + std::vector groupEnums; + DisplayGroupEnum::getAllEnumsExceptTab(groupEnums); + + for (auto group : groupEnums) { + setItemDisplaySelected(group, + 0, + TriStateSelectionStatusEnum::UNSELECTED); + } +} + /** * Set the display status to SELECTED for the given group (not TAB) and * UNSELECTED for all other groups (not TAB). diff --git a/src/Common/DisplayGroupAndTabItemInterface.h b/src/Common/DisplayGroupAndTabItemInterface.h index d0a17f64af3356b989afe26ea1f3e63fc4836326..aab162aa4a341796521e3b85596ad3b02f874fee 100644 --- a/src/Common/DisplayGroupAndTabItemInterface.h +++ b/src/Common/DisplayGroupAndTabItemInterface.h @@ -158,6 +158,8 @@ namespace caret { virtual void setItemDisplaySelectedInAllGroups(); + virtual void setItemDisplaySelectedInNoDisplayGroups(); + virtual void setItemDisplaySelectedInOneGroup(const DisplayGroupEnum::Enum displayGroup); /** diff --git a/src/Common/DrawnWithOpenGLTextureInfo.cxx b/src/Common/DrawnWithOpenGLTextureInfo.cxx deleted file mode 100644 index 92431b8b77dedeb33cd625cefe817de9d21a49fc..0000000000000000000000000000000000000000 --- a/src/Common/DrawnWithOpenGLTextureInfo.cxx +++ /dev/null @@ -1,210 +0,0 @@ - -/*LICENSE_START*/ -/* - * Copyright (C) 2016 Washington University School of Medicine - * - * 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 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License along - * with this program; if not, write to the Free Software Foundation, Inc., - * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. - */ -/*LICENSE_END*/ - -#define __DRAWN_WITH_OPENGL_TEXTURE_INFO_DECLARE__ -#include "DrawnWithOpenGLTextureInfo.h" -#undef __DRAWN_WITH_OPENGL_TEXTURE_INFO_DECLARE__ - -#include "CaretAssert.h" -#include "EventManager.h" -#include "EventOpenGLTexture.h" - -using namespace caret; - - - -/** - * \class caret::DrawnWithOpenGLTextureInfo - * \brief Assists with managing texture names for items drawn as textures. - * \ingroup Common - * - * OpenGL textures use resources in the graphics system. Classes - * implementing this interface must contain an instance of - * DrawnWithOpenGLTextureInfo which contains information about - * the OpenGL texture. When an instance of DrawnWithOpenGLTextureInfo - * goes out of scope (deleted), it will send a message to - * OpenGL rendering to free up any associated resources with the - * OpenGL texture. - */ - -/** - * Constructor. - */ -DrawnWithOpenGLTextureInfo::DrawnWithOpenGLTextureInfo() -: CaretObject(), - EventListenerInterface() -{ - resetTextureNames(); - - EventManager::get()->addEventListener(this, EventTypeEnum::EVENT_OPENGL_TEXTURE); -} - -/** - * Destructor. - */ -DrawnWithOpenGLTextureInfo::~DrawnWithOpenGLTextureInfo() -{ - EventManager::get()->removeAllEventsFromListener(this); - - /* - * Need to release the texture names since no longer needed. - */ - for (int32_t i = 0; i < BrainConstants::MAXIMUM_NUMBER_OF_BROWSER_WINDOWS; i++) { - CaretAssertArrayIndex(m_textureNamesForWindow, BrainConstants::MAXIMUM_NUMBER_OF_BROWSER_WINDOWS, i); - if (m_textureNamesForWindow[i] > 0) { - EventOpenGLTexture textureEvent; - textureEvent.setModeDeleteTexture(i, m_textureNamesForWindow[i]); - EventManager::get()->sendEvent(textureEvent.getPointer()); - } - } -} - -/** - * Copy constructor. - * @param obj - * Object that is copied. - */ -DrawnWithOpenGLTextureInfo::DrawnWithOpenGLTextureInfo(const DrawnWithOpenGLTextureInfo& obj) -: CaretObject(obj), EventListenerInterface() -{ - this->copyHelperDrawnWithOpenGLTextureInfo(obj); -} - -/** - * Assignment operator. - * @param obj - * Data copied from obj to this. - * @return - * Reference to this object. - */ -DrawnWithOpenGLTextureInfo& -DrawnWithOpenGLTextureInfo::operator=(const DrawnWithOpenGLTextureInfo& obj) -{ - if (this != &obj) { - CaretObject::operator=(obj); - this->copyHelperDrawnWithOpenGLTextureInfo(obj); - } - return *this; -} - -/** - * Helps with copying an object of this type. - * @param obj - * Object that is copied. - */ -void -DrawnWithOpenGLTextureInfo::copyHelperDrawnWithOpenGLTextureInfo(const DrawnWithOpenGLTextureInfo& /*obj*/) -{ - /* - * Do not copy the texture identfiers. - */ - resetTextureNames(); -} - -/** - * Receive an event. - * - * @param event - * The event that the receive can respond to. - */ -void -DrawnWithOpenGLTextureInfo::receiveEvent(Event* event) -{ - if (event->getEventType() == EventTypeEnum::EVENT_OPENGL_TEXTURE) { - EventOpenGLTexture* textureEvent = dynamic_cast(event); - CaretAssert(textureEvent); - - switch (textureEvent->getMode()) { - case EventOpenGLTexture::MODE_NONE: - break; - case EventOpenGLTexture::MODE_DELETE_ALL_TEXTURES_IN_WINDOW: - { - int32_t windowIndex = 0; - textureEvent->getModeDeleteAllTexturesInWindow(windowIndex); - if ((windowIndex >= 0) - && (windowIndex < BrainConstants::MAXIMUM_NUMBER_OF_BROWSER_WINDOWS)) { - m_textureNamesForWindow[windowIndex] = 0; - textureEvent->setEventProcessed(); - } - } - break; - case EventOpenGLTexture::MODE_DELETE_TEXTURE: - break; - } - } -} - -/** - * Reset the texture names. - */ -void -DrawnWithOpenGLTextureInfo::resetTextureNames() -{ - for (int32_t i = 0; i < BrainConstants::MAXIMUM_NUMBER_OF_BROWSER_WINDOWS; i++) { - CaretAssertArrayIndex(m_textureNamesForWindow, BrainConstants::MAXIMUM_NUMBER_OF_BROWSER_WINDOWS, i); - m_textureNamesForWindow[i] = -1; - } -} - - -/** - * Get the texture name for the given window index. - * - * @param windowIndex - * Index of the window. - * @return - * The texture name. Valid texture names are always - * greater than zero. - */ -int32_t -DrawnWithOpenGLTextureInfo::getTextureNameForWindow(const int32_t windowIndex) const -{ - CaretAssertArrayIndex(m_textureNamesForWindow, BrainConstants::MAXIMUM_NUMBER_OF_BROWSER_WINDOWS, windowIndex); - return m_textureNamesForWindow[windowIndex]; -} - -/** - * Set the texture name for the given window index. - * - * @param windowIndex - * Index of the window. - * @param textureName - * The texture name. Valid texture name are always - * greater than zero. - */ -void -DrawnWithOpenGLTextureInfo::setTextureNameForWindow(const int32_t windowIndex, - const int32_t textureName) -{ - CaretAssertArrayIndex(m_textureNamesForWindow, BrainConstants::MAXIMUM_NUMBER_OF_BROWSER_WINDOWS, windowIndex); - m_textureNamesForWindow[windowIndex] = textureName; -} - -/** - * Get a description of this object's content. - * @return String describing this object's content. - */ -AString -DrawnWithOpenGLTextureInfo::toString() const -{ - return "DrawnWithOpenGLTextureInfo"; -} - diff --git a/src/Common/DrawnWithOpenGLTextureInfo.h b/src/Common/DrawnWithOpenGLTextureInfo.h deleted file mode 100644 index 4527df9fb9edb384b6d129768dda0dd555d3401f..0000000000000000000000000000000000000000 --- a/src/Common/DrawnWithOpenGLTextureInfo.h +++ /dev/null @@ -1,68 +0,0 @@ -#ifndef __DRAWN_WITH_OPENGL_TEXTURE_INFO_H__ -#define __DRAWN_WITH_OPENGL_TEXTURE_INFO_H__ - -/*LICENSE_START*/ -/* - * Copyright (C) 2016 Washington University School of Medicine - * - * 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 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License along - * with this program; if not, write to the Free Software Foundation, Inc., - * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. - */ -/*LICENSE_END*/ - -#include "BrainConstants.h" -#include "CaretObject.h" -#include "EventListenerInterface.h" - -namespace caret { - - class DrawnWithOpenGLTextureInfo : public CaretObject, public EventListenerInterface { - - public: - DrawnWithOpenGLTextureInfo(); - - virtual ~DrawnWithOpenGLTextureInfo(); - - virtual void receiveEvent(Event* event); - - DrawnWithOpenGLTextureInfo(const DrawnWithOpenGLTextureInfo& obj); - - DrawnWithOpenGLTextureInfo& operator=(const DrawnWithOpenGLTextureInfo& obj); - - int32_t getTextureNameForWindow(const int32_t windowIndex) const; - - void setTextureNameForWindow(const int32_t windowIndex, - const int32_t textureName); - - // ADD_NEW_METHODS_HERE - - virtual AString toString() const; - - private: - void copyHelperDrawnWithOpenGLTextureInfo(const DrawnWithOpenGLTextureInfo& obj); - - void resetTextureNames(); - - int64_t m_textureNamesForWindow[BrainConstants::MAXIMUM_NUMBER_OF_BROWSER_WINDOWS]; - - // ADD_NEW_MEMBERS_HERE - - }; - -#ifdef __DRAWN_WITH_OPENGL_TEXTURE_INFO_DECLARE__ - // -#endif // __DRAWN_WITH_OPENGL_TEXTURE_INFO_DECLARE__ - -} // namespace -#endif //__DRAWN_WITH_OPENGL_TEXTURE_INFO_H__ diff --git a/src/Common/DrawnWithOpenGLTextureInterface.cxx b/src/Common/DrawnWithOpenGLTextureInterface.cxx deleted file mode 100644 index 968823aa957a51a8b2e59a87ae8488c2ef131ba8..0000000000000000000000000000000000000000 --- a/src/Common/DrawnWithOpenGLTextureInterface.cxx +++ /dev/null @@ -1,92 +0,0 @@ - -/*LICENSE_START*/ -/* - * Copyright (C) 2016 Washington University School of Medicine - * - * 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 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License along - * with this program; if not, write to the Free Software Foundation, Inc., - * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. - */ -/*LICENSE_END*/ - -#define __DRAWN_WITH_TEXTURE_INTERFACE_DECLARE__ -#include "DrawnWithTextureInterface.h" -#undef __DRAWN_WITH_TEXTURE_INTERFACE_DECLARE__ - -#include "CaretAssert.h" -using namespace caret; - - - -/** - * \class caret::DrawnWithTextureInterface - * \brief - * \ingroup Common - * - * - */ - -/** - * Constructor. - */ -DrawnWithTextureInterface::DrawnWithTextureInterface() -{ - -} - -/** - * Destructor. - */ -DrawnWithTextureInterface::~DrawnWithTextureInterface() -{ -} - -/** - * Copy constructor. - * @param obj - * Object that is copied. - */ -DrawnWithTextureInterface::DrawnWithTextureInterface(const DrawnWithTextureInterface& obj) -: (obj) -{ - this->copyHelperDrawnWithTextureInterface(obj); -} - -/** - * Assignment operator. - * @param obj - * Data copied from obj to this. - * @return - * Reference to this object. - */ -DrawnWithTextureInterface& -DrawnWithTextureInterface::operator=(const DrawnWithTextureInterface& obj) -{ - if (this != &obj) { - ::operator=(obj); - this->copyHelperDrawnWithTextureInterface(obj); - } - return *this; -} - -/** - * Helps with copying an object of this type. - * @param obj - * Object that is copied. - */ -void -DrawnWithTextureInterface::copyHelperDrawnWithTextureInterface(const DrawnWithTextureInterface& obj) -{ - -} - diff --git a/src/Common/DrawnWithOpenGLTextureInterface.h b/src/Common/DrawnWithOpenGLTextureInterface.h deleted file mode 100644 index ec13708ab4f539f352d40a35564f3c4ed4585b52..0000000000000000000000000000000000000000 --- a/src/Common/DrawnWithOpenGLTextureInterface.h +++ /dev/null @@ -1,87 +0,0 @@ -#ifndef __DRAWN_WITH_OPENGL_TEXTURE_INTERFACE_H__ -#define __DRAWN_WITH_OPENGL_TEXTURE_INTERFACE_H__ - -/*LICENSE_START*/ -/* - * Copyright (C) 2016 Washington University School of Medicine - * - * 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 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License along - * with this program; if not, write to the Free Software Foundation, Inc., - * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. - */ -/*LICENSE_END*/ - -#include - -/** - * \class caret::DrawnWithOpenGLTextureInterface - * \brief Interface for items that are drawn with an OpenGL texture. - * \ingroup Common - * - * OpenGL textures use resources in the graphics system. Classes - * implementing this interface must contain an instance of - * DrawnWithOpenGLTextureInfo which contains information about - * the OpenGL texture. When an instance of DrawnWithOpenGLTextureInfo - * goes out of scope (deleted), it will send a message to - * OpenGL rendering to free up any associated resources with the - * OpenGL texture. - */ - - - -namespace caret { - - class DrawnWithOpenGLTextureInfo; - - class DrawnWithOpenGLTextureInterface { - - public: - /** - * Constructor. - */ - DrawnWithOpenGLTextureInterface() { } - - /** - * Destructor. - */ - virtual ~DrawnWithOpenGLTextureInterface() { } - - /** - * @return The OpenGL texture information used for managing - * texture resources. - */ - virtual DrawnWithOpenGLTextureInfo* getDrawWithOpenGLTextureInfo() = 0; - - /** - * @return The OpenGL texture information used for managing - * texture resources (const method) - */ - virtual const DrawnWithOpenGLTextureInfo* getDrawWithOpenGLTextureInfo() const = 0; - - // ADD_NEW_METHODS_HERE - - private: - DrawnWithOpenGLTextureInterface(const DrawnWithOpenGLTextureInterface& /*obj*/); - - DrawnWithOpenGLTextureInterface& operator=(const DrawnWithOpenGLTextureInterface& /*obj*/); - - // ADD_NEW_MEMBERS_HERE - - }; - -#ifdef __DRAWN_WITH_OPENGL_TEXTURE_INTERFACE_DECLARE__ - // -#endif // __DRAWN_WITH_OPENGL_TEXTURE_INTERFACE_DECLARE__ - -} // namespace -#endif //__DRAWN_WITH_OPENGL_TEXTURE_INTERFACE_H__ diff --git a/src/Common/ElapsedTimer.cxx b/src/Common/ElapsedTimer.cxx index ccafa3939cd530c575a6b2f696790949e196f34c..d49549d8c047cef68b65ceabbba2e9770b27d1cf 100644 --- a/src/Common/ElapsedTimer.cxx +++ b/src/Common/ElapsedTimer.cxx @@ -123,3 +123,16 @@ ElapsedTimer::getElapsedTimeMilliseconds() const return diffTimeMilli; } +/** + * Print the given message followed by the elapsed time in seconds. + * + * @param message + * Message that is printed before the seconds. + */ +void +ElapsedTimer::printSeconds(const AString& message) +{ + std::cout << message << " " << getElapsedTimeSeconds() << " seconds" << std::endl; +} + + diff --git a/src/Common/ElapsedTimer.h b/src/Common/ElapsedTimer.h index 08151b8fd7c33dac9af7f8d10ed4c4350410021c..7b4f3923adbe1bdbbd2c3530bec40502a039b79d 100644 --- a/src/Common/ElapsedTimer.h +++ b/src/Common/ElapsedTimer.h @@ -64,6 +64,8 @@ namespace caret { double getElapsedTimeMilliseconds() const; + void printSeconds(const AString& message); + private: ElapsedTimer(const ElapsedTimer&); diff --git a/src/Brain/EventBrowserTabDelete.cxx b/src/Common/EventBrowserTabDelete.cxx similarity index 89% rename from src/Brain/EventBrowserTabDelete.cxx rename to src/Common/EventBrowserTabDelete.cxx index 2b4f8d148773c65cfd1016dd22d3a90ec495fa81..c341e5f4d26236ed96ef289e1086718e47c38300 100644 --- a/src/Brain/EventBrowserTabDelete.cxx +++ b/src/Common/EventBrowserTabDelete.cxx @@ -19,7 +19,6 @@ /*LICENSE_END*/ #include "CaretAssert.h" -#include "BrowserTabContent.h" #include "EventBrowserTabDelete.h" using namespace caret; @@ -27,12 +26,13 @@ using namespace caret; /** * Constructor. */ -EventBrowserTabDelete::EventBrowserTabDelete(BrowserTabContent* browserTab) -: Event(EventTypeEnum::EVENT_BROWSER_TAB_DELETE) +EventBrowserTabDelete::EventBrowserTabDelete(BrowserTabContent* browserTab, + const int32_t browserTabIndex) +: Event(EventTypeEnum::EVENT_BROWSER_TAB_DELETE), +m_browserTab(browserTab), +m_browserTabIndex(browserTabIndex) { CaretAssert(browserTab); - m_browserTab = browserTab; - m_browserTabIndex = browserTab->getTabNumber(); } /** diff --git a/src/Brain/EventBrowserTabDelete.h b/src/Common/EventBrowserTabDelete.h similarity index 90% rename from src/Brain/EventBrowserTabDelete.h rename to src/Common/EventBrowserTabDelete.h index 678aac926797a3987aa9fa119bdbec61d7983ea1..d2f717f28149dbdd5cae12336a1151cba1a67d82 100644 --- a/src/Brain/EventBrowserTabDelete.h +++ b/src/Common/EventBrowserTabDelete.h @@ -33,7 +33,8 @@ namespace caret { class EventBrowserTabDelete : public Event { public: - EventBrowserTabDelete(BrowserTabContent* browserTab); + EventBrowserTabDelete(BrowserTabContent* browserTab, + const int32_t browserTabIndex); virtual ~EventBrowserTabDelete(); @@ -48,7 +49,7 @@ namespace caret { BrowserTabContent* m_browserTab; - int32_t m_browserTabIndex; + const int32_t m_browserTabIndex; }; } // namespace diff --git a/src/Common/EventCaretPreferencesGet.cxx b/src/Common/EventCaretPreferencesGet.cxx new file mode 100644 index 0000000000000000000000000000000000000000..1ce82069bab741f21b0f8fea302b1f67fd76ad21 --- /dev/null +++ b/src/Common/EventCaretPreferencesGet.cxx @@ -0,0 +1,75 @@ + +/*LICENSE_START*/ +/* + * Copyright (C) 2017 Washington University School of Medicine + * + * 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 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ +/*LICENSE_END*/ + +#define __EVENT_CARET_PREFERENCES_GET_DECLARE__ +#include "EventCaretPreferencesGet.h" +#undef __EVENT_CARET_PREFERENCES_GET_DECLARE__ + +#include "CaretAssert.h" +#include "EventTypeEnum.h" + +using namespace caret; + + + +/** + * \class caret::EventCaretPreferencesGet + * \brief Event to get the Caret Preferences + * \ingroup Common + */ + +/** + * Constructor. + */ +EventCaretPreferencesGet::EventCaretPreferencesGet() +: Event(EventTypeEnum::EVENT_CARET_PREFERENCES_GET) +{ + +} + +/** + * Destructor. + */ +EventCaretPreferencesGet::~EventCaretPreferencesGet() +{ +} + +/** + * Set the caret preferences. + * + * @param caretPreferences + * Pointer to Caret Preferences + */ +void +EventCaretPreferencesGet::setCaretPreferences(CaretPreferences* caretPreferences) +{ + m_caretPreferences = caretPreferences; +} + +/** + * @return The caret preferences (NULL if caret preferences are invalid) + */ +CaretPreferences* +EventCaretPreferencesGet::getCaretPreferences() const +{ + return m_caretPreferences; +} + diff --git a/src/Common/EventCaretPreferencesGet.h b/src/Common/EventCaretPreferencesGet.h new file mode 100644 index 0000000000000000000000000000000000000000..f381205c105a56d9d07273dc4447c60938dca508 --- /dev/null +++ b/src/Common/EventCaretPreferencesGet.h @@ -0,0 +1,65 @@ +#ifndef __EVENT_CARET_PREFERENCES_GET_H__ +#define __EVENT_CARET_PREFERENCES_GET_H__ + +/*LICENSE_START*/ +/* + * Copyright (C) 2017 Washington University School of Medicine + * + * 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 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ +/*LICENSE_END*/ + + + +#include + +#include "Event.h" + + + +namespace caret { + + class CaretPreferences; + + class EventCaretPreferencesGet : public Event { + + public: + EventCaretPreferencesGet(); + + virtual ~EventCaretPreferencesGet(); + + void setCaretPreferences(CaretPreferences* caretPreferences); + + CaretPreferences* getCaretPreferences() const; + + // ADD_NEW_METHODS_HERE + + private: + EventCaretPreferencesGet(const EventCaretPreferencesGet&); + + EventCaretPreferencesGet& operator=(const EventCaretPreferencesGet&); + + CaretPreferences* m_caretPreferences = NULL; + + // ADD_NEW_MEMBERS_HERE + + }; + +#ifdef __EVENT_CARET_PREFERENCES_GET_DECLARE__ + // +#endif // __EVENT_CARET_PREFERENCES_GET_DECLARE__ + +} // namespace +#endif //__EVENT_CARET_PREFERENCES_GET_H__ diff --git a/src/Brain/CaretOpenGLInclude.h b/src/Common/EventListenerInterface.cxx similarity index 60% rename from src/Brain/CaretOpenGLInclude.h rename to src/Common/EventListenerInterface.cxx index d90329cdbfcf696973d4298516ce9b29659b3e32..2ad316b4762fa790bc77596a98fbb1fccfa0b655 100644 --- a/src/Brain/CaretOpenGLInclude.h +++ b/src/Common/EventListenerInterface.cxx @@ -1,9 +1,7 @@ -#ifndef __CARET_OPEN_GL_INCLUDE_H__ -#define __CARET_OPEN_GL_INCLUDE_H__ /*LICENSE_START*/ /* - * Copyright (C) 2014 Washington University School of Medicine + * Copyright (C) 2017 Washington University School of Medicine * * 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 @@ -21,22 +19,33 @@ */ /*LICENSE_END*/ +#include "EventListenerInterface.h" + +#include "CaretAssert.h" +#include "EventManager.h" +using namespace caret; + + + +/** + * \class caret::EventListenerInterface2 + * \brief Interface for classes that listen for events + * \ingroup Common + */ + +/** + * Constructor. + */ +EventListenerInterface::EventListenerInterface() +{ + +} + /** - * \file CaretOpenGLInclude.h - * \brief Contains OpenGL include declarations that work for all operating systems. - * \ingroup Brain + * Destructor. */ +EventListenerInterface::~EventListenerInterface() +{ + EventManager::get()->verifyAllListenersRemoved(this); +} -#ifdef CARET_OS_WINDOWS -#include -#endif -#ifdef CARET_OS_MACOSX -#include -#include -#else -#define GL_GLEXT_PROTOTYPES -#include -#include -#endif - -#endif //__CARET_OPEN_GL_INCLUDE_H__ diff --git a/src/Common/EventListenerInterface.h b/src/Common/EventListenerInterface.h index d4e798ae90af1e8f7a22182c97affda2eb29a5a7..9031ae654a6116fbabecbc0e6d1b917b293665f3 100644 --- a/src/Common/EventListenerInterface.h +++ b/src/Common/EventListenerInterface.h @@ -37,12 +37,12 @@ namespace caret { /** * Constructor. */ - EventListenerInterface() { } + EventListenerInterface(); /** * Destructor. */ - virtual ~EventListenerInterface() { } + virtual ~EventListenerInterface(); private: EventListenerInterface(const EventListenerInterface&) { } diff --git a/src/Common/EventManager.cxx b/src/Common/EventManager.cxx index d803f50b71836f89899d499b493f2e263d5f884a..be106076752095e3a5a4fddd32042a895913eb17 100644 --- a/src/Common/EventManager.cxx +++ b/src/Common/EventManager.cxx @@ -34,6 +34,7 @@ #include "CaretLogger.h" #include "EventAlertUser.h" #include "EventListenerInterface.h" +#include "SystemUtilities.h" using namespace caret; /** @@ -175,12 +176,6 @@ EventManager::addEventListener(EventListenerInterface* eventListener, #else INTENTIONAL_COMPILER_ERROR_MISSING_CONTAINER_TYPE #endif - - //std::cout << "Adding listener from class " - //<< typeid(*eventListener).name() - //<< " for " - //<< EventTypeEnum::toName(listenForEventType) - //<< std::endl; } /** @@ -205,12 +200,6 @@ EventManager::addProcessedEventListener(EventListenerInterface* eventListener, #else INTENTIONAL_COMPILER_ERROR_MISSING_CONTAINER_TYPE #endif - - //std::cout << "Adding listener from class " - //<< typeid(*eventListener).name() - //<< " for " - //<< EventTypeEnum::toName(listenForEventType) - //<< std::endl; } /** @@ -248,59 +237,6 @@ EventManager::removeEventFromListener(EventListenerInterface* eventListener, if (processedEventIter != processedListeners.end()) { processedListeners.erase(processedEventIter); } - -// EVENT_LISTENER_CONTAINER listeners = m_eventListeners[listenForEventType]; -// -// /* -// * Remove the listener by creating a new container -// * of non-matching listeners. -// */ -// EVENT_LISTENER_CONTAINER updatedListeners; -// for (EVENT_LISTENER_CONTAINER_ITERATOR iter = listeners.begin(); -// iter != listeners.end(); -// iter++) { -// if (*iter == eventListener) { -// //std::cout << "Removing listener from class " -// //<< typeid(*eventListener).name() -// //<< " for " -// //<< EventTypeEnum::toName(listenForEventType) -// //<< std::endl; -// } -// else { -// updatedListeners.push_back(*iter); -// } -// } -// -// if (updatedListeners.size() != listeners.size()) { -// m_eventListeners[listenForEventType] = updatedListeners; -// } -// -// -// EVENT_LISTENER_CONTAINER processedListeners = m_eventProcessedListeners[listenForEventType]; -// -// /* -// * Remove the listener by creating a new container -// * of non-matching listeners. -// */ -// EVENT_LISTENER_CONTAINER updatedProcessedListeners; -// for (EVENT_LISTENER_CONTAINER_ITERATOR iter = processedListeners.begin(); -// iter != processedListeners.end(); -// iter++) { -// if (*iter == eventListener) { -// //std::cout << "Removing listener from class " -// //<< typeid(*eventListener).name() -// //<< " for " -// //<< EventTypeEnum::toName(listenForEventType) -// //<< std::endl; -// } -// else { -// updatedProcessedListeners.push_back(*iter); -// } -// } -// -// if (updatedProcessedListeners.size() != processedListeners.size()) { -// m_eventProcessedListeners[listenForEventType] = updatedProcessedListeners; -// } #elif CONTAINER_HASH_SET m_eventListeners[listenForEventType].erase(eventListener); m_eventProcessedListeners[listenForEventType].erase(eventListener); @@ -374,11 +310,6 @@ EventManager::sendEvent(Event* event) const AString eventNumberString = AString::number(m_eventIssuedCounter); - // Too many prints (JWH) - //AString msg = (eventMessagePrefix + " SENT."); - //CaretLogFiner(msg); - //std::cout << msg << std::endl; - /* * Send event to each of the listeners. */ @@ -386,14 +317,7 @@ EventManager::sendEvent(Event* event) iter != listeners.end(); iter++) { EventListenerInterface* listener = *iter; - - //std::cout << "Sending event from class " - //<< typeid(*listener).name() - //<< " for " - //<< EventTypeEnum::toName(eventType) - //<< std::endl; - - + listener->receiveEvent(event); if (event->isError()) { @@ -414,14 +338,6 @@ EventManager::sendEvent(Event* event) iter != processedListeners.end(); iter++) { EventListenerInterface* listener = *iter; - - //std::cout << "Sending event from class " - //<< typeid(*listener).name() - //<< " for " - //<< EventTypeEnum::toName(eventType) - //<< std::endl; - - listener->receiveEvent(event); if (event->isError()) { @@ -431,7 +347,6 @@ EventManager::sendEvent(Event* event) } } else { - // Too many prints (JWH) CaretLogFine("Event " + eventNumberString + " not processed: " + event->toString()); } m_eventIssuedCounter++; @@ -469,6 +384,7 @@ EventManager::sendSimpleEvent(const EventTypeEnum::Enum eventType) break; case EventTypeEnum::EVENT_ALERT_USER: case EventTypeEnum::EVENT_ANNOTATION_ADD_TO_REMOVE_FROM_FILE: + case EventTypeEnum::EVENT_ANNOTATION_CHART_LABEL_GET: case EventTypeEnum::EVENT_ANNOTATION_COLOR_BAR_GET: case EventTypeEnum::EVENT_ANNOTATION_CREATE_NEW_TYPE: case EventTypeEnum::EVENT_ANNOTATION_GET_DRAWN_IN_WINDOW: @@ -482,13 +398,21 @@ EventManager::sendSimpleEvent(const EventTypeEnum::Enum eventType) case EventTypeEnum::EVENT_BROWSER_TAB_GET_ALL_VIEWED: case EventTypeEnum::EVENT_BROWSER_TAB_INDICES_GET_ALL: case EventTypeEnum::EVENT_BROWSER_TAB_NEW: - case EventTypeEnum::EVENT_BROWSER_WINDOW_CONTENT_GET: + case EventTypeEnum::EVENT_BROWSER_WINDOW_CONTENT: case EventTypeEnum::EVENT_BROWSER_WINDOW_CREATE_TABS: + case EventTypeEnum::EVENT_BROWSER_WINDOW_DRAWING_CONTENT_GET: case EventTypeEnum::EVENT_BROWSER_WINDOW_GRAPHICS_HAVE_BEEN_REDRAWN: case EventTypeEnum::EVENT_BROWSER_WINDOW_NEW: + case EventTypeEnum::EVENT_BROWSER_WINDOW_TILE_TAB_OPERATION: + case EventTypeEnum::EVENT_CARET_DATA_FILES_GET: case EventTypeEnum::EVENT_CARET_MAPPABLE_DATA_FILES_GET: case EventTypeEnum::EVENT_CARET_MAPPABLE_DATA_FILE_MAPS_VIEWED_IN_OVERLAYS: + case EventTypeEnum::EVENT_CARET_PREFERENCES_GET: case EventTypeEnum::EVENT_CHART_MATRIX_YOKING_VALIDATION: + case EventTypeEnum::EVENT_CHART_OVERLAY_VALIDATE: + case EventTypeEnum::EVENT_CHART_TWO_ATTRIBUTES_CHANGED: + case EventTypeEnum::EVENT_CHART_TWO_AXIS_GET_DATA_RANGE: + case EventTypeEnum::EVENT_CHART_TWO_LOAD_LINE_SERIES_DATA: case EventTypeEnum::EVENT_DATA_FILE_ADD: case EventTypeEnum::EVENT_DATA_FILE_DELETE: case EventTypeEnum::EVENT_DATA_FILE_READ: @@ -498,6 +422,10 @@ EventManager::sendSimpleEvent(const EventTypeEnum::Enum eventType) case EventTypeEnum::EVENT_GET_OR_SET_USER_INPUT_MODE: case EventTypeEnum::EVENT_GET_TEXT_RENDERER_FOR_WINDOW: case EventTypeEnum::EVENT_GET_VIEWPORT_SIZE: + case EventTypeEnum::EVENT_GRAPHICS_OPENGL_CREATE_BUFFER_OBJECT: + case EventTypeEnum::EVENT_GRAPHICS_OPENGL_CREATE_TEXTURE_NAME: + case EventTypeEnum::EVENT_GRAPHICS_OPENGL_DELETE_BUFFER_OBJECT: + case EventTypeEnum::EVENT_GRAPHICS_OPENGL_DELETE_TEXTURE_NAME: case EventTypeEnum::EVENT_GRAPHICS_UPDATE_ALL_WINDOWS: case EventTypeEnum::EVENT_GRAPHICS_UPDATE_ONE_WINDOW: case EventTypeEnum::EVENT_HELP_VIEWER_DISPLAY: @@ -511,14 +439,16 @@ EventManager::sendSimpleEvent(const EventTypeEnum::Enum eventType) case EventTypeEnum::EVENT_MODEL_ADD: case EventTypeEnum::EVENT_MODEL_DELETE: case EventTypeEnum::EVENT_MODEL_GET_ALL: + case EventTypeEnum::EVENT_MODEL_GET_ALL_DISPLAYED: case EventTypeEnum::EVENT_MODEL_SURFACE_GET: case EventTypeEnum::EVENT_NODE_IDENTIFICATION_COLORS_GET_FROM_CHARTS: - case EventTypeEnum::EVENT_OPENGL_TEXTURE: + case EventTypeEnum::EVENT_OPENGL_OBJECT_TO_WINDOW_TRANSFORM: case EventTypeEnum::EVENT_OPERATING_SYSTEM_REQUEST_OPEN_DATA_FILE: case EventTypeEnum::EVENT_OVERLAY_SETTINGS_EDITOR_SHOW: case EventTypeEnum::EVENT_OVERLAY_VALIDATE: case EventTypeEnum::EVENT_PALETTE_COLOR_MAPPING_EDITOR_SHOW: case EventTypeEnum::EVENT_PALETTE_GET_BY_NAME: + case EventTypeEnum::EVENT_SHOW_FILE_DATA_READ_WARNING_DIALOG: case EventTypeEnum::EVENT_SPEC_FILE_READ_DATA_FILES: case EventTypeEnum::EVENT_SURFACE_COLORING_INVALIDATE: case EventTypeEnum::EVENT_SURFACES_GET: @@ -527,8 +457,8 @@ EventManager::sendSimpleEvent(const EventTypeEnum::Enum eventType) case EventTypeEnum::EVENT_USER_INTERFACE_UPDATE: case EventTypeEnum::EVENT_PROGRESS_UPDATE: case EventTypeEnum::EVENT_UPDATE_INFORMATION_WINDOWS: - case EventTypeEnum::EVENT_UPDATE_YOKED_WINDOWS: case EventTypeEnum::EVENT_UPDATE_VOLUME_EDITING_TOOLBAR: + case EventTypeEnum::EVENT_UPDATE_YOKED_WINDOWS: { const AString msg(EventTypeEnum::toName(eventType) + " has an special subclass of class Event and should never be sent as an event."); @@ -599,4 +529,35 @@ EventManager::getEventIssuedCounter() const return m_eventIssuedCounter; } +/** + * Verify that all listeners have been removed from the given event listener. + * + * @param eventListener + * The event listener. + */ +void +EventManager::verifyAllListenersRemoved(EventListenerInterface* eventListener) +{ + AString eventNames; + + for (int32_t i = 0; i < EventTypeEnum::EVENT_COUNT; i++) { + const EventTypeEnum::Enum eventType = static_cast(i); + if ((m_eventListeners[eventType].find(eventListener) != m_eventListeners[eventType].end()) + || (m_eventProcessedListeners[eventType].find(eventListener) != m_eventProcessedListeners[eventType].end())) { + eventNames.appendWithNewLine(" " + + EventTypeEnum::toName(eventType)); + } + } + + if ( ! eventNames.isEmpty()) { + SystemBacktrace myBacktrace; + SystemUtilities::getBackTrace(myBacktrace); + + CaretLogSevere("Failed to remove events from class instance. Event names:\n" + + eventNames + + ":\n" + + myBacktrace.toSymbolString()); + } +} + diff --git a/src/Common/EventManager.h b/src/Common/EventManager.h index 1b4056369f2c355f72e5a11fb2cb72984b712aa2..bc295eece3b472ce71947118ff201b805f7d3cb4 100644 --- a/src/Common/EventManager.h +++ b/src/Common/EventManager.h @@ -99,6 +99,8 @@ namespace caret { virtual ~EventManager(); + void verifyAllListenersRemoved(EventListenerInterface* eventListener); + /** * Define the container */ @@ -137,6 +139,8 @@ namespace caret { static EventManager* s_singletonEventManager; + friend EventListenerInterface; + }; #ifdef __EVENT_MANAGER_MAIN__ diff --git a/src/Common/EventOpenGLTexture.cxx b/src/Common/EventOpenGLTexture.cxx deleted file mode 100644 index 724f9a25a37460e5ca64da153a46afde7ab23acb..0000000000000000000000000000000000000000 --- a/src/Common/EventOpenGLTexture.cxx +++ /dev/null @@ -1,128 +0,0 @@ - -/*LICENSE_START*/ -/* - * Copyright (C) 2016 Washington University School of Medicine - * - * 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 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License along - * with this program; if not, write to the Free Software Foundation, Inc., - * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. - */ -/*LICENSE_END*/ - -#define __EVENT_OPENGL_TEXTURE_DECLARE__ -#include "EventOpenGLTexture.h" -#undef __EVENT_OPENGL_TEXTURE_DECLARE__ - -#include "CaretAssert.h" -#include "EventTypeEnum.h" - -using namespace caret; - - - -/** - * \class caret::EventOpenGLTexture - * \brief Event that assists with management of OpenGL textures. - * \ingroup Common - * - * When an object is destroyed that used texture resources, the - * texture resources need to be released. This event can be - * sent by the object to the texture manager to free these - * texture resources. - */ - -/** - * Constructor. - */ -EventOpenGLTexture::EventOpenGLTexture() -: Event(EventTypeEnum::EVENT_OPENGL_TEXTURE), -m_mode(MODE_NONE), -m_windowIndex(-1), -m_textureName(0) -{ -} - -/** - * Destructor. - */ -EventOpenGLTexture::~EventOpenGLTexture() -{ -} - -/** - * @return The mode. - */ -EventOpenGLTexture::Mode -EventOpenGLTexture::getMode() const -{ - return m_mode; -} - -/** - * Set the mode to delete all textures for a given window. - * - * @param windowIndex - * Index of the window. - */ -void -EventOpenGLTexture::setModeDeleteAllTexturesInWindow(const int32_t windowIndex) -{ - m_mode = MODE_DELETE_ALL_TEXTURES_IN_WINDOW; - m_windowIndex = windowIndex; -} - -/** - * Get the mode to delete all textures for a given window. - * - * @param windowIndex - * Index of the window. - */ -void -EventOpenGLTexture::getModeDeleteAllTexturesInWindow(int32_t windowIndexOut) const -{ - windowIndexOut = m_windowIndex; -} - -/** - * Set the mode to delete texture name for window. - * - * @param windowIndex - * Index of window in which texture is used. - * @param textureName - * OpenGL texture name. - */ -void -EventOpenGLTexture::setModeDeleteTexture(const int32_t windowIndex, - const int32_t textureName) -{ - m_mode = MODE_DELETE_TEXTURE; - m_windowIndex = windowIndex; - m_textureName = textureName; -} - -/** - * Get the mode to delete texture name for window. - * - * @param windowIndex - * Index of window in which texture is used. - * @param textureName - * OpenGL texture name. - */ -void -EventOpenGLTexture::getModeDeleteTexture(int32_t& windowIndexOut, - int32_t& textureNameOut) const -{ - windowIndexOut = m_windowIndex; - textureNameOut = m_textureName; -} - diff --git a/src/Common/EventOpenGLTexture.h b/src/Common/EventOpenGLTexture.h deleted file mode 100644 index accfbb47ca3b27155269562a134667ab8a597c76..0000000000000000000000000000000000000000 --- a/src/Common/EventOpenGLTexture.h +++ /dev/null @@ -1,78 +0,0 @@ -#ifndef __EVENT_OPENGL_TEXTURE_H__ -#define __EVENT_OPENGL_TEXTURE_H__ - -/*LICENSE_START*/ -/* - * Copyright (C) 2016 Washington University School of Medicine - * - * 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 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License along - * with this program; if not, write to the Free Software Foundation, Inc., - * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. - */ -/*LICENSE_END*/ - - -#include "Event.h" - - - -namespace caret { - - class EventOpenGLTexture : public Event { - - public: - enum Mode { - MODE_NONE, - MODE_DELETE_ALL_TEXTURES_IN_WINDOW, - MODE_DELETE_TEXTURE - }; - - EventOpenGLTexture(); - - virtual ~EventOpenGLTexture(); - - Mode getMode() const; - - void setModeDeleteAllTexturesInWindow(const int32_t windowIndex); - - void getModeDeleteAllTexturesInWindow(int32_t windowIndexOut) const; - - void setModeDeleteTexture(const int32_t windowIndex, - const int32_t textureName); - - void getModeDeleteTexture(int32_t& windowIndexOut, - int32_t& textureNameOut) const; - - // ADD_NEW_METHODS_HERE - - private: - EventOpenGLTexture(const EventOpenGLTexture&); - - EventOpenGLTexture& operator=(const EventOpenGLTexture&); - - Mode m_mode; - - int32_t m_windowIndex; - - int32_t m_textureName; - - // ADD_NEW_MEMBERS_HERE - - }; - -#ifdef __EVENT_OPENGL_TEXTURE_DECLARE__ - // -#endif // __EVENT_OPENGL_TEXTURE_DECLARE__ - -} // namespace -#endif //__EVENT_OPENGL_TEXTURE_H__ diff --git a/src/Common/EventTypeEnum.cxx b/src/Common/EventTypeEnum.cxx index 1bf2d1bc3d3be7e8807801340768bdb28bcdada9..8f460cfd3720d8869622670ad4ba9bb5582a9bf3 100644 --- a/src/Common/EventTypeEnum.cxx +++ b/src/Common/EventTypeEnum.cxx @@ -23,6 +23,7 @@ #undef __EVENT_TYPE_ENUM_DECLARE__ #include "CaretAssert.h" +#include "CaretLogger.h" using namespace caret; @@ -72,7 +73,11 @@ EventTypeEnum::initialize() enumData.push_back(EventTypeEnum(EVENT_ANNOTATION_ADD_TO_REMOVE_FROM_FILE, "EVENT_ANNOTATION_ADD_TO_REMOVE_FROM_FILE", "Event to add or remove an annotation from a file")); - + + enumData.push_back(EventTypeEnum(EVENT_ANNOTATION_CHART_LABEL_GET, + "EVENT_ANNOTATION_CHART_LABEL_GET", + "Event to get annotation chart labels")); + enumData.push_back(EventTypeEnum(EVENT_ANNOTATION_COLOR_BAR_GET, "EVENT_ANNOTATION_COLOR_BAR_GET", "Event to get annotation color bars from tab(s)")); @@ -129,8 +134,12 @@ EventTypeEnum::initialize() "EVENT_BROWSER_TAB_NEW", "Create a browser tab")); - enumData.push_back(EventTypeEnum(EVENT_BROWSER_WINDOW_CONTENT_GET, - "EVENT_BROWSER_WINDOW_CONTENT_GET", + enumData.push_back(EventTypeEnum(EVENT_BROWSER_WINDOW_CONTENT, + "EVENT_BROWSER_WINDOW_CONTENT", + "Event for browser window content")); + + enumData.push_back(EventTypeEnum(EVENT_BROWSER_WINDOW_DRAWING_CONTENT_GET, + "EVENT_BROWSER_WINDOW_DRAWING_CONTENT_GET", "Get the content in a browser window")); enumData.push_back(EventTypeEnum(EVENT_BROWSER_WINDOW_CREATE_TABS, @@ -149,18 +158,58 @@ EventTypeEnum::initialize() "EVENT_BROWSER_WINDOW_NEW", "Create a new browser window")); - enumData.push_back(EventTypeEnum(EVENT_CARET_MAPPABLE_DATA_FILES_GET, + enumData.push_back(EventTypeEnum(EVENT_BROWSER_WINDOW_TILE_TAB_OPERATION, + "EVENT_BROWSER_WINDOW_TILE_TAB_OPERATION", + "Browser Window Tile Tab Operation")); + + enumData.push_back(EventTypeEnum(EVENT_CARET_DATA_FILES_GET, + "EVENT_CARET_DATA_FILES_GET", + "Get all Caret data files")); + + enumData.push_back(EventTypeEnum(EVENT_CARET_MAPPABLE_DATA_FILES_GET, "EVENT_CARET_MAPPABLE_DATA_FILES_GET", "Get all Caret Mappable data files")); enumData.push_back(EventTypeEnum(EVENT_CARET_MAPPABLE_DATA_FILE_MAPS_VIEWED_IN_OVERLAYS, "EVENT_CARET_MAPPABLE_DATA_FILE_MAPS_VIEWED_IN_OVERLAYS", "Get Caret Mappable data file maps viewed in overlays")); + + enumData.push_back(EventTypeEnum(EVENT_CARET_PREFERENCES_GET, + "EVENT_CARET_PREFERENCES_GET", + "Get the Caret Preferences")); enumData.push_back(EventTypeEnum(EVENT_CHART_MATRIX_YOKING_VALIDATION, "EVENT_CHART_MATRIX_YOKING_VALIDATION", "Validate Yoking of matrix chart's rows/columns")); + enumData.push_back(EventTypeEnum(EVENT_GRAPHICS_OPENGL_CREATE_BUFFER_OBJECT, + "EVENT_GRAPHICS_OPENGL_CREATE_BUFFER_OBJECT", + "Create an OpenGL Buffer Object for an OpenGL Context")); + enumData.push_back(EventTypeEnum(EVENT_GRAPHICS_OPENGL_CREATE_TEXTURE_NAME, + "EVENT_GRAPHICS_OPENGL_CREATE_TEXTURE_NAME", + "Create an OpenGL Texture Name for an OpenGL Context")); + enumData.push_back(EventTypeEnum(EVENT_GRAPHICS_OPENGL_DELETE_BUFFER_OBJECT, + "EVENT_GRAPHICS_OPENGL_DELETE_BUFFER_OBJECT", + "Delete an OpenGL Buffer Object for an OpenGL Context")); + enumData.push_back(EventTypeEnum(EVENT_GRAPHICS_OPENGL_DELETE_TEXTURE_NAME, + "EVENT_GRAPHICS_OPENGL_DELETE_TEXTURE_NAME", + "Delete an OpenGL Texture Name for an OpenGL Context")); + enumData.push_back(EventTypeEnum(EVENT_CHART_OVERLAY_VALIDATE, + "EVENT_CHART_OVERLAY_VALIDATE", + "Validate a chart overlay for validity (it exists)")); + + enumData.push_back(EventTypeEnum(EVENT_CHART_TWO_ATTRIBUTES_CHANGED, + "EVENT_CHART_TWO_ATTRIBUTES_CHANGED", + "GUI notification of the change in chart two atttributes")); + + enumData.push_back(EventTypeEnum(EVENT_CHART_TWO_AXIS_GET_DATA_RANGE, + "EVENT_CHART_TWO_AXIS_GET_DATA_RANGE", + "Get the range of data for a chart two axis")); + + enumData.push_back(EventTypeEnum(EVENT_CHART_TWO_LOAD_LINE_SERIES_DATA, + "EVENT_CHART_TWO_LOAD_LINE_SERIES_DATA", + "Load line series data for chart two implementation")); + enumData.push_back(EventTypeEnum(EVENT_DATA_FILE_ADD, "EVENT_DATA_FILE_ADD", "Add a data file to the Brain")); @@ -249,7 +298,11 @@ EventTypeEnum::initialize() "EVENT_MODEL_GET_ALL", "Get all models")); - enumData.push_back(EventTypeEnum(EVENT_MODEL_SURFACE_GET, + enumData.push_back(EventTypeEnum(EVENT_MODEL_GET_ALL_DISPLAYED, + "EVENT_MODEL_GET_ALL_DISPLAYED", + "Get all displayed models")); + + enumData.push_back(EventTypeEnum(EVENT_MODEL_SURFACE_GET, "EVENT_MODEL_SURFACE_GET", "Get a specific model surface")); @@ -257,9 +310,9 @@ EventTypeEnum::initialize() "EVENT_NODE_IDENTIFICATION_COLORS_GET_FROM_CHARTS", "Get the color for node identification symbols from all charts that contain nodes")); - enumData.push_back(EventTypeEnum(EVENT_OPENGL_TEXTURE, - "EVENT_OPENGL_TEXTURE", - "OpenGL Texture Event")); + enumData.push_back(EventTypeEnum(EVENT_OPENGL_OBJECT_TO_WINDOW_TRANSFORM, + "EVENT_OPENGL_OBJECT_TO_WINDOW_TRANSFORM", + "Get transformation for converting object coordinates to window coordinates")); enumData.push_back(EventTypeEnum(EVENT_OPERATING_SYSTEM_REQUEST_OPEN_DATA_FILE, "EVENT_OPERATING_SYSTEM_REQUEST_OPEN_DATA_FILE", @@ -281,6 +334,10 @@ EventTypeEnum::initialize() "EVENT_PALETTE_GET_BY_NAME", "Read the selected files in a spec file")); + enumData.push_back(EventTypeEnum(EVENT_SHOW_FILE_DATA_READ_WARNING_DIALOG, + "EVENT_SHOW_FILE_DATA_READ_WARNING_DIALOG", + "Show a dialog with warnings encountered reading data files")); + enumData.push_back(EventTypeEnum(EVENT_SPEC_FILE_READ_DATA_FILES, "EVENT_SPEC_FILE_READ_DATA_FILES", "Read the selected data files in a spec file")); @@ -315,8 +372,8 @@ EventTypeEnum::initialize() enumData.push_back(EventTypeEnum(EVENT_UPDATE_YOKED_WINDOWS, "EVENT_UPDATE_YOKED_WINDOWS", - "Update yoked windows graphics and toolbar")); - + "Update yoked windows")); + enumData.push_back(EventTypeEnum(EVENT_UPDATE_VOLUME_EDITING_TOOLBAR, "EVENT_UPDATE_VOLUME_EDITING_TOOLBAR", "Update the volume editing toolbar")); @@ -354,7 +411,11 @@ EventTypeEnum::findData(const Enum enumValue) } } - return NULL; + AString msg("Failed to find EventTypeEnum for an enumerated value. The most likely causes is a failure " + "to add the enumerated value in EventTypeEnum::initialize()"); + CaretAssertMessage(0, msg); + CaretLogSevere(msg); + return &enumData[0]; // prevent crash } /** diff --git a/src/Common/EventTypeEnum.h b/src/Common/EventTypeEnum.h index a27272f3d38f474707086155ee2be9b9de0b7190..4c4cab54421b909293b3e4fa5933ca49abcbbebb 100644 --- a/src/Common/EventTypeEnum.h +++ b/src/Common/EventTypeEnum.h @@ -42,6 +42,8 @@ public: EVENT_ALERT_USER, /** Add annotation to or remove from a file */ EVENT_ANNOTATION_ADD_TO_REMOVE_FROM_FILE, + /** Get annnotation chart labels */ + EVENT_ANNOTATION_CHART_LABEL_GET, /** Get color bars from tab(s) */ EVENT_ANNOTATION_COLOR_BAR_GET, /** Annotation create new of a particular type */ @@ -70,8 +72,10 @@ public: EVENT_BROWSER_TAB_GET_ALL_VIEWED, /** Create a new browser tab */ EVENT_BROWSER_TAB_NEW, + /** Event for browser window content */ + EVENT_BROWSER_WINDOW_CONTENT, /** Get the content of a browser window */ - EVENT_BROWSER_WINDOW_CONTENT_GET, + EVENT_BROWSER_WINDOW_DRAWING_CONTENT_GET, /** Create tabs after loading a file */ EVENT_BROWSER_WINDOW_CREATE_TABS, /** Issued after a browser window's graphicshave been redrawn */ @@ -80,12 +84,26 @@ public: EVENT_BROWSER_WINDOW_MENUS_UPDATE, /** Create a new browser window */ EVENT_BROWSER_WINDOW_NEW, + /** Browser tile tab operations */ + EVENT_BROWSER_WINDOW_TILE_TAB_OPERATION, + /** Get caret data files */ + EVENT_CARET_DATA_FILES_GET, /** Get CaretMappable data files */ EVENT_CARET_MAPPABLE_DATA_FILES_GET, /** Get CaretMappableDataFiles and their maps viewed as overlays */ EVENT_CARET_MAPPABLE_DATA_FILE_MAPS_VIEWED_IN_OVERLAYS, + /** Event to get the Caret Preferences */ + EVENT_CARET_PREFERENCES_GET, /** Event for yoking the loading of matrix chart rows/columns */ EVENT_CHART_MATRIX_YOKING_VALIDATION, + /** Validate that chart overlay is valid (it exists). */ + EVENT_CHART_OVERLAY_VALIDATE, + /** GUI notification of the change in chart two atttributes */ + EVENT_CHART_TWO_ATTRIBUTES_CHANGED, + /** Get the range of data for a chart two axis */ + EVENT_CHART_TWO_AXIS_GET_DATA_RANGE, + /** Load chart two line series data */ + EVENT_CHART_TWO_LOAD_LINE_SERIES_DATA, /** Add a data file into the Brain*/ EVENT_DATA_FILE_ADD, /** Delete a data file from the brain */ @@ -104,6 +122,14 @@ public: EVENT_GET_TEXT_RENDERER_FOR_WINDOW, /** Get the viewport size for model, tab, window */ EVENT_GET_VIEWPORT_SIZE, + /** Create a buffer object for an OpenGL context */ + EVENT_GRAPHICS_OPENGL_CREATE_BUFFER_OBJECT, + /** Create a texture name for an OpenGL context */ + EVENT_GRAPHICS_OPENGL_CREATE_TEXTURE_NAME, + /** Delete a buffer object for an OpenGL context */ + EVENT_GRAPHICS_OPENGL_DELETE_BUFFER_OBJECT, + /** Delete a texture name for an OpenGL context */ + EVENT_GRAPHICS_OPENGL_DELETE_TEXTURE_NAME, /** Update all graphics windows */ EVENT_GRAPHICS_UPDATE_ALL_WINDOWS, /** Update graphics in a window */ @@ -130,12 +156,14 @@ public: EVENT_MODEL_DELETE, /** model - get all*/ EVENT_MODEL_GET_ALL, + /** model - get all displayed */ + EVENT_MODEL_GET_ALL_DISPLAYED, /** model surface - get */ EVENT_MODEL_SURFACE_GET, /** Get the color for a node's identification symbol from a chart that contains the node */ EVENT_NODE_IDENTIFICATION_COLORS_GET_FROM_CHARTS, - /** OpenGL Texture related event */ - EVENT_OPENGL_TEXTURE, + /** Get the transformation for converting object coordinates to window coordinates */ + EVENT_OPENGL_OBJECT_TO_WINDOW_TRANSFORM, /** open file request from the operating system (Mac only) for now */ EVENT_OPERATING_SYSTEM_REQUEST_OPEN_DATA_FILE, /** request display of overlay settings editor */ @@ -146,6 +174,8 @@ public: EVENT_PALETTE_COLOR_MAPPING_EDITOR_SHOW, /** Get a palette by name from a palette file */ EVENT_PALETTE_GET_BY_NAME, + /** Show a dialog containing warnings encountered when reading data files */ + EVENT_SHOW_FILE_DATA_READ_WARNING_DIALOG, /** Read the selected files in a spec file */ EVENT_SPEC_FILE_READ_DATA_FILES, /** Invalidate surface coloring */ @@ -162,10 +192,10 @@ public: EVENT_PROGRESS_UPDATE, /** Update the information windows */ EVENT_UPDATE_INFORMATION_WINDOWS, - /** Event to update yoked windows (graphics and toolbar) */ - EVENT_UPDATE_YOKED_WINDOWS, /** Update the volume editing toolbar */ EVENT_UPDATE_VOLUME_EDITING_TOOLBAR, + /** Update yoked windows */ + EVENT_UPDATE_YOKED_WINDOWS, /* THIS MUST ALWAYS BE LAST - NOT an event type but is number of event types */ EVENT_COUNT }; diff --git a/src/Common/FileInformation.cxx b/src/Common/FileInformation.cxx index 397fe37e0914b15cf65a154f92b4887d705b588f..7f15a2ee821304a1f89e477b9a61f8ea32eb829c 100644 --- a/src/Common/FileInformation.cxx +++ b/src/Common/FileInformation.cxx @@ -565,12 +565,95 @@ FileInformation::assembleFileComponents(const AString& pathName, } name += fileNameWithoutExtension; if ( ! extensionWithoutDot.isEmpty()) { - name += ("." + extensionWithoutDot); + /* + * User might include dot at beginning of extension + */ + if (extensionWithoutDot.startsWith(".")) { + name += extensionWithoutDot; + } + else { + name += ("." + extensionWithoutDot); + } } return name; } +/** + * Assemble the file components into a file path and name. + * + * @param pathName + * Path for file (may be absolute, relative, or empty). + * @param fileNameWithExtension + * Name of file with extension. + */ +AString +FileInformation::assembleFileComponents(const AString& pathName, + const AString& fileNameWithExtension) +{ + AString name; + if ( ! pathName.isEmpty()) { + name += (pathName + "/"); + } + name += fileNameWithExtension; + + return name; +} + +/** + * Create the file path for a file with the given name in the + * computer system's temporary directory. + * + * @param fileName + * Name of the file with an extension. Any path in the name is ignored. + * @return + * File path in the temporary directory with the given file name. + */ +AString +FileInformation::createTemporaryFilePathName(const AString& fileName) +{ + AString nameOut; + + FileInformation fileInfo(fileName); + AString nameNoPath = fileInfo.getFileName(); + if (nameNoPath.isEmpty()) { + nameOut = FileInformation::assembleFileComponents(SystemUtilities::getTempDirectory(), "name_missing.tmp"); + } + else { + nameOut = FileInformation::assembleFileComponents(SystemUtilities::getTempDirectory(), nameNoPath); + } + + return nameOut; +} + +/** + * Replace the extension in a file name. + * + * @param fileName + * Name of file + * @param newExtension + * New extension that replaces the extension in fileName + * @return + * fileName with extension replaced with newExtension. + */ +AString +FileInformation::replaceExtension(const AString& fileName, + const AString& newExtension) +{ + AString thePath, theName, theExtension; + + FileInformation fileInfo(fileName); + fileInfo.getFileComponents(thePath, + theName, + theExtension); + + const AString nameOut = assembleFileComponents(thePath, + theName, + newExtension); + return nameOut; +} + + /** * Convert, if needed, the file information to a local, absolute path. * diff --git a/src/Common/FileInformation.h b/src/Common/FileInformation.h index 19cd55ff50fc7fc11772ff87f98c61584e22de9c..5899951925b91eece5c727c55ff8ad01c7496c16 100644 --- a/src/Common/FileInformation.h +++ b/src/Common/FileInformation.h @@ -96,6 +96,14 @@ namespace caret { const AString& fileNameWithoutExtension, const AString& extensionWithoutDot); + static AString assembleFileComponents(const AString& pathName, + const AString& fileNameWithExtension); + + static AString createTemporaryFilePathName(const AString& fileNameWithExtension); + + static AString replaceExtension(const AString& fileName, + const AString& newExtension); + bool remove(); void getRemoteUrlUsernameAndPassword(AString& urlOut, diff --git a/src/Common/FloatMatrix.cxx b/src/Common/FloatMatrix.cxx index f3b5569fe3d5cd958c5ab1b101d60634df4857b9..9a7764433472a81b9bb3e42a507e9fade90f7d9b 100644 --- a/src/Common/FloatMatrix.cxx +++ b/src/Common/FloatMatrix.cxx @@ -21,6 +21,7 @@ #include "CaretAssert.h" #include "CaretException.h" +#include "CaretLogger.h" #include "FloatMatrix.h" #include "MatrixFunctions.h" @@ -232,6 +233,33 @@ FloatMatrix FloatMatrix::transpose() const return ret; } +float FloatMatrix::determinant() const +{ + int64_t numRows = getNumberOfRows(), numCols = getNumberOfColumns(); + if (numRows != numCols) throw CaretException("determinant() called on non-square matrix"); + if (numRows == 0) return 1;//whatever + if (numRows == 1) return m_matrix[0][0]; + if (numRows == 2) return m_matrix[0][0] * m_matrix[1][1] - m_matrix[0][1] * m_matrix[1][0]; + if (numRows == 3) return m_matrix[0][0] * m_matrix[1][1] * m_matrix[2][2] + + m_matrix[0][1] * m_matrix[1][2] * m_matrix[2][0] + + m_matrix[0][2] * m_matrix[1][0] * m_matrix[2][1] - + m_matrix[0][0] * m_matrix[1][2] * m_matrix[2][1] - + m_matrix[0][1] * m_matrix[1][0] * m_matrix[2][2] - + m_matrix[0][2] * m_matrix[1][1] * m_matrix[2][0]; + if (numRows > 7) + { + CaretLogWarning("determinant() called on matrix with size " + AString::number(numRows) + ", current algorithm is slow, O(n!) recursive"); + } + double ret = 0.0;//replace with LU decomposition if we need it before adding a real matrix library + for (int64_t i = 0; i < numRows; ++i)//warning, O(n!) runtime + { + float minor = this->getRange(0, i, 1, numCols).concatVert(this->getRange(i + 1, numRows, 1, numCols)).determinant(), + factor = (i % 2 ? -1.0f : 1.0f); + ret += minor * factor; + } + return ret; +} + FloatMatrix FloatMatrix::zeros(const int64_t rows, const int64_t cols) { FloatMatrix ret; diff --git a/src/Common/FloatMatrix.h b/src/Common/FloatMatrix.h index a5332ab14a345c3f90b788412d69bc223fd5bea5..cafbbcb7e10ad02d26d4d7eac7b6a0474edb5430 100644 --- a/src/Common/FloatMatrix.h +++ b/src/Common/FloatMatrix.h @@ -85,14 +85,10 @@ namespace caret { FloatMatrix reducedRowEchelon() const; ///return the transpose FloatMatrix transpose() const; + ///determinant - warning, uses slow O(n!) recursive algorithm + float determinant() const; ///resize the matrix - keeps contents within bounds unless destructive is true (destructive is faster) void resize(const int64_t rows, const int64_t cols, const bool destructive = false); - ///return a matrix of zeros - static FloatMatrix zeros(const int64_t rows, const int64_t cols); - ///return a matrix of ones - static FloatMatrix ones(const int64_t rows, const int64_t cols); - ///return square identity matrix - static FloatMatrix identity(const int64_t rows); ///get the range of values from first until one before afterLast, as a new matrix FloatMatrix getRange(const int64_t firstRow, const int64_t afterLastRow, const int64_t firstCol, const int64_t afterLastCol) const; ///return a matrix formed by concatenating right to the right of this @@ -106,13 +102,20 @@ namespace caret { ///separate 3x4 or 4x4 into Vector3Ds, throw on wrong dimensions void getAffineVectors(Vector3D& xvec, Vector3D& yvec, Vector3D& zvec, Vector3D& offset) const; ///get number of rows - int64_t getNumberOfRows() { return (int64_t)m_matrix.size(); } + int64_t getNumberOfRows() const { return (int64_t)m_matrix.size(); } ///get number of columns - int64_t getNumberOfColumns() + int64_t getNumberOfColumns() const { if (m_matrix.size() == 0) return 0; return (int64_t)m_matrix[0].size(); } + + ///return a matrix of zeros + static FloatMatrix zeros(const int64_t rows, const int64_t cols); + ///return a matrix of ones + static FloatMatrix ones(const int64_t rows, const int64_t cols); + ///return square identity matrix + static FloatMatrix identity(const int64_t rows); }; } diff --git a/src/Common/Histogram.cxx b/src/Common/Histogram.cxx index 3f08b7529353187e631ed434568cb5fed6bb52b6..8f3887a38ab6a57e029d4376a3b2a9f04322fdd2 100644 --- a/src/Common/Histogram.cxx +++ b/src/Common/Histogram.cxx @@ -73,6 +73,9 @@ void Histogram::reset() m_cumulative[i] = 0; m_display[i] = 0.0f; } + m_displayHeightMax = 0.0; + m_bucketMin = 0.0; + m_bucketMax = 0.0; } void Histogram::update(const int& numBuckets, const float* data, const int64_t& dataCount) @@ -170,12 +173,27 @@ void Histogram::update(const float* data, const int64_t& dataCount) ++m_buckets[bucket]; } computeCumulative(); + m_displayHeightMax = 0.0; for (int i = 0; i < numBuckets; ++i) {//compute display values by normalizing by bucket size m_display[i] = m_buckets[i] / bucketsize; + if (m_display[i] > m_displayHeightMax) { + m_displayHeightMax = m_display[i]; + } } } +void Histogram::update(const int32_t& numBuckets, + const float* data, const int64_t& dataCount, float mostPositiveValueInclusive, + float leastPositiveValueInclusive, float leastNegativeValueInclusive, + float mostNegativeValueInclusive, const bool& includeZeroValues) +{ + resize(numBuckets); + update(data, dataCount, mostPositiveValueInclusive, + leastPositiveValueInclusive, leastNegativeValueInclusive, + mostNegativeValueInclusive, includeZeroValues); +} + void Histogram::update(const float* data, const int64_t& dataCount, float mostPositiveValueInclusive, float leastPositiveValueInclusive, float leastNegativeValueInclusive, float mostNegativeValueInclusive, const bool& includeZeroValues) @@ -298,9 +316,13 @@ void Histogram::update(const float* data, const int64_t& dataCount, float mostPo ++m_buckets[bucket]; } computeCumulative(); + m_displayHeightMax = 0.0; for (int i = 0; i < numBuckets; ++i) {//compute display values by normalizing by bucket size m_display[i] = m_buckets[i] / bucketsize; + if (m_display[i] > m_displayHeightMax) { + m_displayHeightMax = m_display[i]; + } } } @@ -314,3 +336,49 @@ void Histogram::computeCumulative() m_cumulative[i] = accum; } } + +/** + * Get the data value and height for the histogram's bucket index. + * + * @param bucketIndex + * The bucket index. + * @param bucketDataValueOut + * Output with data value at the bucket (x-axis) + * @param bucketHeightOut + * Output with height of bucket (y-axis) + * @return + * True if output values are positive, else false. + */ +bool +Histogram::getHistogramDisplayBucketDataValueAndHeight( const int32_t bucketIndex, + float& bucketDataValueOut, + float& bucketHeightOut) const +{ + bucketDataValueOut = 0.0; + bucketHeightOut = 0.0; + + const std::vector& buckets = getHistogramDisplay(); + const int32_t numberOfBuckets = static_cast(buckets.size()); + if ((bucketIndex >= 0) + && (bucketIndex < numberOfBuckets)) { + CaretAssertVectorIndex(buckets, + bucketIndex); + + float rangeMin = 0.0; + float rangeMax = 0.0; + getRange(rangeMin, rangeMax); + bucketDataValueOut = rangeMin; + if (rangeMax > rangeMin) { + const float range = rangeMax - rangeMin; + const float bucketWidth = range / numberOfBuckets; + bucketDataValueOut = (rangeMin + + (bucketIndex * bucketWidth)); + } + + bucketHeightOut = buckets[bucketIndex]; + + return true; + } + + return false; +} diff --git a/src/Common/Histogram.h b/src/Common/Histogram.h index d7570ff978d1561dfb5e344f9f383b96b67fe326..51b2d46ae199831c490acc99ed343b194c82f389 100644 --- a/src/Common/Histogram.h +++ b/src/Common/Histogram.h @@ -32,6 +32,8 @@ namespace caret std::vector m_buckets, m_cumulative; std::vector m_display; float m_bucketMin, m_bucketMax; + float m_displayHeightMax; + ///counts of each class of number int64_t m_posCount, m_zeroCount, m_negCount, m_infCount, m_negInfCount, m_nanCount; @@ -41,6 +43,16 @@ namespace caret void computeCumulative(); + void update(const float* data, + const int64_t& dataCount, + float mostPositiveValueInclusive, + float leastPositiveValueInclusive, + float leastNegativeValueInclusive, + float mostNegativeValueInclusive, + const bool& includeZeroValues); + + void update(const float* data, const int64_t& dataCount); + public: Histogram(const int& numBuckets = 100); @@ -48,11 +60,10 @@ namespace caret Histogram(const int& numBuckets, const float* data, const int64_t& dataCount); - void update(const float* data, const int64_t& dataCount); - void update(const int& numBuckets, const float* data, const int64_t& dataCount); - void update(const float* data, + void update(const int32_t& numBuckets, + const float* data, const int64_t& dataCount, float mostPositiveValueInclusive, float leastPositiveValueInclusive, @@ -68,6 +79,10 @@ namespace caret ///get display values - counts divided by bucket widths - will be consistent on the same data regardless of number of buckets or const std::vector& getHistogramDisplay() const { return m_display; } + bool getHistogramDisplayBucketDataValueAndHeight( const int32_t bucketIndex, + float& bucketDataValueOut, + float& bucketHeightOut) const; + int getNumberOfBuckets() const { return (int)m_buckets.size(); } void getCounts(int64_t& posCount, int64_t& zeroCount, int64_t& negCount, int64_t& infCount, int64_t& negInfCount, int64_t& nanCount) const @@ -80,6 +95,17 @@ namespace caret nanCount = m_nanCount; } + /** + * Get the low edge of the low bucket (x-min), and the high edge of the high bucket (x-max) + * and the maximum display height (y-max). Low Y is always zero. + */ + void getRangeAndMaxDisplayHeight(float& histMin, float& histMax, float& displayHeightMax) const + { + histMin = m_bucketMin; + histMax = m_bucketMax; + displayHeightMax = m_displayHeightMax; + } + ///returns the low edge of the low bucket, and the high edge of the high bucket void getRange(float& histMin, float& histMax) const { diff --git a/src/Common/ImageCaptureMethodEnum.cxx b/src/Common/ImageCaptureMethodEnum.cxx index 0c563af67212e0a8e40eb67ce89de2901209552e..fd6ee9889e6bec2982e36a3082444a1537260d1d 100644 --- a/src/Common/ImageCaptureMethodEnum.cxx +++ b/src/Common/ImageCaptureMethodEnum.cxx @@ -114,6 +114,9 @@ ImageCaptureMethodEnum::initialize() "IMAGE_CAPTURE_WITH_GRAB_FRAME_BUFFER", "Grab Frame Buffer")); + enumData.push_back(ImageCaptureMethodEnum(IMAGE_CAPTURE_WITH_OFFSCREEN_FRAME_BUFFER, + "IMAGE_CAPTURE_WITH_OFFSCREEN_FRAME_BUFFER", + "Offscreen Frame Buffer")); } /** diff --git a/src/Common/ImageCaptureMethodEnum.h b/src/Common/ImageCaptureMethodEnum.h index 72c42714360c110342610067e705b2f3b099a4c1..0fb1c4675b5e9299895fd1e24ef1a2a6712d3477 100644 --- a/src/Common/ImageCaptureMethodEnum.h +++ b/src/Common/ImageCaptureMethodEnum.h @@ -38,7 +38,9 @@ public: /** Capture the image with QGLWidget::renderPixmap() */ IMAGE_CAPTURE_WITH_RENDER_PIXMAP, /** Capture the image with QGLWidget::grabFrameBuffer() */ - IMAGE_CAPTURE_WITH_GRAB_FRAME_BUFFER + IMAGE_CAPTURE_WITH_GRAB_FRAME_BUFFER, + /** Capture with QOffscreenSurface and QOpenGLFramebufferObject */ + IMAGE_CAPTURE_WITH_OFFSCREEN_FRAME_BUFFER }; diff --git a/src/Common/JsonHelper.cxx b/src/Common/JsonHelper.cxx new file mode 100644 index 0000000000000000000000000000000000000000..b5d05dc8b5cd55426dc6ae01bf159a728593c4a2 --- /dev/null +++ b/src/Common/JsonHelper.cxx @@ -0,0 +1,130 @@ + +/*LICENSE_START*/ +/* + * Copyright (C) 2017 Washington University School of Medicine + * + * 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 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ +/*LICENSE_END*/ + +#define __JSON_HELPER_DECLARE__ +#include "JsonHelper.h" +#undef __JSON_HELPER_DECLARE__ + +#include +#include +#include +#include + +#include "CaretAssert.h" +using namespace caret; + + + +/** + * \class caret::JsonHelper + * \brief Helper class for JSON processing. + * \ingroup Common + */ + +/** + * Constructor. + */ +JsonHelper::JsonHelper() +{ + +} + +/** + * Destructor. + */ +JsonHelper::~JsonHelper() +{ +} + +/** + * Convert the JSON Array to a String representation + * + * @param jsonValue + * The JSON Value + * @return + * The string representation of the value. + */ +AString +JsonHelper::jsonArrayToString(const QJsonArray& jsonArray) +{ + const QJsonDocument contentJson = QJsonDocument(jsonArray); + const AString s = contentJson.toJson(QJsonDocument::Compact); + return s; +} + + +/** + * Determine type of content in the value and return its string representation + * + * @param jsonValue + * The JSON Value + * @return + * The string representation of the value. + */ +AString +JsonHelper::jsonValueToString(const QJsonValue& jsonValue) +{ + AString typeString("Invalid: "); + AString contentString; + QJsonDocument contentJson; + + switch (jsonValue.type()) { + case QJsonValue::Array: + typeString = "Array: "; + contentJson = QJsonDocument(jsonValue.toArray()); + break; + case QJsonValue::Bool: + typeString = "Bool: "; + contentString = AString::fromBool(jsonValue.toBool()); + break; + case QJsonValue::Double: + typeString = "Double: "; + contentString = AString::fromBool(jsonValue.toDouble()); + break; + case QJsonValue::Null: + typeString = "Null: "; + contentString = ""; + break; + case QJsonValue::Object: + typeString = "Object: "; + contentJson = QJsonDocument(jsonValue.toObject()); + break; + case QJsonValue::String: + typeString = "String: "; + contentString = jsonValue.toString(); + break; + case QJsonValue::Undefined: + typeString = "Undefined: "; + contentString = ""; + break; + } + + AString s(typeString); + + if (contentJson.isNull()) { + s.append(contentString); + } + else { + s.append(contentJson.toJson(QJsonDocument::Compact)); + } + + return s; +} diff --git a/src/Common/JsonHelper.h b/src/Common/JsonHelper.h new file mode 100644 index 0000000000000000000000000000000000000000..c307fc0a52c3c5d75699e4c3993f6b96c0d8d6f5 --- /dev/null +++ b/src/Common/JsonHelper.h @@ -0,0 +1,64 @@ +#ifndef __JSON_HELPER_H__ +#define __JSON_HELPER_H__ + +/*LICENSE_START*/ +/* + * Copyright (C) 2017 Washington University School of Medicine + * + * 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 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ +/*LICENSE_END*/ + + + +#include + +#include "AString.h" + +class QJsonArray; +class QJsonValue; + + +namespace caret { + + class JsonHelper { + + public: + static AString jsonArrayToString(const QJsonArray& jsonArray); + + static AString jsonValueToString(const QJsonValue& jsonValue); + + + // ADD_NEW_METHODS_HERE + + private: + JsonHelper(); + + virtual ~JsonHelper(); + + JsonHelper(const JsonHelper&); + + JsonHelper& operator=(const JsonHelper&); + + // ADD_NEW_MEMBERS_HERE + + }; + +#ifdef __JSON_HELPER_DECLARE__ + // +#endif // __JSON_HELPER_DECLARE__ + +} // namespace +#endif //__JSON_HELPER_H__ diff --git a/src/Common/Logger.cxx b/src/Common/Logger.cxx index a142d48a06a54ef2475f7853496fb1290ec41551..2836a17f6b7885c93ee98548d73df214e7050119 100644 --- a/src/Common/Logger.cxx +++ b/src/Common/Logger.cxx @@ -74,7 +74,7 @@ Logger::getLogger(const AString& name) } Logger* logger = new Logger(name); - bool exists = LogManager::getLogManager()->addLogger(logger); + CaretUsedInDebugCompileOnly(bool exists = LogManager::getLogManager()->addLogger(logger)); CaretAssertMessage(exists, "Trying to add logger and logger with name exists."); return logger; } diff --git a/src/Common/MathFunctions.cxx b/src/Common/MathFunctions.cxx index 5ee6a76034620f3b38ce1ff9f78f2abd8e2f40d2..f741aebcd7a04919d2fc9bec32fda3c11885c9b6 100644 --- a/src/Common/MathFunctions.cxx +++ b/src/Common/MathFunctions.cxx @@ -778,8 +778,28 @@ MathFunctions::triangleAreaSigned2D( const float p2[2], const float p3[2]) { - float area = ( p1[0]*p2[1] + p2[0]*p3[1] + p3[0]*p1[1] - - p1[1]*p2[0] - p2[1]*p3[0] - p3[1]*p1[0] ) * 0.5f; + /* + * Prior to 07 Dec 2017, this area calculation was performed + * using floats. However, when a triangle edge was very small, + * the area calculation was incorrect (did not match the area + * produced by triangleAreaSigned3D() and often with the incorrect + * sign). Using double precision values corrects the problem. + * + * When calculated as floats in this equation, changing the order + * even affected the area calculation: + * + * float area = ( p1[0]*p2[1] + p2[0]*p3[1] + p3[0]*p1[1] + * - p1[1]*p2[0] - p2[1]*p3[0] - p3[1]*p1[0] ) * 0.5f; + */ + + const double x1 = p1[0]; + const double y1 = p1[1]; + const double x2 = p2[0]; + const double y2 = p2[1]; + const double x3 = p3[0]; + const double y3 = p3[1]; + const double area = ( x1*y2 + x2*y3 + x3*y1 + - y1*x2 - y2*x3 - y3*x1) * 0.5; return area; } @@ -1373,8 +1393,6 @@ MathFunctions::vtkPerpendiculars(const double x[3], double y[3], double z[3], } } - - /** * Determine if 2D line segments intersect. * Algorithm from http://mathworld.wolfram.com/Line-LineIntersection.html @@ -1388,16 +1406,17 @@ MathFunctions::vtkPerpendiculars(const double x[3], double y[3], double z[3], * parameter to 0.01. * @param intersectionOut Location of intersection. * @return true if the line segments intersect else false. - * + * The intersection MUST BE within the range of + * each of the line segments. */ bool MathFunctions::lineIntersection2D( - const float p1[2], - const float p2[2], - const float q1[2], - const float q2[2], - const float tolerance, - float intersectionOut[2]) + const float p1[2], + const float p2[2], + const float q1[2], + const float q2[2], + const float tolerance, + float intersectionOut[2]) { double tol = tolerance; double x1 = p1[0]; @@ -1439,6 +1458,57 @@ MathFunctions::lineIntersection2D( return false; } + +/** + * Determine if 2D vectors intersect. + * Algorithm from http://mathworld.wolfram.com/Line-LineIntersection.html + * + * @param p1 Vector 1 end point 1. + * @param p2 Vector 1 end point 2. + * @param q1 Vector 2 end point 1. + * @param q2 Vector 2 end point 2. + * @param tolerance Tolerance around the vertices (essentially + * lengthens lines by this quantity). Caret5 set this + * parameter to 0.01. + * @param intersectionOut Location of intersection. + * @return true if the vectors intersect. + */ +bool +MathFunctions::vectorIntersection2D( + const float p1[2], + const float p2[2], + const float q1[2], + const float q2[2], + const float /*tolerance*/, + float intersectionOut[2]) +{ +// double tol = tolerance; + double x1 = p1[0]; + double y1 = p1[1]; + double x2 = p2[0]; + double y2 = p2[1]; + + double x3 = q1[0]; + double y3 = q1[1]; + double x4 = q2[0]; + double y4 = q2[1]; + + double denom = ((x1 - x2) * (y3 - y4)) - ((x3 - x4) * (y1 - y2)); + + if (denom != 0.0) { + double a = (x1 * y2) - (x2 * y1); + double c = (x3 * y4) - (x4 * y3); + double x = ((a * (x3 - x4)) - (c * (x1 - x2))) / denom; + double y = ((a * (y3 - y4)) - (c * (y1 - y2))) / denom; + + intersectionOut[0] = (float)x; + intersectionOut[1] = (float)y; + return true; + } + + return false; +} + /** * Determine if a ray intersects a plane. * @param p1 - 1st point defining the plane diff --git a/src/Common/MathFunctions.h b/src/Common/MathFunctions.h index df7d974d6540e621ed143dba314cc9daaaa3a7c1..0555612c53b96d75fb272840c91df9dc7fb209ae 100644 --- a/src/Common/MathFunctions.h +++ b/src/Common/MathFunctions.h @@ -239,6 +239,14 @@ public: const float tolerance, float intersectionOut[3]); + static bool vectorIntersection2D( + const float p1[3], + const float p2[3], + const float q1[3], + const float q2[3], + const float tolerance, + float intersectionOut[3]); + static bool rayIntersectPlane( const float p1[3], const float p2[3], diff --git a/src/Common/MatrixFunctions.h b/src/Common/MatrixFunctions.h index c9c633f6fc5000bf31c085d834f61f608b25d237..2f9cb2477e47a5c2452b4c3261a04514e7daf4d4 100644 --- a/src/Common/MatrixFunctions.h +++ b/src/Common/MatrixFunctions.h @@ -597,15 +597,15 @@ namespace caret { void MatrixFunctions::horizCat(const vector >& left, const vector >& right, vector >& result) { msize_t inrows = (msize_t)left.size(), leftcols, rightcols; - vector > tempstorage, *tresult = &result; - bool copyout = false; - if (&left == &result || &right == &result) - { - copyout = true; - tresult = &tempstorage; - } - if (inrows && inrows == (msize_t)right.size()) + if (inrows > 0 && inrows == (msize_t)right.size()) { + vector > tempstorage, *tresult = &result; + bool copyout = false; + if (&left == &result || &right == &result) + { + copyout = true; + tresult = &tempstorage; + } leftcols = (msize_t)left[0].size(); rightcols = (msize_t)right[0].size(); (*tresult) = left;//use STL copy to start @@ -617,13 +617,22 @@ namespace caret { (*tresult)[i][j + leftcols] = right[i][j]; } } - } else { - result.resize(0); - return; - } - if (copyout) - { - result = tempstorage; + if (copyout) + { + result = tempstorage; + } + } else {//handle special cases + if (inrows == 0 || right.size() == 0)//allow concatenating any empty matrix to any matrix + { + if (inrows == 0) + { + result = right; + } else { + result = left; + } + } else { + result.resize(0); + } } } @@ -631,18 +640,18 @@ namespace caret { void MatrixFunctions::vertCat(const vector >& top, const vector >& bottom, vector >& result) { msize_t toprows = (msize_t)top.size(), botrows = (msize_t)bottom.size(), incols; - vector > tempstorage, *tresult = &result; - bool copyout = false; - if (&top == &result || &bottom == &result) - { - copyout = true; - tresult = &tempstorage; - } if (toprows && botrows) { incols = (msize_t)top[0].size(); if (incols == (msize_t)bottom[0].size()) { + vector > tempstorage, *tresult = &result; + bool copyout = false; + if (&top == &result || &bottom == &result) + { + copyout = true; + tresult = &tempstorage; + } (*tresult) = top; resize(toprows + botrows, incols, (*tresult));//nondestructive resize for (msize_t i = 0; i < botrows; ++i) @@ -652,17 +661,25 @@ namespace caret { (*tresult)[i + toprows][j] = bottom[i][j]; } } + if (copyout) + { + result = tempstorage; + } + } else { + result.resize(0); + } + } else {//handle special cases + if (toprows == 0 || botrows == 0)//allow concatenation of empty matrix to any matrix + { + if (toprows == 0) + { + result = bottom; + } else { + result = top; + } } else { result.resize(0); - return; } - } else { - result.resize(0); - return; - } - if (copyout) - { - result = tempstorage; } } diff --git a/src/Common/NumericTextFormatting.cxx b/src/Common/NumericTextFormatting.cxx index 0e302e1d3b7f2ba12300b1c69cfe92970ceb387c..8d82b3ac34c13dc01a1b57c67372d122e3e81dc6 100644 --- a/src/Common/NumericTextFormatting.cxx +++ b/src/Common/NumericTextFormatting.cxx @@ -294,6 +294,145 @@ NumericTextFormatting::getFormatAndPrecision(const float valueIn, } } +/** + * Format the negative and positive values by using the value of the range (min to max) + * to set format and precision for all values. + * + * @param numericFormatIn + * How to format the numbers when converted to text. + * @param numericFormatPrecisionIn + * Precision used the numeric format is not automatic. + * @param negativeValuesIn + * The negative input values array. Must be at least two elements and + * the values must be sorted from smallest to largest. + * @param negativeFormattedValuesOut + * Output containing negativevalues formatted as text. + * @param negativeBumberOfValues + * Number of negativevalues in the arrays and both the input and output + * arrays must sized to this value. + * @param positiveValuesIn + * The positive input values array. Must be at least two elements and + * the values must be sorted from smallest to largest. + * @param positiveFormattedValuesOut + * Output containing positive values formatted as text. + * @param positiveNumberOfValues + * Number of positive values in the arrays and both the input and output + * arrays must sized to this value. + */ +void +NumericTextFormatting::formatValueRangeNegPos(const NumericFormatModeEnum::Enum numericFormatIn, + const int32_t numericFormatPrecisionIn, + const float negativeValuesIn[], + AString negativeFormattedValuesOut[], + const int32_t negativeNumberOfValues, + const float positiveValuesIn[], + AString positiveFormattedValuesOut[], + const int32_t positiveNumberOfValues) +{ + NumericFormatModeEnum::Enum numericFormat = numericFormatIn; + int32_t numericFormatPrecision = numericFormatPrecisionIn; + + + if (negativeNumberOfValues > 0) { + formatValueRange(numericFormat, + numericFormatPrecision, + negativeValuesIn, + negativeFormattedValuesOut, + negativeNumberOfValues); + } + + if (positiveNumberOfValues > 0) { + formatValueRange(numericFormat, + numericFormatPrecision, + positiveValuesIn, + positiveFormattedValuesOut, + positiveNumberOfValues); + } + + switch (numericFormat) { + case NumericFormatModeEnum::AUTO: + removeDotZeroIfAllIntegers(negativeFormattedValuesOut, + negativeNumberOfValues, + positiveFormattedValuesOut, + positiveNumberOfValues); + break; + case NumericFormatModeEnum::DECIMAL: + break; + case NumericFormatModeEnum::SCIENTIFIC: + break; + } +} + +/** + * If all of the positive and negative text values represent integers + * (contain no decimal or end with decimal followed by zeros), + * strip off decimal and trailing zeros. + * + * @param negativeFormattedValues + * Negative values formatted as text. + * @param negativeBumberOfValues + * Number of negative values. + * @param positiveFormattedValues + * Positive values formatted as text. + * @param positiveNumberOfValues + * Number of positive values. + */ +void +NumericTextFormatting::removeDotZeroIfAllIntegers(AString negativeFormattedValues[], + const int32_t negativeNumberOfValues, + AString positiveFormattedValues[], + const int32_t positiveNumberOfValues) +{ + std::vector allValues(negativeFormattedValues, + negativeFormattedValues + negativeNumberOfValues); + allValues.insert(allValues.end(), + positiveFormattedValues, + positiveFormattedValues + positiveNumberOfValues); + + for (auto& text: allValues) { + const int32_t len = text.length(); + + /* + * Does this text end with a decimal followed by all zeros + */ + const int32_t decimalIndex = text.indexOf('.'); + if (decimalIndex >= 0) { + for (int32_t j = decimalIndex + 1; j < len; j++) { + if (text[j] != '0') { + /* + * Non-zero trailing value so not integer value + */ + return; + } + } + + if (decimalIndex >= 0) { + /* + * Chop off decimal and zeros + * "text" is a reference so changes value in allValues vector + */ + text.resize(decimalIndex); + } + else { + /* if no decimal found, then value is already integer */ + } + } + } + + for (int32_t i = 0; i < negativeNumberOfValues; i++) { + CaretAssertArrayIndex(negativeFormattedValues, negativeNumberOfValues, i); + CaretAssertVectorIndex(allValues, i); + negativeFormattedValues[i] = allValues[i]; + } + + for (int32_t i = 0; i < positiveNumberOfValues; i++) { + CaretAssertArrayIndex(positiveFormattedValues, positiveNumberOfValues, i); + const int32_t j = i + negativeNumberOfValues; + CaretAssertVectorIndex(allValues, j); + positiveFormattedValues[i] = allValues[j]; + } +} + /** * Format the values by using the value of the range (min to max) * to set format and precision for all values. diff --git a/src/Common/NumericTextFormatting.h b/src/Common/NumericTextFormatting.h index 411fa8382e0dd07cf201d778394ec88cdc6118a6..5d0d4b5704368a8dd40fc25846a6749f2aab7c7b 100644 --- a/src/Common/NumericTextFormatting.h +++ b/src/Common/NumericTextFormatting.h @@ -44,11 +44,23 @@ namespace caret { AString formattedValuesOut[], const int32_t numberOfValues); + static void formatValueRangeNegPos(const NumericFormatModeEnum::Enum numericFormat, + const int32_t numericFormatPrecision, + const float negativeValuesIn[], + AString negativeFormattedValuesOut[], + const int32_t negativeNumberOfValues, + const float positiveValuesIn[], + AString positiveFormattedValuesOut[], + const int32_t positiveNumberOfValues); + static AString formatValue(const float valueIn); static void formatValuesIndividually(const float valuesIn[], AString formattedValuesOut[], const int32_t numberOfValues); + + static AString cleanZerosInValueText(const AString& textValueIn); + // ADD_NEW_METHODS_HERE private: @@ -60,8 +72,6 @@ namespace caret { NumericTextFormatting& operator=(const NumericTextFormatting&); - static AString cleanZerosInValueText(const AString& textValueIn); - static AString removeLeadingZeroFromExponent(const NumericFormatModeEnum::Enum numericFormat, const AString& textValueIn); @@ -74,6 +84,11 @@ namespace caret { static void getFormatAndPrecision(const float valueIn, char& formatOut, int& precisionOut); + + static void removeDotZeroIfAllIntegers(AString negativeFormattedValues[], + const int32_t negativeNumberOfValues, + AString positiveFormattedValues[], + const int32_t positiveNumberOfValues); // ADD_NEW_MEMBERS_HERE }; diff --git a/src/Common/StructureEnum.cxx b/src/Common/StructureEnum.cxx index afa9ee7994ea71aff31a87b908073873caa155c0..de9800e8e1a7a530b1e39dfff2a4ef476392652f 100644 --- a/src/Common/StructureEnum.cxx +++ b/src/Common/StructureEnum.cxx @@ -68,6 +68,8 @@ StructureEnum::initialize() return; } initializedFlag = true; + + //TSC: WARNING: the order of these determines the standard order of the structures in some -cifti-create-* commands, DO NOT reorder any *existing* entries enumData.push_back(StructureEnum(CORTEX_LEFT, "CORTEX_LEFT", diff --git a/src/Common/SystemUtilities.cxx b/src/Common/SystemUtilities.cxx index 13b164272113f93cf4aedd267ebc30fd64eea81b..386baeaee03a7984b895b24199f66999fc3af54a 100644 --- a/src/Common/SystemUtilities.cxx +++ b/src/Common/SystemUtilities.cxx @@ -21,10 +21,15 @@ #include #include #include +#ifdef CARET_OS_WINDOWS +#include +#else #include +#endif #include #include +#include #include #include @@ -649,3 +654,37 @@ SystemUtilities::getWorkbenchHome() return workbenchHomeDirectory = QDir::toNativeSeparators(workbenchHomeDirectory); } +/** + * @return The fully qualifed domain name for the local host. + * + * If failure, an empty string is returned and a message + * is sent to the logger. + * + * If the address is a "loopback" (127.0.0.1) it typically + * indicates that there is not a valid network connection + * and an empty string is returned and a message is logged. + */ +AString +SystemUtilities::getLocalHostName() +{ + QString hostName; + + QHostInfo hostInfo = QHostInfo::fromName(QHostInfo::localHostName()); + if (hostInfo.error() == QHostInfo::NoError) { + for (auto address : hostInfo.addresses()) { + if (address.isLoopback()) { + CaretLogFine("Lookup of host found loopback address (localhost). May not have valid network connection"); + return ""; + } + } + hostName = hostInfo.hostName(); + } + else { + CaretLogWarning("Lookup of local host name failed: " + + hostInfo.errorString()); + } + + return hostName; +} + + diff --git a/src/Common/SystemUtilities.h b/src/Common/SystemUtilities.h index 7a4f8e6a4d1819f390d20e252ef64a26ea249b4c..2035bff99a137bfb1102da9a4e1a00c423153d32 100644 --- a/src/Common/SystemUtilities.h +++ b/src/Common/SystemUtilities.h @@ -100,6 +100,9 @@ public: static void sleepSeconds(const float numberOfSeconds); static AString getWorkbenchHome(); + + static AString getLocalHostName(); + }; } // namespace diff --git a/src/Common/TileTabsConfiguration.cxx b/src/Common/TileTabsConfiguration.cxx index 9bc3a5b32aa78f2e4e0340c2568f616b27ac4358..2d6c23d4f13e71ae3c5156753ddfe30c7db8636d 100644 --- a/src/Common/TileTabsConfiguration.cxx +++ b/src/Common/TileTabsConfiguration.cxx @@ -68,6 +68,7 @@ TileTabsConfiguration::~TileTabsConfiguration() TileTabsConfiguration::TileTabsConfiguration(const TileTabsConfiguration& obj) : CaretObject(obj) { + initialize(); this->copyHelperTileTabsConfiguration(obj); } @@ -117,7 +118,9 @@ TileTabsConfiguration::initialize() 1.0); m_columnStretchFactors.resize(getMaximumNumberOfColumns(), 1.0); - + m_numberOfColumns = 0; + m_numberOfRows = 0; + setNumberOfRows(2); setNumberOfColumns(2); @@ -329,8 +332,9 @@ TileTabsConfiguration::getNumberOfRows() const void TileTabsConfiguration::setNumberOfRows(const int32_t numberOfRows) { - CaretAssert(numberOfRows >= 1); + const int32_t oldNumerOfRows = m_numberOfRows; + CaretAssert(numberOfRows >= 1); m_numberOfRows = numberOfRows; if (m_numberOfRows > getMaximumNumberOfRows()) { CaretLogSevere("Requested number of rows is " @@ -339,6 +343,14 @@ TileTabsConfiguration::setNumberOfRows(const int32_t numberOfRows) + getMaximumNumberOfRows()); m_numberOfRows = getMaximumNumberOfRows(); } + + /* + * Stretch factors default to 1.0 + */ + for (int32_t iRow = oldNumerOfRows; iRow < m_numberOfRows; iRow++) { + CaretAssertVectorIndex(m_rowStretchFactors, iRow); + m_rowStretchFactors[iRow] = 1.0; + } } /** @@ -359,6 +371,8 @@ TileTabsConfiguration::getNumberOfColumns() const void TileTabsConfiguration::setNumberOfColumns(const int32_t numberOfColumns) { + const int32_t oldNumberOfColumns = m_numberOfColumns; + CaretAssert(numberOfColumns >= 1); m_numberOfColumns = numberOfColumns; @@ -369,6 +383,14 @@ TileTabsConfiguration::setNumberOfColumns(const int32_t numberOfColumns) + getMaximumNumberOfColumns()); m_numberOfColumns = getMaximumNumberOfColumns(); } + + /* + * Stretch factors default to 1.0 + */ + for (int32_t iCol = oldNumberOfColumns; iCol < m_numberOfColumns; iCol++) { + CaretAssertVectorIndex(m_columnStretchFactors, iCol); + m_columnStretchFactors[iCol] = 1.0; + } } /** diff --git a/src/Common/WorkbenchSpecialVersionEnum.cxx b/src/Common/WorkbenchSpecialVersionEnum.cxx new file mode 100644 index 0000000000000000000000000000000000000000..779b04f3ba6937e7db8879fed060ba2ce70e97b1 --- /dev/null +++ b/src/Common/WorkbenchSpecialVersionEnum.cxx @@ -0,0 +1,373 @@ + +/*LICENSE_START*/ +/* + * Copyright (C) 2016 Washington University School of Medicine + * + * 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 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ +/*LICENSE_END*/ + +#include +#define __WORKBENCH_SPECIAL_VERSION_ENUM_DECLARE__ +#include "WorkbenchSpecialVersionEnum.h" +#undef __WORKBENCH_SPECIAL_VERSION_ENUM_DECLARE__ + +#include "CaretAssert.h" + +using namespace caret; + + +/** + * \class caret::WorkbenchSpecialVersionEnum + * \brief Identifies versions of Workbench with special functionality. + * + * Using this enumerated type in the GUI with an EnumComboBoxTemplate + * + * Header File (.h) + * Forward declare the data type: + * class EnumComboBoxTemplate; + * + * Declare the member: + * EnumComboBoxTemplate* m_workbenchSpecialVersionEnumComboBox; + * + * Declare a slot that is called when user changes selection + * private slots: + * void workbenchSpecialVersionEnumComboBoxItemActivated(); + * + * Implementation File (.cxx) + * Include the header files + * #include "EnumComboBoxTemplate.h" + * #include "WorkbenchSpecialVersionEnum.h" + * + * Instatiate: + * m_workbenchSpecialVersionEnumComboBox = new EnumComboBoxTemplate(this); + * m_workbenchSpecialVersionEnumComboBox->setup(); + * + * Get notified when the user changes the selection: + * QObject::connect(m_workbenchSpecialVersionEnumComboBox, SIGNAL(itemActivated()), + * this, SLOT(workbenchSpecialVersionEnumComboBoxItemActivated())); + * + * Update the selection: + * m_workbenchSpecialVersionEnumComboBox->setSelectedItem(NEW_VALUE); + * + * Read the selection: + * const WorkbenchSpecialVersionEnum::Enum VARIABLE = m_workbenchSpecialVersionEnumComboBox->getSelectedItem(); + * + */ + +/** + * Constructor. + * + * @param enumValue + * An enumerated value. + * @param name + * Name of enumerated value. + * + * @param guiName + * User-friendly name for use in user-interface. + */ +WorkbenchSpecialVersionEnum::WorkbenchSpecialVersionEnum(const Enum enumValue, + const AString& name, + const AString& guiName) +{ + this->enumValue = enumValue; + this->integerCode = integerCodeCounter++; + this->name = name; + this->guiName = guiName; +} + +/** + * Destructor. + */ +WorkbenchSpecialVersionEnum::~WorkbenchSpecialVersionEnum() +{ +} + +/** + * Initialize the enumerated metadata. + */ +void +WorkbenchSpecialVersionEnum::initialize() +{ + if (initializedFlag) { + return; + } + initializedFlag = true; + + enumData.push_back(WorkbenchSpecialVersionEnum(WORKBENCH_SPECIAL_VERSION_NO, + "WORKBENCH_SPECIAL_VERSION_NO", + "Workbench Special Version No")); + + enumData.push_back(WorkbenchSpecialVersionEnum(WORKBENCH_SPECIAL_VERSION_FIRST_CHARTING, + "WORKBENCH_SPECIAL_VERSION_FIRST_CHARTING", + "Old Charting")); + +} + +/** + * Find the data for and enumerated value. + * @param enumValue + * The enumerated value. + * @return Pointer to data for this enumerated type + * or NULL if no data for type or if type is invalid. + */ +const WorkbenchSpecialVersionEnum* +WorkbenchSpecialVersionEnum::findData(const Enum enumValue) +{ + if (initializedFlag == false) initialize(); + + size_t num = enumData.size(); + for (size_t i = 0; i < num; i++) { + const WorkbenchSpecialVersionEnum* d = &enumData[i]; + if (d->enumValue == enumValue) { + return d; + } + } + + return NULL; +} + +/** + * Get a string representation of the enumerated type. + * @param enumValue + * Enumerated value. + * @return + * String representing enumerated value. + */ +AString +WorkbenchSpecialVersionEnum::toName(Enum enumValue) { + if (initializedFlag == false) initialize(); + + const WorkbenchSpecialVersionEnum* enumInstance = findData(enumValue); + return enumInstance->name; +} + +/** + * Get an enumerated value corresponding to its name. + * @param name + * Name of enumerated value. + * @param isValidOut + * If not NULL, it is set indicating that a + * enum value exists for the input name. + * @return + * Enumerated value. + */ +WorkbenchSpecialVersionEnum::Enum +WorkbenchSpecialVersionEnum::fromName(const AString& name, bool* isValidOut) +{ + if (initializedFlag == false) initialize(); + + bool validFlag = false; + Enum enumValue = WorkbenchSpecialVersionEnum::enumData[0].enumValue; + + for (std::vector::iterator iter = enumData.begin(); + iter != enumData.end(); + iter++) { + const WorkbenchSpecialVersionEnum& d = *iter; + if (d.name == name) { + enumValue = d.enumValue; + validFlag = true; + break; + } + } + + if (isValidOut != 0) { + *isValidOut = validFlag; + } + else if (validFlag == false) { + CaretAssertMessage(0, AString("Name " + name + "failed to match enumerated value for type WorkbenchSpecialVersionEnum")); + } + return enumValue; +} + +/** + * Get a GUI string representation of the enumerated type. + * @param enumValue + * Enumerated value. + * @return + * String representing enumerated value. + */ +AString +WorkbenchSpecialVersionEnum::toGuiName(Enum enumValue) { + if (initializedFlag == false) initialize(); + + const WorkbenchSpecialVersionEnum* enumInstance = findData(enumValue); + return enumInstance->guiName; +} + +/** + * Get an enumerated value corresponding to its GUI name. + * @param s + * Name of enumerated value. + * @param isValidOut + * If not NULL, it is set indicating that a + * enum value exists for the input name. + * @return + * Enumerated value. + */ +WorkbenchSpecialVersionEnum::Enum +WorkbenchSpecialVersionEnum::fromGuiName(const AString& guiName, bool* isValidOut) +{ + if (initializedFlag == false) initialize(); + + bool validFlag = false; + Enum enumValue = WorkbenchSpecialVersionEnum::enumData[0].enumValue; + + for (std::vector::iterator iter = enumData.begin(); + iter != enumData.end(); + iter++) { + const WorkbenchSpecialVersionEnum& d = *iter; + if (d.guiName == guiName) { + enumValue = d.enumValue; + validFlag = true; + break; + } + } + + if (isValidOut != 0) { + *isValidOut = validFlag; + } + else if (validFlag == false) { + CaretAssertMessage(0, AString("guiName " + guiName + "failed to match enumerated value for type WorkbenchSpecialVersionEnum")); + } + return enumValue; +} + +/** + * Get the integer code for a data type. + * + * @return + * Integer code for data type. + */ +int32_t +WorkbenchSpecialVersionEnum::toIntegerCode(Enum enumValue) +{ + if (initializedFlag == false) initialize(); + const WorkbenchSpecialVersionEnum* enumInstance = findData(enumValue); + return enumInstance->integerCode; +} + +/** + * Find the data type corresponding to an integer code. + * + * @param integerCode + * Integer code for enum. + * @param isValidOut + * If not NULL, on exit isValidOut will indicate if + * integer code is valid. + * @return + * Enum for integer code. + */ +WorkbenchSpecialVersionEnum::Enum +WorkbenchSpecialVersionEnum::fromIntegerCode(const int32_t integerCode, bool* isValidOut) +{ + if (initializedFlag == false) initialize(); + + bool validFlag = false; + Enum enumValue = WorkbenchSpecialVersionEnum::enumData[0].enumValue; + + for (std::vector::iterator iter = enumData.begin(); + iter != enumData.end(); + iter++) { + const WorkbenchSpecialVersionEnum& enumInstance = *iter; + if (enumInstance.integerCode == integerCode) { + enumValue = enumInstance.enumValue; + validFlag = true; + break; + } + } + + if (isValidOut != 0) { + *isValidOut = validFlag; + } + else if (validFlag == false) { + CaretAssertMessage(0, AString("Integer code " + AString::number(integerCode) + "failed to match enumerated value for type WorkbenchSpecialVersionEnum")); + } + return enumValue; +} + +/** + * Get all of the enumerated type values. The values can be used + * as parameters to toXXX() methods to get associated metadata. + * + * @param allEnums + * A vector that is OUTPUT containing all of the enumerated values. + */ +void +WorkbenchSpecialVersionEnum::getAllEnums(std::vector& allEnums) +{ + if (initializedFlag == false) initialize(); + + allEnums.clear(); + + for (std::vector::iterator iter = enumData.begin(); + iter != enumData.end(); + iter++) { + allEnums.push_back(iter->enumValue); + } +} + +/** + * Get all of the names of the enumerated type values. + * + * @param allNames + * A vector that is OUTPUT containing all of the names of the enumerated values. + * @param isSorted + * If true, the names are sorted in alphabetical order. + */ +void +WorkbenchSpecialVersionEnum::getAllNames(std::vector& allNames, const bool isSorted) +{ + if (initializedFlag == false) initialize(); + + allNames.clear(); + + for (std::vector::iterator iter = enumData.begin(); + iter != enumData.end(); + iter++) { + allNames.push_back(WorkbenchSpecialVersionEnum::toName(iter->enumValue)); + } + + if (isSorted) { + std::sort(allNames.begin(), allNames.end()); + } +} + +/** + * Get all of the GUI names of the enumerated type values. + * + * @param allNames + * A vector that is OUTPUT containing all of the GUI names of the enumerated values. + * @param isSorted + * If true, the names are sorted in alphabetical order. + */ +void +WorkbenchSpecialVersionEnum::getAllGuiNames(std::vector& allGuiNames, const bool isSorted) +{ + if (initializedFlag == false) initialize(); + + allGuiNames.clear(); + + for (std::vector::iterator iter = enumData.begin(); + iter != enumData.end(); + iter++) { + allGuiNames.push_back(WorkbenchSpecialVersionEnum::toGuiName(iter->enumValue)); + } + + if (isSorted) { + std::sort(allGuiNames.begin(), allGuiNames.end()); + } +} + diff --git a/src/Common/WorkbenchSpecialVersionEnum.h b/src/Common/WorkbenchSpecialVersionEnum.h new file mode 100644 index 0000000000000000000000000000000000000000..5e85bf3c8ed2dfc7548df2640813d40cb39c98cc --- /dev/null +++ b/src/Common/WorkbenchSpecialVersionEnum.h @@ -0,0 +1,104 @@ +#ifndef __WORKBENCH_SPECIAL_VERSION_ENUM_H__ +#define __WORKBENCH_SPECIAL_VERSION_ENUM_H__ + +/*LICENSE_START*/ +/* + * Copyright (C) 2016 Washington University School of Medicine + * + * 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 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ +/*LICENSE_END*/ + + +#include +#include +#include "AString.h" + +namespace caret { + +class WorkbenchSpecialVersionEnum { + +public: + /** + * Enumerated values. + */ + enum Enum { + /** Not a special version */ + WORKBENCH_SPECIAL_VERSION_NO, + /** First Implementation of Charting */ + WORKBENCH_SPECIAL_VERSION_FIRST_CHARTING + }; + + + ~WorkbenchSpecialVersionEnum(); + + static AString toName(Enum enumValue); + + static Enum fromName(const AString& name, bool* isValidOut); + + static AString toGuiName(Enum enumValue); + + static Enum fromGuiName(const AString& guiName, bool* isValidOut); + + static int32_t toIntegerCode(Enum enumValue); + + static Enum fromIntegerCode(const int32_t integerCode, bool* isValidOut); + + static void getAllEnums(std::vector& allEnums); + + static void getAllNames(std::vector& allNames, const bool isSorted); + + static void getAllGuiNames(std::vector& allGuiNames, const bool isSorted); + +private: + WorkbenchSpecialVersionEnum(const Enum enumValue, + const AString& name, + const AString& guiName); + + static const WorkbenchSpecialVersionEnum* findData(const Enum enumValue); + + /** Holds all instance of enum values and associated metadata */ + static std::vector enumData; + + /** Initialize instances that contain the enum values and metadata */ + static void initialize(); + + /** Indicates instance of enum values and metadata have been initialized */ + static bool initializedFlag; + + /** Auto generated integer codes */ + static int32_t integerCodeCounter; + + /** The enumerated type value for an instance */ + Enum enumValue; + + /** The integer code associated with an enumerated value */ + int32_t integerCode; + + /** The name, a text string that is identical to the enumerated value */ + AString name; + + /** A user-friendly name that is displayed in the GUI */ + AString guiName; +}; + +#ifdef __WORKBENCH_SPECIAL_VERSION_ENUM_DECLARE__ +std::vector WorkbenchSpecialVersionEnum::enumData; +bool WorkbenchSpecialVersionEnum::initializedFlag = false; +int32_t WorkbenchSpecialVersionEnum::integerCodeCounter = 0; +#endif // __WORKBENCH_SPECIAL_VERSION_ENUM_DECLARE__ + +} // namespace +#endif //__WORKBENCH_SPECIAL_VERSION_ENUM_H__ diff --git a/src/Common/dot_wrapper.h b/src/Common/dot_wrapper.h index a27198b20db17e0656f395116512213785a49d6e..d8cb9345b323e695c2a30557d081fa3d9d3ab300 100644 --- a/src/Common/dot_wrapper.h +++ b/src/Common/dot_wrapper.h @@ -16,21 +16,23 @@ extern "C" #ifdef CARET_DOTFCN #include "dot.h" #else -inline double sddot (const float *a, const float *b, int n) +inline double dsdot (const float *a, const float *b, int n) { double sum = 0; for (int k = 0; k < n; k++) sum += a[k] * b[k]; return sum; -} // sddot() +} // dsdot() //copy enum from dot.h //renamed to dot_flags in both files for less conflict chance typedef enum { - DOT_NAIVE = 1, - DOT_SSE2 = 2, - DOT_AVX = 3, - DOT_AVXFMA = 4, - DOT_AUTO = 100 + DOT_NAIVE = 1, + DOT_SSE2 = 2, + DOT_AVX = 3, + DOT_AVXFMA = 4, + DOT_AVX512 = 5, + DOT_AVX512FMA = 6, + DOT_AUTO = 100 } dot_flags; //and dummy implementation of dot_set_impl inline dot_flags dot_set_impl (dot_flags) @@ -55,7 +57,7 @@ namespace caret { public: typedef dot_flags Enum; - + static inline std::vector getAllEnums() { std::vector ret; @@ -63,10 +65,12 @@ namespace caret ret.push_back(DOT_SSE2); ret.push_back(DOT_AVX); ret.push_back(DOT_AVXFMA); + ret.push_back(DOT_AVX512); + ret.push_back(DOT_AVX512FMA); ret.push_back(DOT_AUTO); return ret; } - + static inline Enum fromName(const AString& name, bool* isValidOut = NULL) { bool valid = false; @@ -84,6 +88,12 @@ namespace caret } else if (name == "AVXFMA") { ret = DOT_AVXFMA; valid = true; + } else if (name == "AVX512") { + ret = DOT_AVX512; + valid = true; + } else if (name == "AVX512FMA") { + ret = DOT_AVX512FMA; + valid = true; } else if (name == "AUTO") { ret = DOT_AUTO; valid = true; @@ -96,7 +106,7 @@ namespace caret } return ret; } - + static inline AString toName(const Enum& value) { switch (value) @@ -109,6 +119,10 @@ namespace caret return "AVX"; case DOT_AVXFMA: return "AVXFMA"; + case DOT_AVX512: + return "AVX512"; + case DOT_AVX512FMA: + return "AVX512FMA"; case DOT_AUTO: return "AUTO"; default: diff --git a/src/Desktop/CMakeLists.txt b/src/Desktop/CMakeLists.txt index f6afe880f372c41b688cfc147e030ff87fc24287..d9db987878dedf99ac0742dcdd28cf9873750a1d 100644 --- a/src/Desktop/CMakeLists.txt +++ b/src/Desktop/CMakeLists.txt @@ -8,18 +8,24 @@ PROJECT(Desktop) # set (EXE_NAME wb_view) -# -# QT Libraries -# -SET(QT_USE_QTXML TRUE) -SET(QT_USE_QTOPENGL TRUE) -SET(QT_USE_QTNETWORK TRUE) -#SET(QT_USE_QTWEBKIT TRUE) - # # QT Include directories # -INCLUDE (${QT_USE_FILE}) +if(Qt5_FOUND) + include_directories(${Qt5Core_INCLUDE_DIRS}) + include_directories(${Qt5Network_INCLUDE_DIRS}) + if(Qt5OpenGL_FOUND) + include_directories(${Qt5OpenGL_INCLUDE_DIRS}) + endif() + include_directories(${Qt5Widgets_INCLUDE_DIRS}) +endif() +IF (QT4_FOUND) + SET(QT_USE_QTXML TRUE) + SET(QT_USE_QTOPENGL TRUE) + SET(QT_USE_QTNETWORK TRUE) + INCLUDE(${QT_USE_FILE}) +ENDIF () + # # Added by JWH to eliminate OpenGL linking errors in Ubuntu Linux @@ -33,7 +39,17 @@ ENDIF (UNIX) # # Resources # -QT4_ADD_RESOURCES(IMAGE_RCS_SRCS ../Resources/GeneralResources/general_resources.qrc ../Resources/GuiResources/gui_resources.qrc) +SET (RESOURCES_QRC_FILE + ../Resources/General/general_resources.qrc + ../Resources/Gui/gui_resources.qrc + ../Resources/Help/help_resources.qrc) +IF (Qt5_FOUND) + QT5_ADD_RESOURCES(IMAGE_RCS_SRCS ${RESOURCES_QRC_FILE}) +ELSE (Qt5_FOUND) + IF (QT4_FOUND) + QT4_ADD_RESOURCES(IMAGE_RCS_SRCS ${RESOURCES_QRC_FILE}) + ENDIF() +ENDIF (Qt5_FOUND) # # Create the executable @@ -81,6 +97,22 @@ IF (NOT APPLE) ENDIF (UNIX) ENDIF (NOT APPLE) +if(Qt5OpenGL_FOUND) + set(QT5_OPENGL_LIB_NAME Qt5::OpenGL) +endif() +if(Qt5_FOUND) + set(QT5_LINK_LIBS + Qt5::Concurrent + Qt5::Core + Qt5::Gui + Qt5::Network + ${QT5_OPENGL_LIB_NAME} + Qt5::PrintSupport + Qt5::Test + Qt5::Widgets + Qt5::Xml) +endif() + # # Libraries that are linked # @@ -93,8 +125,9 @@ OperationsBase ${Qwt_LIBRARIES} OSMesaDummy Brain -${FTGL_LIBRARIES} Files +Graphics +${FTGL_LIBRARIES} Annotations Charting Palette @@ -105,14 +138,18 @@ FilesBase Scenes Xml Common +${GLEW_LIBRARIES} ${QUAZIP_LIBRARIES} ${FREETYPE_LIBRARIES} ${QT_LIBRARIES} +${QT5_LINK_LIBS} ${ZLIB_LIBRARIES} #${LIBS} ) -INSTALL(TARGETS ${EXE_NAME} DESTINATION bin) +INCLUDE(GNUInstallDirs) + +INSTALL(TARGETS ${EXE_NAME} DESTINATION ${CMAKE_INSTALL_BINDIR}) IF(WIN32) TARGET_LINK_LIBRARIES(${EXE_NAME} @@ -161,6 +198,7 @@ ${CMAKE_SOURCE_DIR}/Algorithms ${CMAKE_SOURCE_DIR}/Commands ${CMAKE_SOURCE_DIR}/GuiQt ${CMAKE_SOURCE_DIR}/Brain +${CMAKE_SOURCE_DIR}/Graphics ${CMAKE_SOURCE_DIR}/FilesBase ${CMAKE_SOURCE_DIR}/Files ${CMAKE_SOURCE_DIR}/Palette @@ -180,7 +218,6 @@ ${CMAKE_SOURCE_DIR}/Common # Apple needs framework # IF (APPLE) - MESSAGE("EXE: " ${CMAKE_BUILD_DIR} " " ${EXE_NAME}) ADD_CUSTOM_COMMAND( TARGET ${EXE_NAME} POST_BUILD diff --git a/src/Desktop/desktop.cxx b/src/Desktop/desktop.cxx index c8a185acf81ab4f07f78de8d2161bd4dd8560493..68d5035bb8c65cf3585403628ee5a43e70272dd3 100644 --- a/src/Desktop/desktop.cxx +++ b/src/Desktop/desktop.cxx @@ -18,11 +18,19 @@ */ /*LICENSE_END*/ +/* + * When GLEW is used, CaretOpenGLInclude.h will include "Gl/glew.h". + * Gl/glew.h MUST BE BEFORE Gl/gl.h and Gl/gl.h is included by + * some Qt classes so, we must include CaretOpenGL.h first. + */ +#include "CaretOpenGLInclude.h" #include #include #include +#ifndef WORKBENCH_USE_QT5_QOPENGL_WIDGET #include +#endif #include #include #include @@ -33,6 +41,7 @@ #include "ApplicationInformation.h" #include "BrainBrowserWindow.h" +#include "BrainOpenGL.h" #include "BrainOpenGLWidget.h" #include "CaretAssert.h" #include "CaretCommandLine.h" @@ -57,11 +66,125 @@ static bool caretLoggerIsValid = false; using namespace caret; using namespace std; +#if QT_VERSION >= 0x050000 /** - * Handles message produced by Qt. + * Handles message produced by Qt 5 */ static void -messageHandlerForQt(QtMsgType type, const char* msg) +messageHandlerForQt5(QtMsgType type, const QMessageLogContext& context, const QString& msg) +{ + const AString backtrace = SystemUtilities::getBackTrace(); + + const AString contextInfo = (" Context Info File (" + + QString(context.file) + + ") Function (" + QString(context.function) + + ") Line (" + QString::number(context.line) + + ") Version (" + QString::number(context.version) + + ") Category (" + QString(context.category) + + ")"); + const AString message = (AString(msg) + "\n" + + contextInfo + "\n" + + backtrace); + + if (caretLoggerIsValid) { + bool abortFlag = false; + bool displayedFlag = false; + switch (type) { + case QtDebugMsg: + CaretLogInfo(message); + displayedFlag = CaretLogger::getLogger()->isInfo(); + break; + case QtWarningMsg: + CaretLogWarning(message); + displayedFlag = CaretLogger::getLogger()->isWarning(); + break; + case QtCriticalMsg: + CaretLogSevere(message); + displayedFlag = CaretLogger::getLogger()->isSevere(); + break; + case QtFatalMsg: + cerr << "Qt Fatal: " << message << endl; + abortFlag = true;//fatal will cause an abort, so always display it, bypassing logger entirely + displayedFlag = true; + break; +#if QT_VERSION >= 0x050500 + case QtInfoMsg: + CaretLogInfo(message); + displayedFlag = CaretLogger::getLogger()->isInfo(); + break; +#endif + } + + /* + * Beep to alert user about an error!!! + */ + if (displayedFlag && (type != QtDebugMsg))//don't beep for debug + { + GuiManager::beep(); + } +#ifndef NDEBUG + if (!displayedFlag) + { + cerr << "DEBUG: Qt "; + switch (type) + { + case QtDebugMsg: + cerr << "Debug "; + break; + case QtWarningMsg: + cerr << "Warning "; + break; + case QtCriticalMsg: + cerr << "Critical "; + break; + case QtFatalMsg: + cerr << "FATAL (?!?) ";//should never happen + break; +#if QT_VERSION >= 0x050500 + case QtInfoMsg: + std::cerr << "Info "; + break; +#endif + } + cerr << "message hidden" << endl; + } +#endif + + if (abortFlag) { + std::abort(); + } + } + else { + switch (type) { + case QtDebugMsg: + std::cerr << "Qt Debug: " << message << std::endl; + break; + case QtWarningMsg: + std::cerr << "Qt Warning: " << message << std::endl; + break; + case QtCriticalMsg: + std::cerr << "Qt Critical: " << message << std::endl; + break; + case QtFatalMsg: + std::cerr << "Qt Fatal: " << message << std::endl; + std::abort(); + break; +#if QT_VERSION >= 0x050500 + case QtInfoMsg: + std::cerr << "Qt Info: " << message << std::endl; + break; +#endif + } + } +} + +#else // QT_VERSION + +/** + * Handles message produced by Qt 4. + */ +static void +messageHandlerForQt4(QtMsgType type, const char* msg) { const AString backtrace = SystemUtilities::getBackTrace(); @@ -143,16 +266,20 @@ messageHandlerForQt(QtMsgType type, const char* msg) } } +#endif // QT_VERSION + //struct for communicating stuff back to main from parseCommandLine struct ProgramState { vector fileList; - int specLoadType; int windowSizeXY[2]; int windowPosXY[2]; int graphicsSizeXY[2]; bool showSplash; + AString specFileNameLoadWithDialog; + AString specFileNameLoadAll; + AString sceneFileName; AString sceneNameOrNumber; @@ -160,89 +287,6 @@ struct ProgramState }; -/* -// maximum mumber of lines the output console should have - -static const WORD MAX_CONSOLE_LINES = 500; -#include -#include -#include -#include -#include -#include - -void RedirectIOToConsole() -{ - - int hConHandle; - - long lStdHandle; - - CONSOLE_SCREEN_BUFFER_INFO coninfo; - - FILE *fp; - - // allocate a console for this app - - AllocConsole(); - - // set the screen buffer to be big enough to let us scroll text - - GetConsoleScreenBufferInfo(GetStdHandle(STD_OUTPUT_HANDLE), - - &coninfo); - - coninfo.dwSize.Y = MAX_CONSOLE_LINES; - - SetConsoleScreenBufferSize(GetStdHandle(STD_OUTPUT_HANDLE), - - coninfo.dwSize); - - // redirect unbuffered STDOUT to the console - - lStdHandle = (long)GetStdHandle(STD_OUTPUT_HANDLE); - - hConHandle = _open_osfhandle(lStdHandle, _O_TEXT); - - fp = _fdopen( hConHandle, "w" ); - - *stdout = *fp; - - setvbuf( stdout, NULL, _IONBF, 0 ); - - // redirect unbuffered STDIN to the console - - lStdHandle = (long)GetStdHandle(STD_INPUT_HANDLE); - - hConHandle = _open_osfhandle(lStdHandle, _O_TEXT); - - fp = _fdopen( hConHandle, "r" ); - - *stdin = *fp; - - setvbuf( stdin, NULL, _IONBF, 0 ); - - // redirect unbuffered STDERR to the console - - lStdHandle = (long)GetStdHandle(STD_ERROR_HANDLE); - - hConHandle = _open_osfhandle(lStdHandle, _O_TEXT); - - fp = _fdopen( hConHandle, "w" ); - - *stderr = *fp; - - setvbuf( stderr, NULL, _IONBF, 0 ); - - // make cout, wcout, cin, wcin, wcerr, cerr, wclog and clog - - // point to console as well - - ios::sync_with_stdio(); - -}*/ -//#pragma comment(linker, "/SUBSYSTEM:windows /ENTRY:mainCRTStartup") - //declare the functions associated with command line void printHelp(const AString& progName); void parseCommandLine(const AString& progName, ProgramParameters* myParams, ProgramState& myState); @@ -250,8 +294,6 @@ int main(int argc, char* argv[]) { srand(time(NULL)); - //MS Windows code to allocate a new console, will have a preference to set this up - //RedirectIOToConsole(); int result; { /* @@ -282,9 +324,20 @@ main(int argc, char* argv[]) */ CaretLogFine("Running: " + caret_global_commandLine); + /* + * Setup OpenGL if using Qt 5's QOpenGLWidget. + * QOpenGLWidget's documentation indicates that + * default format must be initizlied before + * QApplication is created. + */ +#ifdef WORKBENCH_USE_QT5_QOPENGL_WIDGET + QCoreApplication::setAttribute(Qt::AA_ShareOpenGLContexts); + BrainOpenGLWidget::initializeDefaultGLFormat(); +#endif + //change the default graphics system on mac to avoid rendering performance issues with qwtplotter #ifdef CARET_OS_MACOSX - QApplication::setGraphicsSystem("raster"); + // Qt-Deprecated: QApplication::setGraphicsSystem("raster"); MacApplication app(argc, argv); #else //CARET_OS_MACOSX QApplication app(argc, argv); @@ -308,52 +361,44 @@ main(int argc, char* argv[]) /* * Make sure OpenGL is available. */ - if (QGLFormat::hasOpenGL() == false) { - QString msg = "This computer does not support OpenGL (3D graphics system).\n" - "You will need to install OpenGL to run Workbench.\n" - "Please see your system administrator."; + const QString noOpenGLMessage("OpenGL (3D Graphics System) is not available. " + "This may be caused by missing or outdated OpenGL libraries." + +#ifdef CARET_OS_LINUX + "On Linux, this may be caused by a missing plugin " + "library /xcbglintegrations/libqxcb-glx-integration.so." +#endif + ); + + +#ifndef WORKBENCH_USE_QT5_QOPENGL_WIDGET + if ( ! QGLFormat::hasOpenGL()) { app.processEvents(); WuQMessageBox::errorOk(NULL, - msg); + noOpenGLMessage); app.processEvents(); return -1; } +#endif /* - * Setup OpenGL + * Setup OpenGL if NOT using Qt 5's QOpenGLWidget */ +#ifndef WORKBENCH_USE_QT5_QOPENGL_WIDGET BrainOpenGLWidget::initializeDefaultGLFormat(); +#endif - qInstallMsgHandler(messageHandlerForQt);//this handler uses CaretLogger and GuiManager, so we must install it after the logger is available and the application is created +#if QT_VERSION >= 0x050000 + qInstallMessageHandler(messageHandlerForQt5);//this handler uses CaretLogger and GuiManager, so we must install it after the logger is available and the application is created +#else // QT_VERSION + qInstallMsgHandler(messageHandlerForQt4);//this handler uses CaretLogger and GuiManager, so we must install it after the logger is available and the application is created +#endif // QT_VERSION /* * Log debug status */ CaretLogConfig(applicationInformation.getCompiledWithDebugStatus()); - - //sanity check command line - bool haveSpec = false; - bool haveFiles = false; - for (int i = 0; i < (int)myState.fileList.size(); ++i) - { - if (myState.fileList[i].endsWith(".spec")) - { - if (haveSpec) - { - cerr << "error, cannot load multiple spec files at this time" << endl; - return -1; - } - haveSpec = true; - } else { - haveFiles = true; - } - } - //if error to have both data and spec files - /*if (haveFiles && haveSpec) - { - cerr << "error, cannot specify both spec files and data files on the command line" << endl; - return -1; - }//*/ + /* * Enabled the desired splash screen based upon user preferences @@ -362,14 +407,8 @@ main(int argc, char* argv[]) */ CaretPreferences* preferences = SessionManager::get()->getCaretPreferences(); bool showSelectionSplashScreen = preferences->isSplashScreenEnabled(); - if (myState.fileList.empty() == false) { - showSelectionSplashScreen = false; - } - if (myState.sceneFileName.isEmpty() == false) { - showSelectionSplashScreen = false; - } bool showImageSplashScreen = (! showSelectionSplashScreen); - if (myState.showSplash == false) { + if ( ! myState.showSplash) { showSelectionSplashScreen = false; showImageSplashScreen = false; } @@ -407,20 +446,16 @@ main(int argc, char* argv[]) /* * Now that events have processed, see if there was a request for - * a data file to open + * a data file to open. */ const AString dataFileNameFromOS = GuiManager::get()->getNameOfDataFileToOpenAfterStartup(); - if (dataFileNameFromOS.isEmpty() == false) { - myState.fileList.push_back(dataFileNameFromOS); + if ( ! dataFileNameFromOS.isEmpty()) { showSelectionSplashScreen = false; if (dataFileNameFromOS.endsWith(DataFileTypeEnum::toFileExtension(DataFileTypeEnum::SPECIFICATION))) { - haveSpec = true; - haveFiles = false; - myState.specLoadType = 0; + myState.specFileNameLoadWithDialog = dataFileNameFromOS; } else { - haveSpec = false; - haveFiles = true; + myState.fileList.push_back(dataFileNameFromOS); } } @@ -439,15 +474,11 @@ main(int argc, char* argv[]) const QString dataFileName = splashScreen.getSelectedDataFileName(); if ( ! dataFileName.isEmpty()) { myState.fileList.clear(); - myState.fileList.push_back(dataFileName); - if (dataFileName.endsWith(DataFileTypeEnum::SPECIFICATION)) { - myState.specLoadType = 0; // which means use BrainBrowserWindow::LOAD_SPEC_FILE_WITH_DIALOG_VIA_COMMAND_LINE; - haveSpec = true; - haveFiles = false; + if (dataFileName.endsWith(DataFileTypeEnum::toFileExtension(DataFileTypeEnum::SPECIFICATION))) { + myState.specFileNameLoadWithDialog = dataFileName; } else { - haveSpec = false; - haveFiles = true; + myState.fileList.push_back(dataFileName); } } } @@ -464,8 +495,55 @@ main(int argc, char* argv[]) EventManager::get()->sendEvent(newBrowserWindow.getPointer()); splashScreen.close(); + BrainBrowserWindow* myWindow = GuiManager::get()->getBrowserWindowByWindowIndex(0); + if ( ! myWindow->hasValidOpenGL()) { + app.processEvents(); + WuQMessageBox::errorOk(NULL, + noOpenGLMessage); + app.processEvents(); + + return -1; + } + + /** + * Test for the required OpenGL version is available. + */ + AString requiredOpenGLMessage; + if ( ! BrainOpenGL::testForRequiredOpenGLVersion(requiredOpenGLMessage)) { + app.processEvents(); + if ( ! WuQMessageBox::warningAcceptReject(NULL, + requiredOpenGLMessage, + "Continue", + "Exit")) { + return -1; + } + } + /* + * Initialize GLEW if it is being used + */ + AString glewErrorMessage; +#ifdef HAVE_GLEW + GLenum err = glewInit(); + if (GLEW_OK != err) { + /* Problem: glewInit failed, something is seriously wrong. */ + + glewErrorMessage = ("GLEW failed to initialize.\n" + + AString(reinterpret_cast(glewGetErrorString(err)))); + CaretLogSevere(glewErrorMessage); + } + CaretLogInfo("Status: Using GLEW version " + + AString(reinterpret_cast(glewGetString(GLEW_VERSION)))); +#endif /* HAVE_GLEW */ + if ( ! glewErrorMessage.isEmpty()) { + app.processEvents(); + WuQMessageBox::errorOk(NULL, + glewErrorMessage); + app.processEvents(); + + return -1; + } if ((myState.windowSizeXY[0] > 0) && (myState.windowSizeXY[1] > 0)) { @@ -491,38 +569,34 @@ main(int argc, char* argv[]) myWindow->setGraphicsWidgetFixedSize(myState.graphicsSizeXY[0], myState.graphicsSizeXY[1]); } - //use command line - if (haveFiles) - { - myWindow->loadFilesFromCommandLine(myState.fileList, BrainBrowserWindow::LOAD_SPEC_FILE_WITH_DIALOG);//second parameter unused in this case + if ( ! myState.specFileNameLoadAll.isEmpty()) { + myWindow->loadFilesFromCommandLine({ myState.specFileNameLoadAll }, + BrainBrowserWindow::LOAD_SPEC_FILE_CONTENTS_VIA_COMMAND_LINE); } - if (haveSpec) - { - switch (myState.specLoadType) - { - case 0://dialog - myWindow->loadFilesFromCommandLine(myState.fileList, BrainBrowserWindow::LOAD_SPEC_FILE_WITH_DIALOG_VIA_COMMAND_LINE); - break; - case 1://load all - myWindow->loadFilesFromCommandLine(myState.fileList, BrainBrowserWindow::LOAD_SPEC_FILE_CONTENTS_VIA_COMMAND_LINE); - break; - default: - CaretAssert(false); - } + else if ( ! myState.specFileNameLoadWithDialog.isEmpty()) { + myWindow->loadFilesFromCommandLine({ myState.specFileNameLoadWithDialog }, + BrainBrowserWindow::LOAD_SPEC_FILE_WITH_DIALOG_VIA_COMMAND_LINE); } - if (myState.sceneFileName.isEmpty() == false) { + if (! myState.fileList.empty()) { + myWindow->loadFilesFromCommandLine(myState.fileList, + BrainBrowserWindow::LOAD_SPEC_FILE_WITH_DIALOG); + } + + if ( ! myState.sceneFileName.isEmpty()) { myWindow->loadSceneFromCommandLine(myState.sceneFileName, myState.sceneNameOrNumber); } +#ifndef WORKBENCH_USE_QT5_QOPENGL_WIDGET if (QGLPixelBuffer::hasOpenGLPbuffers()) { CaretLogConfig("OpenGL PBuffers are supported"); } else { CaretLogConfig("OpenGL PBuffers are NOT supported"); } - +#endif + /* * Log local (language, country) */ @@ -659,10 +733,6 @@ void printHelp(const AString& progName) iter++) { cout << " " << qPrintable(LogLevelEnum::toName(*iter)) << endl; } -// foreach (LogLevelEnum::Enum level , logLevels) { -// for (LogLevelEnum::Enum level : logLevels) { -// cout << " " << qPrintable(LogLevelEnum::toName(level)) << endl; -// } cout << endl @@ -707,6 +777,8 @@ void parseCommandLine(const AString& progName, ProgramParameters* myParams, Prog { bool hasFatalError = false; + const AString moreThanOneSpecFileErrorMessage("More than one spec file is NOT allowed in options"); + try { while (myParams->hasNext()) { @@ -758,7 +830,16 @@ void parseCommandLine(const AString& progName, ProgramParameters* myParams, Prog hasFatalError = true; } } else if (thisParam == "-spec-load-all") { - myState.specLoadType = 1; + if ( ! myState.specFileNameLoadAll.isEmpty()) { + cerr << qPrintable(moreThanOneSpecFileErrorMessage) << endl; + } + else if (myParams->hasNext()) { + myState.specFileNameLoadAll = myParams->nextString("Spec File Name"); + } + else { + cerr << "Missing spec file name for \"-spec\" option" << endl; + hasFatalError = true; + } } else if (thisParam == "-graphics-size") { if (myParams->hasNext()) { myState.graphicsSizeXY[0] = myParams->nextInt("Graphics Size X"); @@ -819,13 +900,33 @@ void parseCommandLine(const AString& progName, ProgramParameters* myParams, Prog * http://stackoverflow.com/questions/10242115/os-x-strange-psn-command-line-parameter-when-launched-from-finder * http://trac.wxwidgets.org/ticket/15432 */ + } else if (thisParam == "-NSDocumentRevisionsDebugMode") { + /* + * When wb_view is started within Apple's XCode, these parameters are added + * so ignore them: + * -NSDocumentRevisionsDebugMode YES + */ + if (myParams->hasNext()) { + myParams->nextString("Argument to -NSDocumentRevisionsDebugMode"); + } } else { cerr << "unrecognized option \"" << thisParam << "\"" << endl; printHelp(progName); hasFatalError = true; } } else { - myState.fileList.push_back(thisParam); + if (thisParam.endsWith(DataFileTypeEnum::toFileExtension(DataFileTypeEnum::SPECIFICATION))) { + if ( ! myState.specFileNameLoadWithDialog.isEmpty()) { + cerr << qPrintable(moreThanOneSpecFileErrorMessage) << endl; + hasFatalError = true; + } + else { + myState.specFileNameLoadWithDialog = thisParam; + } + } + else { + myState.fileList.push_back(thisParam); + } } } } @@ -834,16 +935,37 @@ void parseCommandLine(const AString& progName, ProgramParameters* myParams, Prog hasFatalError = true; } + if ( ( ! myState.specFileNameLoadWithDialog.isEmpty()) + && ( ! myState.specFileNameLoadAll.isEmpty())) { + cerr << qPrintable(moreThanOneSpecFileErrorMessage) << endl; + hasFatalError = true; + } + if (hasFatalError) { exit(-1); } + + /* + * If any files are listed, disable splash screen + */ + if ( ! myState.fileList.empty()) { + myState.showSplash = false; + } + if ( ! myState.sceneFileName.isEmpty()) { + myState.showSplash = false; + } + if ( ! myState.specFileNameLoadWithDialog.isEmpty()) { + myState.showSplash = false; + } + if ( ! myState.specFileNameLoadAll.isEmpty()) { + myState.showSplash = false; + } } ProgramState::ProgramState() { sceneFileName = ""; sceneNameOrNumber = ""; - specLoadType = 0;//0: use spec window, 1: all windowSizeXY[0] = -1; windowSizeXY[1] = -1; windowPosXY[0] = -1; diff --git a/src/Files/AffineFile.cxx b/src/Files/AffineFile.cxx index 54c1b2858e8384176540620e43d2267f520106db..386c3f09b4674a22d251de4dbadb9c313ab9c361 100644 --- a/src/Files/AffineFile.cxx +++ b/src/Files/AffineFile.cxx @@ -20,11 +20,13 @@ #include "AffineFile.h" #include "CaretAssert.h" +#include "CaretLogger.h" #include "DataFileException.h" #include "FileInformation.h" #include "NiftiIO.h" #include +#include #include using namespace caret; @@ -85,14 +87,18 @@ void AffineFile::write44(const FloatMatrix& out, const AString& filename) } } -void AffineFile::readWorld(const AString& filename) +void AffineFile::readWorld(const AString& filename, bool inverse) { - m_matrix = read34(filename);//and that is it, no quirks + FloatMatrix temp = read34(filename); + if (inverse) temp = temp.inverse();//support reading reverse affines + m_matrix = temp; } -void AffineFile::writeWorld(const AString& filename) +void AffineFile::writeWorld(const AString& filename, bool inverse) const { - write44(m_matrix, filename);//ditto + FloatMatrix temp = m_matrix; + if (inverse) temp = temp.inverse();//and writing + write44(temp, filename); } void AffineFile::readFlirt(const AString& filename, const AString& sourceName, const AString& targetName) @@ -121,3 +127,87 @@ void AffineFile::getFSLQuirks(const AString& niftiName, FloatMatrix& outSform, F outSform = FloatMatrix(myIO.getHeader().getSForm());//NOTE: this is expected to return a 4x4 matrix with the 0 0 0 1 row intact outScale = FloatMatrix(myIO.getHeader().getFSLSpace()); } + +void AffineFile::readITK(const AString& filename) +{//odd text format, need special parsing + FileInformation itkInfo(filename); + if (!itkInfo.exists()) throw DataFileException(filename, "ITK affine file does not exist"); + fstream itkFile(filename.toLocal8Bit().constData(), fstream::in); + if (!itkFile.good()) throw DataFileException(filename, "error opening file for reading"); + string myline; + if (!getline(itkFile, myline)) throw DataFileException(filename, "ITK affine file is empty"); + if (myline != "#Insight Transform File V1.0") throw DataFileException(filename, "wrong header line for ITK affine file"); + bool haveParam = false, haveFixed = false;//maybe they can be reordered, so just wait for both + vector params(12, 0.0f), fixed(3, 0.0f); + while (!(haveParam && haveFixed)) + { + if (!getline(itkFile, myline)) throw DataFileException(filename, "ITK affine file is truncated"); + if (AString::fromStdString(myline).startsWith("Transform: ")) + { + if (myline != "Transform: MatrixOffsetTransformBase_double_3_3") throw DataFileException(filename, "unrecognized transformation type in ITK affine file"); + } else if (!haveParam && AString::fromStdString(myline).startsWith("Parameters: ")) {//don't parse more than one Parameters line, to make sure we get the first + stringstream parsestring(myline); + string junk; + parsestring >> junk;//strip "Parameters: " + for (size_t i = 0; i < params.size(); ++i) + { + if (!(parsestring >> params[i])) throw DataFileException(filename, "expected 12 values in Parameters line, only parsed " + AString::number(i)); + } + haveParam = true; + if (parsestring >> junk) CaretLogWarning("found extra characters on end of Parameters line in file '" + filename + "'"); + } else if (!haveFixed && AString::fromStdString(myline).startsWith("FixedParameters: ")) {//ditto + stringstream parsestring(myline); + string junk; + parsestring >> junk;//strip "FixedParameters: " + for (size_t i = 0; i < fixed.size(); ++i) + { + if (!(parsestring >> fixed[i])) throw DataFileException(filename, "expected 3 values in FixedParameters line, only parsed " + AString::number(i)); + } + haveFixed = true; + if (parsestring >> junk) CaretLogWarning("found extra characters on end of FixedParameters line in file '" + filename + "'"); + } + } + FloatMatrix temp = FloatMatrix::identity(4);//to ensure the right size and the fourth 0 0 0 1 row + for (int i = 0; i < 3; ++i) + { + temp[i][3] = params[9 + i] + fixed[i];//"ComputeOffset", https://github.com/hinerm/ITK/blob/master/Modules/Core/Transform/include/itkMatrixOffsetTransformBase.hxx#L729 + for (int j = 0; j < 3; ++j) + { + temp[i][j] = params[i * 3 + j];//https://afni.nimh.nih.gov/afni/community/board/read.php?1,94478 + temp[i][3] -= temp[i][j] * fixed[j];//the rest of the first link + } + } + FloatMatrix flips = FloatMatrix::identity(4); + flips[0][0] = -1; + flips[1][1] = -1;//this is its own inverse, so we don't need to compute the inverse + m_matrix = (flips * temp * flips).inverse();//convert from reverse, LPS transform to forwards, RAS transform +} + +void AffineFile::writeITK(const AString& filename) const +{ + fstream itkFile(filename.toLocal8Bit().constData(), fstream::out); + FloatMatrix flips = FloatMatrix::identity(4); + flips[0][0] = -1; + flips[1][1] = -1;//this is its own inverse, so we don't need to compute the inverse + FloatMatrix temp = (flips * m_matrix * flips).inverse();//convert from forwards RAS to inverse LPS + if (!itkFile.good()) throw DataFileException(filename, "error opening ITK affine file for writing"); + itkFile.setf(ios::fixed, ios::floatfield); + itkFile.precision(16);//ITK uses 16 because it uses doubles, we only use single, but make them look similar anyway + itkFile << "#Insight Transform File V1.0" << endl; + itkFile << "#Transform 0" << endl; + itkFile << "Transform: MatrixOffsetTransformBase_double_3_3" << endl; + itkFile << "Parameters: "; + for (int i = 0; i < 3; ++i) + { + for (int j = 0; j < 3; ++j) + { + itkFile << " " << temp[i][j]; + } + } + for (int i = 0; i < 3; ++i)//use 0, 0, 0 center to eliminate translation/offset conversion silliness + { + itkFile << " " << temp[i][3]; + } + itkFile << endl; + itkFile << "FixedParameters: 0 0 0" << endl; +} diff --git a/src/Files/AffineFile.h b/src/Files/AffineFile.h index 609df0e0611dc5a19a5f0c92234536f22c2a657b..e26201d5c695f30c7fefa7093c704f5ded904768 100644 --- a/src/Files/AffineFile.h +++ b/src/Files/AffineFile.h @@ -34,11 +34,13 @@ namespace caret { static void getFSLQuirks(const AString& niftiName, FloatMatrix& outSform, FloatMatrix& outScale);//just a convenience wrapper around the vector > version in NiftiHeaderIO public: AffineFile(); - void readWorld(const AString& filename);//forward nifti coordinate transform - void writeWorld(const AString& filename); + void readWorld(const AString& filename, bool inverse = false);//forward nifti coordinate transform, optionally reverse + void writeWorld(const AString& filename, bool inverse = false) const; void readFlirt(const AString& filename, const AString& sourceName, const AString& targetName);//flirt convention matrix, requires source/target volumes void writeFlirt(const AString& filename, const AString& sourceName, const AString& targetName) const; - const FloatMatrix& getMatrix() { return m_matrix; } + void readITK(const AString& filename);//reverse, LPS rather than RAS, with strange encoding for translation + void writeITK(const AString& filename) const; + const FloatMatrix& getMatrix() const { return m_matrix; } void setMatrix(const FloatMatrix& matrix);//needs to do sanity checking, so don't inline }; diff --git a/src/Files/AnnotationFile.cxx b/src/Files/AnnotationFile.cxx index 4af8b8ae62e7b71403224f355d42087fdc4a3352..449419cdb227541a805533013b05f2f5c1a544c2 100644 --- a/src/Files/AnnotationFile.cxx +++ b/src/Files/AnnotationFile.cxx @@ -42,6 +42,7 @@ #include "EventAnnotationAddToRemoveFromFile.h" #include "EventAnnotationGroupGetWithKey.h" #include "EventAnnotationGrouping.h" +#include "EventBrowserTabDelete.h" #include "EventManager.h" #include "GiftiMetaData.h" #include "SceneClass.h" @@ -218,6 +219,7 @@ AnnotationFile::initializeAnnotationFile() EventManager::get()->addEventListener(this, EventTypeEnum::EVENT_ANNOTATION_ADD_TO_REMOVE_FROM_FILE); EventManager::get()->addEventListener(this, EventTypeEnum::EVENT_ANNOTATION_GROUP_GET_WITH_KEY); EventManager::get()->addEventListener(this, EventTypeEnum::EVENT_ANNOTATION_GROUPING); + EventManager::get()->addEventListener(this, EventTypeEnum::EVENT_BROWSER_TAB_DELETE); } /** @@ -235,7 +237,7 @@ AnnotationFile::getSpaceAnnotationGroup(const Annotation* annotation) const AnnotationCoordinateSpaceEnum::Enum annotationSpace = annotation->getCoordinateSpace(); int32_t annotationTabOrWindowIndex = -1; switch (annotationSpace) { - case AnnotationCoordinateSpaceEnum::PIXELS: + case AnnotationCoordinateSpaceEnum::CHART: break; case AnnotationCoordinateSpaceEnum::STEREOTAXIC: break; @@ -244,6 +246,8 @@ AnnotationFile::getSpaceAnnotationGroup(const Annotation* annotation) case AnnotationCoordinateSpaceEnum::TAB: annotationTabOrWindowIndex = annotation->getTabIndex(); break; + case AnnotationCoordinateSpaceEnum::VIEWPORT: + break; case AnnotationCoordinateSpaceEnum::WINDOW: annotationTabOrWindowIndex = annotation->getWindowIndex(); break; @@ -256,13 +260,14 @@ AnnotationFile::getSpaceAnnotationGroup(const Annotation* annotation) if (group->getGroupType() == AnnotationGroupTypeEnum::SPACE) { if (group->getCoordinateSpace() == annotationSpace) { switch (annotationSpace) { - case AnnotationCoordinateSpaceEnum::PIXELS: - CaretAssert(0); - break; + case AnnotationCoordinateSpaceEnum::CHART: case AnnotationCoordinateSpaceEnum::STEREOTAXIC: case AnnotationCoordinateSpaceEnum::SURFACE: return group; break; + case AnnotationCoordinateSpaceEnum::VIEWPORT: + CaretAssert(0); + break; case AnnotationCoordinateSpaceEnum::TAB: case AnnotationCoordinateSpaceEnum::WINDOW: if (annotationTabOrWindowIndex == group->getTabOrWindowIndex()) { @@ -275,9 +280,7 @@ AnnotationFile::getSpaceAnnotationGroup(const Annotation* annotation) } switch (annotationSpace) { - case AnnotationCoordinateSpaceEnum::PIXELS: - CaretAssert(0); - break; + case AnnotationCoordinateSpaceEnum::CHART: case AnnotationCoordinateSpaceEnum::STEREOTAXIC: case AnnotationCoordinateSpaceEnum::SURFACE: break; @@ -285,6 +288,9 @@ AnnotationFile::getSpaceAnnotationGroup(const Annotation* annotation) CaretAssert((annotationTabOrWindowIndex >= 0) && (annotationTabOrWindowIndex < BrainConstants::MAXIMUM_NUMBER_OF_BROWSER_TABS)); break; + case AnnotationCoordinateSpaceEnum::VIEWPORT: + CaretAssert(0); + break; case AnnotationCoordinateSpaceEnum::WINDOW: CaretAssert((annotationTabOrWindowIndex >= 0) && (annotationTabOrWindowIndex < BrainConstants::MAXIMUM_NUMBER_OF_BROWSER_WINDOWS)); @@ -341,15 +347,22 @@ AnnotationFile::receiveEvent(Event* event) } break; case EventAnnotationAddToRemoveFromFile::MODE_CUT: - if (removeAnnotation(annotation)) { + if (removeAnnotationWithUndoRedo(annotation)) { annEvent->setSuccessful(true); } break; case EventAnnotationAddToRemoveFromFile::MODE_DELETE: - if (removeAnnotation(annotation)) { + if (removeAnnotationWithUndoRedo(annotation)) { annEvent->setSuccessful(true); } break; + case EventAnnotationAddToRemoveFromFile::MODE_DUPLICATE: + if (annotationFile == this) { + if (restoreAnnotationAddIfNotFound(annotation)) { + annEvent->setSuccessful(true); + } + } + break; case EventAnnotationAddToRemoveFromFile::MODE_PASTE: if (annotationFile == this) { if (restoreAnnotationAddIfNotFound(annotation)) { @@ -359,7 +372,7 @@ AnnotationFile::receiveEvent(Event* event) break; case EventAnnotationAddToRemoveFromFile::MODE_UNCREATE: if (annotationFile == this) { - if (removeAnnotation(annotation)) { + if (removeAnnotationWithUndoRedo(annotation)) { annEvent->setSuccessful(true); } } @@ -374,9 +387,15 @@ AnnotationFile::receiveEvent(Event* event) annEvent->setSuccessful(true); } break; + case EventAnnotationAddToRemoveFromFile::MODE_UNDUPLICATE: + if (annotationFile == this) { + if (removeAnnotationWithUndoRedo(annotation)) { + annEvent->setSuccessful(true); + } + } case EventAnnotationAddToRemoveFromFile::MODE_UNPASTE: if (annotationFile == this) { - if (removeAnnotation(annotation)) { + if (removeAnnotationWithUndoRedo(annotation)) { annEvent->setSuccessful(true); } } @@ -419,6 +438,15 @@ AnnotationFile::receiveEvent(Event* event) } } } + else if (event->getEventType() == EventTypeEnum::EVENT_BROWSER_TAB_DELETE) { + EventBrowserTabDelete* deleteEvent = dynamic_cast(event); + CaretAssert(deleteEvent); + + const int32_t tabIndex = deleteEvent->getBrowserTabIndex(); + if (removeAnnotationsInTab(tabIndex)) { + deleteEvent->setEventProcessed(); + } + } } /** @@ -607,13 +635,15 @@ AnnotationFile::addAnnotationGroupDuringFileReading(const AnnotationGroupTypeEnu } switch (coordinateSpace) { - case AnnotationCoordinateSpaceEnum::PIXELS: - throw DataFileException("PIXELS coordinate space is not allowed for group while annotation file."); + case AnnotationCoordinateSpaceEnum::CHART: break; case AnnotationCoordinateSpaceEnum::STEREOTAXIC: break; case AnnotationCoordinateSpaceEnum::SURFACE: break; + case AnnotationCoordinateSpaceEnum::VIEWPORT: + throw DataFileException("VIEWPORT coordinate space is not allowed for group while annotation file."); + break; case AnnotationCoordinateSpaceEnum::TAB: case AnnotationCoordinateSpaceEnum::WINDOW: if ((tabOrWindowIndex < 0) @@ -653,9 +683,7 @@ AnnotationFile::addAnnotationGroupDuringFileReading(const AnnotationGroupTypeEnu if (groupType == AnnotationGroupTypeEnum::SPACE) { if (group->getCoordinateSpace() == coordinateSpace) { switch (coordinateSpace) { - case AnnotationCoordinateSpaceEnum::PIXELS: - CaretAssert(0); - break; + case AnnotationCoordinateSpaceEnum::CHART: case AnnotationCoordinateSpaceEnum::STEREOTAXIC: case AnnotationCoordinateSpaceEnum::SURFACE: throw DataFileException("There is more than one annotation space group with space " @@ -671,6 +699,9 @@ AnnotationFile::addAnnotationGroupDuringFileReading(const AnnotationGroupTypeEnu + ". Only one space group for each space is allowed."); } break; + case AnnotationCoordinateSpaceEnum::VIEWPORT: + CaretAssert(0); + break; case AnnotationCoordinateSpaceEnum::WINDOW: if (tabOrWindowIndex == group->getTabOrWindowIndex()) { throw DataFileException("There is more than one annotation space group with space " @@ -762,9 +793,9 @@ AnnotationFile::restoreAnnotation(Annotation* annotation) } /** - * Remove the annotation. NOTE: The annotation is NOT deleted - * but instead it is saved so that it can be 'undeleted' - * or 're-pasted'. + * Remove the annotation for use with REDO/UNDO. + * NOTE: The annotation is NOT deleted but instead + * it is saved so that it can be 'undeleted' or 're-pasted'. * * @param annotation * Annotation that is removed. @@ -772,9 +803,56 @@ AnnotationFile::restoreAnnotation(Annotation* annotation) * True if the annotation was removed, otherwise false. */ bool -AnnotationFile::removeAnnotation(Annotation* annotation) +AnnotationFile::removeAnnotationWithUndoRedo(Annotation* annotation) { + return removeAnnotationPrivate(annotation, true); + +// for (AnnotationGroupIterator groupIter = m_annotationGroups.begin(); +// groupIter != m_annotationGroups.end(); +// groupIter++) { +// QSharedPointer group = *groupIter; +// QSharedPointer removedAnnotationPointer; +// if (group->removeAnnotation(annotation, +// removedAnnotationPointer)) { +// +// removedAnnotationPointer->invalidateAnnotationGroupKey(); +// +// m_removedAnnotations.insert(removedAnnotationPointer); +// +// /* +// * Remove group if it is empty. +// */ +// if (group->isEmpty()) { +// m_annotationGroups.erase(groupIter); +// } +// +// setModified(); +// return true; +// } +// } +// +// /* +// * Annotation not in this file +// */ +// return false; +} + +/** + * Remove the + * + * @param annotation + * Annotation that is removed. + * @param keepAnnotationForUndoRedoFlag + * If true, keep the annotation available for any + * undo or redo operation. + * @return + * True if the annotation was removed, otherwise false. + */ +bool +AnnotationFile::removeAnnotationPrivate(Annotation* annotation, + const bool keepAnnotationForUndoRedoFlag) +{ for (AnnotationGroupIterator groupIter = m_annotationGroups.begin(); groupIter != m_annotationGroups.end(); groupIter++) { @@ -785,7 +863,9 @@ AnnotationFile::removeAnnotation(Annotation* annotation) removedAnnotationPointer->invalidateAnnotationGroupKey(); - m_removedAnnotations.insert(removedAnnotationPointer); + if (keepAnnotationForUndoRedoFlag) { + m_removedAnnotations.insert(removedAnnotationPointer); + } /* * Remove group if it is empty. @@ -798,23 +878,6 @@ AnnotationFile::removeAnnotation(Annotation* annotation) return true; } } -// for (AnnotationIterator iter = m_annotations.begin(); -// iter != m_annotations.end(); -// iter++) { -// QSharedPointer& annotationPointer = *iter; -// if (annotationPointer == annotation) { -// m_removedAnnotations.insert(annotationPointer); -// -// m_annotations.erase(iter); -// -// setModified(); -// -// /* -// * Successfully removed -// */ -// return true; -// } -// } /* * Annotation not in this file @@ -822,6 +885,50 @@ AnnotationFile::removeAnnotation(Annotation* annotation) return false; } +/** + * Remove all annotations in tab space in the given tab. + * + * @param tabIndex + * Index of the tab. + * @return + * True if any annotations were removed. + */ +bool +AnnotationFile::removeAnnotationsInTab(const int32_t tabIndex) +{ + std::vector groupsToRemoveIterators; + + /* + * Find annotation group(s) in tab space + * with the given tab index. + */ + for (AnnotationGroupIterator groupIter = m_annotationGroups.begin(); + groupIter != m_annotationGroups.end(); + groupIter++) { + QSharedPointer& group = *groupIter; + if (group->getCoordinateSpace() == AnnotationCoordinateSpaceEnum::TAB) { + if (group->getTabOrWindowIndex() == tabIndex) { + groupsToRemoveIterators.push_back(groupIter); + } + } + } + + /* + * Remove the groups (shared pointers will do all deleting) + * NOTE: MUST delete iterators that are closest to the end first + * as any iterators that point to elements at the erased iterator + * or beyond are invalidated. + */ + const int32_t numGroupsToRemove = static_cast(groupsToRemoveIterators.size()); + for (int32_t i = (numGroupsToRemove - 1); i >= 0; i--) { + CaretAssertVectorIndex(groupsToRemoveIterators, i); + m_annotationGroups.erase(groupsToRemoveIterators[i]); + } + + return ( ! groupsToRemoveIterators.empty()); +} + + /** * Get all annotations in this file. * @@ -1139,6 +1246,17 @@ AnnotationFile::processRegroupingAnnotations(EventAnnotationGrouping* groupingEv } } + if ( ! allValidFlag) { + for (std::vector >::iterator annPtrIter = movedAnnotations.begin(); + annPtrIter != movedAnnotations.end(); + annPtrIter++) { + spaceGroup->addAnnotationPrivateSharedPointer(*annPtrIter); + } + + groupingEvent->setErrorMessage("PROGRAM ERROR: Failed to remove an anntotation from its space group."); + return; + } + for (std::vector >::iterator annPtrIter = movedAnnotations.begin(); annPtrIter != movedAnnotations.end(); annPtrIter++) { diff --git a/src/Files/AnnotationFile.h b/src/Files/AnnotationFile.h index ecc6f54f0c752f3b8817befe18dfb9be1fdbbe99..a4b9859728c0473af08d3a3c85004810224dfd88 100644 --- a/src/Files/AnnotationFile.h +++ b/src/Files/AnnotationFile.h @@ -195,7 +195,12 @@ namespace caret { bool restoreAnnotationAddIfNotFound(Annotation* annotation); - bool removeAnnotation(Annotation* annotation); + bool removeAnnotationWithUndoRedo(Annotation* annotation); + + bool removeAnnotationPrivate(Annotation* annotation, + const bool keepAnnotationForUndoRedoFlag); + + bool removeAnnotationsInTab(const int32_t tabIndex); int32_t generateUniqueKey(); diff --git a/src/Files/AnnotationFileXmlFormatBase.h b/src/Files/AnnotationFileXmlFormatBase.h index a06c4241a6d01da8caa1c4d7b64be851c77cf3f3..66e58272ba15b29119bb55566fe325dab52c9010 100644 --- a/src/Files/AnnotationFileXmlFormatBase.h +++ b/src/Files/AnnotationFileXmlFormatBase.h @@ -74,7 +74,9 @@ namespace caret { static const QString ATTRIBUTE_FOREGROUND_CUSTOM_RGBA; - static const QString ATTRIBUTE_FOREGROUND_LINE_WIDTH; + static const QString ATTRIBUTE_FOREGROUND_LINE_WIDTH_PIXELS; + + static const QString ATTRIBUTE_FOREGROUND_LINE_WIDTH_PERCENTAGE; static const QString ATTRIBUTE_GROUP_TYPE; @@ -148,6 +150,8 @@ namespace caret { static const QString ELEMENT_PERCENT_SIZE_TEXT; + static const QString ELEMENT_PERCENT_WIDTH_SIZE_TEXT; + static const QString ELEMENT_POINT_SIZE_TEXT; static const QString ELEMENT_TEXT_OBSOLETE; @@ -190,7 +194,9 @@ namespace caret { const QString AnnotationFileXmlFormatBase::ATTRIBUTE_FOREGROUND_CUSTOM_RGBA = "foregroundCustomRGBA"; - const QString AnnotationFileXmlFormatBase::ATTRIBUTE_FOREGROUND_LINE_WIDTH = "foregroundLineWidth"; + const QString AnnotationFileXmlFormatBase::ATTRIBUTE_FOREGROUND_LINE_WIDTH_PIXELS = "foregroundLineWidth"; + + const QString AnnotationFileXmlFormatBase::ATTRIBUTE_FOREGROUND_LINE_WIDTH_PERCENTAGE = "foregroundLineWidthPercentage"; const QString AnnotationFileXmlFormatBase::ATTRIBUTE_GROUP_TYPE = "groupType"; @@ -264,6 +270,8 @@ namespace caret { const QString AnnotationFileXmlFormatBase::ELEMENT_PERCENT_SIZE_TEXT = "percentSizeText"; + const QString AnnotationFileXmlFormatBase::ELEMENT_PERCENT_WIDTH_SIZE_TEXT = "percentWidthSizeText"; + const QString AnnotationFileXmlFormatBase::ELEMENT_POINT_SIZE_TEXT = "pointSizeText"; const QString AnnotationFileXmlFormatBase::ELEMENT_TEXT_OBSOLETE = "text"; diff --git a/src/Files/AnnotationFileXmlReader.cxx b/src/Files/AnnotationFileXmlReader.cxx index 6f0a8fb7ef43de443b533acfcd39dfc059240862..aab0fb224644e54831da56414eea28c1508ee5ad 100644 --- a/src/Files/AnnotationFileXmlReader.cxx +++ b/src/Files/AnnotationFileXmlReader.cxx @@ -252,6 +252,13 @@ AnnotationFileXmlReader::readVersionOne(AnnotationFile* annotationFile) annotation); annotationFile->addAnnotationDuringFileVersionOneReading(annotation.releasePointer()); } + else if (elementName == ELEMENT_PERCENT_WIDTH_SIZE_TEXT) { + CaretPointer annotation(new AnnotationPercentSizeText(AnnotationAttributesDefaultTypeEnum::NORMAL, + AnnotationTextFontSizeTypeEnum::PERCENTAGE_OF_VIEWPORT_WIDTH)); + readTwoDimensionalAnnotation(ELEMENT_PERCENT_WIDTH_SIZE_TEXT, + annotation); + annotationFile->addAnnotationDuringFileVersionOneReading(annotation.releasePointer()); + } else if (elementName == ELEMENT_POINT_SIZE_TEXT) { CaretPointer annotation(new AnnotationPointSizeText(AnnotationAttributesDefaultTypeEnum::NORMAL)); readTwoDimensionalAnnotation(ELEMENT_POINT_SIZE_TEXT, @@ -534,9 +541,20 @@ AnnotationFileXmlReader::readAnnotationAttributes(Annotation* annotation, /* * Foreground line width */ - annotation->setLineWidth(m_streamHelper->getRequiredAttributeFloatValue(attributes, + annotation->setLineWidthPixelsObsolete(m_streamHelper->getRequiredAttributeFloatValue(attributes, annotationElementName, - ATTRIBUTE_FOREGROUND_LINE_WIDTH)); + ATTRIBUTE_FOREGROUND_LINE_WIDTH_PIXELS)); + + /* + * Line width percentage added on July 26, 2017. + * A "negative value" indicates it is missing and the percentage width + * may be set by graphics code that attempts to set the percentage using + * the obsolete pixel width and viewport height. + */ + annotation->setLineWidthPercentage(m_streamHelper->getOptionalAttributeFloatValue(attributes, + annotationElementName, + ATTRIBUTE_FOREGROUND_LINE_WIDTH_PERCENTAGE, + -1.0f)); /* * Tab Index */ @@ -612,7 +630,7 @@ AnnotationFileXmlReader::readGroup(AnnotationFile* annotationFile) /* * Coordinate space */ - AnnotationCoordinateSpaceEnum::Enum coordSpace = AnnotationCoordinateSpaceEnum::PIXELS; + AnnotationCoordinateSpaceEnum::Enum coordSpace = AnnotationCoordinateSpaceEnum::VIEWPORT; { const QString valueString = m_streamHelper->getRequiredAttributeStringValue(attributes, ELEMENT_GROUP, @@ -692,6 +710,12 @@ AnnotationFileXmlReader::readGroup(AnnotationFile* annotationFile) annotation); annotations.push_back(annotation.releasePointer()); } + else if (elementName == ELEMENT_PERCENT_WIDTH_SIZE_TEXT) { + CaretPointer annotation(new AnnotationPercentSizeText(AnnotationAttributesDefaultTypeEnum::NORMAL)); + readTwoDimensionalAnnotation(ELEMENT_PERCENT_WIDTH_SIZE_TEXT, + annotation); + annotationFile->addAnnotationDuringFileVersionOneReading(annotation.releasePointer()); + } else if (elementName == ELEMENT_POINT_SIZE_TEXT) { CaretPointer annotation(new AnnotationPointSizeText(AnnotationAttributesDefaultTypeEnum::NORMAL)); readTwoDimensionalAnnotation(ELEMENT_POINT_SIZE_TEXT, @@ -836,7 +860,7 @@ AnnotationFileXmlReader::readImageDataElement(AnnotationImage* imageAnnotation) + m_stream->errorString()); } - QByteArray imageBytes = QByteArray::fromBase64(imageChars.toAscii()); + QByteArray imageBytes = QByteArray::fromBase64(imageChars.toLatin1()); if (imageBytes.size() == numberOfBytes) { const uint8_t* imageBytesPointer = (const uint8_t*)(imageBytes.data()); imageAnnotation->setImageBytesRGBA(imageBytesPointer, @@ -1001,7 +1025,8 @@ AnnotationFileXmlReader::readTextDataElement(AnnotationText *textAnnotation, } } - if (annotationTextElementName != ELEMENT_PERCENT_SIZE_TEXT) { + if ((annotationTextElementName != ELEMENT_PERCENT_SIZE_TEXT) + && (annotationTextElementName != ELEMENT_PERCENT_WIDTH_SIZE_TEXT)) { const QString valueString = m_streamHelper->getRequiredAttributeStringValue(attributes, ELEMENT_TEXT_DATA, ATTRIBUTE_TEXT_FONT_POINT_SIZE, @@ -1079,6 +1104,11 @@ AnnotationFileXmlReader::readTextDataElement(AnnotationText *textAnnotation, ELEMENT_TEXT_DATA, ATTRIBUTE_TEXT_FONT_PERCENT_VIEWPORT_SIZE)); } + else if (annotationTextElementName == ELEMENT_PERCENT_WIDTH_SIZE_TEXT) { + textAnnotation->setFontPercentViewportSizeProtected(m_streamHelper->getRequiredAttributeFloatValue(attributes, + ELEMENT_TEXT_DATA, + ATTRIBUTE_TEXT_FONT_PERCENT_VIEWPORT_SIZE)); + } else { textAnnotation->setFontPercentViewportSizeProtected(m_streamHelper->getOptionalAttributeFloatValue(attributes, ELEMENT_TEXT_DATA, @@ -1095,6 +1125,12 @@ AnnotationFileXmlReader::readTextDataElement(AnnotationText *textAnnotation, */ textAnnotation->setFontPercentViewportSizeProtected(textAnnotation->getFontPercentViewportSizeProtected()); } + else if (annotationTextElementName == ELEMENT_PERCENT_WIDTH_SIZE_TEXT) { + /* + * Will cause warning or assertion failure if invalid value + */ + textAnnotation->setFontPercentViewportSizeProtected(textAnnotation->getFontPercentViewportSizeProtected()); + } else if (annotationTextElementName == ELEMENT_POINT_SIZE_TEXT) { } @@ -1104,6 +1140,8 @@ AnnotationFileXmlReader::readTextDataElement(AnnotationText *textAnnotation, + "\" expected " + ELEMENT_PERCENT_SIZE_TEXT + " or " + + ELEMENT_PERCENT_WIDTH_SIZE_TEXT + + " or " + ELEMENT_POINT_SIZE_TEXT); } diff --git a/src/Files/AnnotationFileXmlWriter.cxx b/src/Files/AnnotationFileXmlWriter.cxx index 74097ee5530da39fb996c89ae70f61821115c067..3b0edb917d06c1acf7ec603f191b5a06d5e65e89 100644 --- a/src/Files/AnnotationFileXmlWriter.cxx +++ b/src/Files/AnnotationFileXmlWriter.cxx @@ -317,8 +317,8 @@ AnnotationFileXmlWriter::writeImage(const AnnotationImage* image) QByteArray byteArray((const char*)imageBytesRGBA, numberOfBytes); QByteArray byteArrayBase64(byteArray.toBase64()); - const QString stringBase64(QString::fromAscii(byteArrayBase64.constData(), - byteArrayBase64.size())); + const QString stringBase64(QString::fromLatin1(byteArrayBase64.constData(), + byteArrayBase64.size())); QXmlStreamAttributes attributes; getTwoDimAnnotationPropertiesAsAttributes(image, @@ -427,6 +427,9 @@ AnnotationFileXmlWriter::writeText(const AnnotationText* text) case AnnotationTextFontSizeTypeEnum::PERCENTAGE_OF_VIEWPORT_HEIGHT: annotationElementName = ELEMENT_PERCENT_SIZE_TEXT; break; + case AnnotationTextFontSizeTypeEnum::PERCENTAGE_OF_VIEWPORT_WIDTH: + annotationElementName = ELEMENT_PERCENT_WIDTH_SIZE_TEXT; + break; case AnnotationTextFontSizeTypeEnum::POINTS: annotationElementName = ELEMENT_POINT_SIZE_TEXT; break; @@ -485,8 +488,11 @@ AnnotationFileXmlWriter::getAnnotationPropertiesAsAttributes(const Annotation* a attributes.append(ATTRIBUTE_FOREGROUND_CUSTOM_RGBA, realArrayToString(rgba, 4)); - attributes.append(ATTRIBUTE_FOREGROUND_LINE_WIDTH, - QString::number(annotation->getLineWidth())); + attributes.append(ATTRIBUTE_FOREGROUND_LINE_WIDTH_PIXELS, + QString::number(annotation->getLineWidthPixelsObsolete())); + + attributes.append(ATTRIBUTE_FOREGROUND_LINE_WIDTH_PERCENTAGE, + QString::number(annotation->getLineWidthPercentage())); attributes.append(ATTRIBUTE_TAB_INDEX, QString::number(annotation->getTabIndex())); diff --git a/src/Files/BorderFile.cxx b/src/Files/BorderFile.cxx index a8b470cbdf89787fb4c253ad7fe55840d0bf923a..13c0bb7913d649b9b1f64415b1ff904062c9314e 100644 --- a/src/Files/BorderFile.cxx +++ b/src/Files/BorderFile.cxx @@ -888,8 +888,7 @@ BorderFile::findBordersInsideRegionOfInterest(const DisplayGroupEnum::Enum displ std::vector >& insideCountAndBorderOut) const { CaretAssert(surfaceFile); - const int32_t surfaceNumberOfNodes = surfaceFile->getNumberOfNodes(); - CaretAssert(surfaceNumberOfNodes == static_cast(nodesInROI.size())); + CaretAssert(surfaceFile->getNumberOfNodes() == static_cast(nodesInROI.size())); insideCountAndBorderOut.clear(); diff --git a/src/Files/CMakeLists.txt b/src/Files/CMakeLists.txt index 60f36ee79980e61ced7ffe5ab4b8333cb48cd341..e109677ca8e131393aad87d2efbfba764c8ee836 100755 --- a/src/Files/CMakeLists.txt +++ b/src/Files/CMakeLists.txt @@ -3,16 +3,19 @@ # PROJECT (Files) -# -# Need XML from Qt -# -SET(QT_DONT_USE_QTGUI) -SET(QT_USE_QTNETWORK TRUE) - # # Add QT for includes # -INCLUDE (${QT_USE_FILE}) +if(Qt5_FOUND) + include_directories(${Qt5Core_INCLUDE_DIRS}) + include_directories(${Qt5Gui_INCLUDE_DIRS}) + include_directories(${Qt5Network_INCLUDE_DIRS}) +endif() +IF (QT4_FOUND) + SET(QT_DONT_USE_QTGUI) + SET(QT_USE_QTNETWORK TRUE) + INCLUDE(${QT_USE_FILE}) +ENDIF () # # Files Library @@ -32,7 +35,9 @@ BorderTracingHelper.h BrainordinateRegionOfInterest.h CaretDataFile.h CaretDataFileHelper.h +CaretDataFileSelectionModel.h CaretMappableDataFile.h +CaretMappableDataFileAndMapSelectionModel.h CaretSparseFile.h CaretVolumeExtension.h ChartableLineSeriesBrainordinateInterface.h @@ -41,6 +46,11 @@ ChartableLineSeriesRowColumnInterface.h ChartableMatrixInterface.h ChartableMatrixParcelInterface.h ChartableMatrixSeriesInterface.h +ChartableTwoFileDelegate.h +ChartableTwoFileBaseChart.h +ChartableTwoFileHistogramChart.h +ChartableTwoFileLineSeriesChart.h +ChartableTwoFileMatrixChart.h CiftiBrainordinateDataSeriesFile.h CiftiBrainordinateLabelFile.h CiftiBrainordinateScalarFile.h @@ -62,6 +72,8 @@ CiftiParcelScalarFile.h CiftiScalarDataSeriesFile.h ConnectivityDataLoaded.h ControlPointFile.h +EventCaretDataFilesGet.h +EventCaretMappableDataFileMapsViewedInOverlays.h EventCaretMappableDataFilesGet.h EventChartMatrixParcelYokingValidation.h EventGetDisplayedDataFiles.h @@ -115,6 +127,7 @@ StudyMetaDataLink.h StudyMetaDataLinkSet.h StudyMetaDataLinkSetSaxReader.h SurfaceFile.h +SurfacePlaneIntersectionToContour.h SurfaceProjectedItem.h SurfaceProjectedItemSaxReader.h SurfaceProjection.h @@ -153,11 +166,20 @@ BorderTracingHelper.cxx BrainordinateRegionOfInterest.cxx CaretDataFile.cxx CaretDataFileHelper.cxx +CaretDataFileSelectionModel.cxx CaretMappableDataFile.cxx +CaretMappableDataFileAndMapSelectionModel.cxx CaretSparseFile.cxx CaretVolumeExtension.cxx ChartableLineSeriesInterface.cxx ChartableMatrixInterface.cxx + +ChartableTwoFileDelegate.cxx +ChartableTwoFileBaseChart.cxx +ChartableTwoFileHistogramChart.cxx +ChartableTwoFileLineSeriesChart.cxx +ChartableTwoFileMatrixChart.cxx + CiftiBrainordinateDataSeriesFile.cxx CiftiBrainordinateLabelFile.cxx CiftiBrainordinateScalarFile.cxx @@ -179,6 +201,8 @@ CiftiParcelScalarFile.cxx CiftiScalarDataSeriesFile.cxx ConnectivityDataLoaded.cxx ControlPointFile.cxx +EventCaretDataFilesGet.cxx +EventCaretMappableDataFileMapsViewedInOverlays.cxx EventCaretMappableDataFilesGet.cxx EventChartMatrixParcelYokingValidation.cxx EventGetDisplayedDataFiles.cxx @@ -232,6 +256,7 @@ StudyMetaDataLink.cxx StudyMetaDataLinkSet.cxx StudyMetaDataLinkSetSaxReader.cxx SurfaceFile.cxx +SurfacePlaneIntersectionToContour.cxx SurfaceProjectedItem.cxx SurfaceProjectedItemSaxReader.cxx SurfaceProjection.cxx @@ -258,6 +283,7 @@ XmlStreamReaderHelper.cxx XmlStreamWriterHelper.cxx ) +TARGET_LINK_LIBRARIES(Files ${CARET_QT5_LINK}) # # Find Headers @@ -267,6 +293,7 @@ ${CMAKE_SOURCE_DIR}/Annotations ${CMAKE_SOURCE_DIR}/Charting ${CMAKE_SOURCE_DIR}/Files ${CMAKE_SOURCE_DIR}/FilesBase +${CMAKE_SOURCE_DIR}/Graphics ${CMAKE_SOURCE_DIR}/Cifti ${CMAKE_SOURCE_DIR}/Palette ${CMAKE_SOURCE_DIR}/Gifti diff --git a/src/Files/CaretDataFile.cxx b/src/Files/CaretDataFile.cxx index 5ae5561c7475d1e3175caf1975c853a31710204f..4865f0e77caa73d99d91af3345924b186c9b7131 100644 --- a/src/Files/CaretDataFile.cxx +++ b/src/Files/CaretDataFile.cxx @@ -47,14 +47,13 @@ SceneableInterface() { m_dataFileType = dataFileType; - AString name = (DataFileTypeEnum::toName(m_dataFileType).toLower() - + "_file_" + AString name = ("untitled_" + AString::number(s_defaultFileNameCounter) + "." + DataFileTypeEnum::toFileExtension(m_dataFileType)); s_defaultFileNameCounter++; - setFileName(name); + setFileNameProtected(name); } /** diff --git a/src/Files/CaretDataFileHelper.cxx b/src/Files/CaretDataFileHelper.cxx index 607ea0ec62b52954d3e636234f1630dec1e6a402..734d860599836e6137a47e33cd6c82e799568302 100644 --- a/src/Files/CaretDataFileHelper.cxx +++ b/src/Files/CaretDataFileHelper.cxx @@ -239,7 +239,7 @@ CaretDataFileHelper::readCaretDataFile(CaretDataFile* caretDataFile, try { caretDataFile->readFile(filename); } - catch (const std::bad_alloc& badAlloc) { + catch (const std::bad_alloc&) { /* * This DataFileException will be caught * in the outer try/catch and it will @@ -465,7 +465,7 @@ CaretDataFileHelper::getDataFileTypeFromFileContent(const AString& filename) break; } } - catch (const DataFileException& dfe) { + catch (const DataFileException&) { CaretLogInfo(filename + " could not be read as a CIFTI file"); } diff --git a/src/Brain/CaretDataFileSelectionModel.cxx b/src/Files/CaretDataFileSelectionModel.cxx similarity index 79% rename from src/Brain/CaretDataFileSelectionModel.cxx rename to src/Files/CaretDataFileSelectionModel.cxx index ee4cc8e18c24fcea1a2d0604cd90d777e12d89b4..2b02bfb162e43e2a0258503682bf6748cb7f29fa 100644 --- a/src/Brain/CaretDataFileSelectionModel.cxx +++ b/src/Files/CaretDataFileSelectionModel.cxx @@ -23,12 +23,13 @@ #include "CaretDataFileSelectionModel.h" #undef __CARET_DATA_FILE_SELECTION_MODEL_DECLARE__ -#include "BorderFile.h" -#include "Brain.h" #include "CaretAssert.h" #include "CaretDataFile.h" #include "CaretMappableDataFile.h" #include "ChartableMatrixParcelInterface.h" +#include "EventCaretDataFilesGet.h" +#include "EventCaretMappableDataFilesGet.h" +#include "EventManager.h" #include "SceneClass.h" #include "SceneClassAssistant.h" @@ -39,7 +40,7 @@ using namespace caret; /** * \class caret::CaretDataFileSelectionModel * \brief Selection model for a CaretDataFile. - * \ingroup Brain + * \ingroup Files * * Maintains selection of a type of CaretDataFile. Used in the GUI * by CaretDataFileSelectionComboBox. @@ -50,24 +51,34 @@ using namespace caret; * factory methods should be used to create new instances of * this class. * - * @param brain - * Brain containing the files. + * @param mappableDataFile + * Mappable data file for matching brainordinates. * @param structure * Structure for the files. * @param fileMode - * File mode that indicates how files are chosen from the 'Brain'. + * File mode that indicates how files are chosen. */ -CaretDataFileSelectionModel::CaretDataFileSelectionModel(Brain* brain, +CaretDataFileSelectionModel::CaretDataFileSelectionModel(const CaretMappableDataFile* mappableDataFile, const StructureEnum::Enum structure, const FileMode fileMode) : CaretObject(), +m_mappableDataFile(mappableDataFile), m_fileMode(fileMode), -m_brain(brain), m_structure(structure) { - m_overrideOfAvailableFilesValid = false; + switch (m_fileMode) { + case FILE_MODE_DATA_FILE_TYPE_ENUM: + break; + case FILE_MODE_CHARTABLE_MATRIX_PARCEL_INTERFACE: + break; + case FILE_MODE_MAPS_TO_SAME_BRAINORDINATES: + CaretAssert(m_mappableDataFile); + break; + case FILE_MODE_MULTI_STRUCTURE_BORDER_FILES: + break; + } - CaretAssert(brain); + m_overrideOfAvailableFilesValid = false; m_sceneAssistant = new SceneClassAssistant(); m_dataFileTypes.clear(); @@ -82,19 +93,33 @@ CaretDataFileSelectionModel::~CaretDataFileSelectionModel() } /** - * Create a new instance of a Caret Data File Selection Model that - * selects files of the given Data File Type from the given Brain. + * Create a new instance of a Caret Data File Selection Model that + * matches a mappable data file's brainordinate mapping. + * + * @param dataFileType + * Type of the data file. + */ +CaretDataFileSelectionModel* +CaretDataFileSelectionModel::newInstanceMapsToSameBrainordinates(const CaretMappableDataFile* mappableDataFile) +{ + CaretDataFileSelectionModel* model = new CaretDataFileSelectionModel(mappableDataFile, + StructureEnum::ALL, + FILE_MODE_MAPS_TO_SAME_BRAINORDINATES); + return model; +} + + +/** + * Create a new instance of a Caret Data File Selection Model that + * selects files of the given Data File Type. * - * @param brain - * Brain from which files are obtained. * @param dataFileType * Type of the data file. */ CaretDataFileSelectionModel* -CaretDataFileSelectionModel::newInstanceForCaretDataFileType(Brain* brain, - const DataFileTypeEnum::Enum dataFileType) +CaretDataFileSelectionModel::newInstanceForCaretDataFileType(const DataFileTypeEnum::Enum dataFileType) { - CaretDataFileSelectionModel* model = new CaretDataFileSelectionModel(brain, + CaretDataFileSelectionModel* model = new CaretDataFileSelectionModel(NULL, StructureEnum::ALL, FILE_MODE_DATA_FILE_TYPE_ENUM); model->m_dataFileTypes.push_back(dataFileType); @@ -104,18 +129,15 @@ CaretDataFileSelectionModel::newInstanceForCaretDataFileType(Brain* brain, /** * Create a new instance of a Caret Data File Selection Model that - * selects files of the given Data File Types from the given Brain. + * selects files of the given Data File Types. * - * @param brain - * Brain from which files are obtained. * @param dataFileTypes * Types of the data file. */ CaretDataFileSelectionModel* -CaretDataFileSelectionModel::newInstanceForCaretDataFileTypes(Brain* brain, - const std::vector& dataFileTypes) +CaretDataFileSelectionModel::newInstanceForCaretDataFileTypes(const std::vector& dataFileTypes) { - CaretDataFileSelectionModel* model = new CaretDataFileSelectionModel(brain, + CaretDataFileSelectionModel* model = new CaretDataFileSelectionModel(NULL, StructureEnum::ALL, FILE_MODE_DATA_FILE_TYPE_ENUM); model->m_dataFileTypes.insert(model->m_dataFileTypes.end(), @@ -127,11 +149,8 @@ CaretDataFileSelectionModel::newInstanceForCaretDataFileTypes(Brain* brain, /** * Create a new instance of a Caret Data File Selection Model that - * selects files of the given Data File Types for the given structure - * from the given Brain. + * selects files of the given Data File Types for the given structure. * - * @param brain - * Brain from which files are obtained. * @param structure * Structure for files. Files with the identical structure are used * as well as those files with structure 'ALL'. @@ -139,11 +158,10 @@ CaretDataFileSelectionModel::newInstanceForCaretDataFileTypes(Brain* brain, * Types of the data file. */ CaretDataFileSelectionModel* -CaretDataFileSelectionModel::newInstanceForCaretDataFileTypesInStructure(Brain* brain, - const StructureEnum::Enum structure, - const std::vector& dataFileTypes) +CaretDataFileSelectionModel::newInstanceForCaretDataFileTypesInStructure(const StructureEnum::Enum structure, + const std::vector& dataFileTypes) { - CaretDataFileSelectionModel* model = new CaretDataFileSelectionModel(brain, + CaretDataFileSelectionModel* model = new CaretDataFileSelectionModel(NULL, structure, FILE_MODE_DATA_FILE_TYPE_ENUM); model->m_dataFileTypes.insert(model->m_dataFileTypes.end(), @@ -158,46 +176,25 @@ CaretDataFileSelectionModel::newInstanceForCaretDataFileTypesInStructure(Brain* /** * Create a new instance of a Caret Data File Selection Model that * selects files that implement the chartable matrix parcel interface. - * - * @param brain - * Brain from which files are obtained. */ CaretDataFileSelectionModel* -CaretDataFileSelectionModel::newInstanceForChartableMatrixParcelInterface(Brain* brain) +CaretDataFileSelectionModel::newInstanceForChartableMatrixParcelInterface() { - CaretDataFileSelectionModel* model = new CaretDataFileSelectionModel(brain, + CaretDataFileSelectionModel* model = new CaretDataFileSelectionModel(NULL, StructureEnum::ALL, FILE_MODE_CHARTABLE_MATRIX_PARCEL_INTERFACE); return model; } -/** - * Create a new instance of a Caret Data File Selection Model that - * selects files that implement the chartable matrix interface but - * NOT the chartable matrix parcel interface. - * - * @param brain - * Brain from which files are obtained. - */ -CaretDataFileSelectionModel* -CaretDataFileSelectionModel::newInstanceForChartableMatrixSeriesInterface(Brain* brain) -{ - CaretDataFileSelectionModel* model = new CaretDataFileSelectionModel(brain, - StructureEnum::ALL, - FILE_MODE_CHARTABLE_MATRIX_SERIES_INTERFACE); - - return model; -} - /** * Create a new instance of a Caret Data File Selection Model that * selectes multi-structure border files. */ CaretDataFileSelectionModel* -CaretDataFileSelectionModel::newInstanceForMultiStructureBorderFiles(Brain* brain) +CaretDataFileSelectionModel::newInstanceForMultiStructureBorderFiles() { - CaretDataFileSelectionModel* model = new CaretDataFileSelectionModel(brain, + CaretDataFileSelectionModel* model = new CaretDataFileSelectionModel(NULL, StructureEnum::ALL, FILE_MODE_MULTI_STRUCTURE_BORDER_FILES); @@ -214,8 +211,8 @@ CaretDataFileSelectionModel::newInstanceForMultiStructureBorderFiles(Brain* brai CaretDataFileSelectionModel::CaretDataFileSelectionModel(const CaretDataFileSelectionModel& obj) : CaretObject(obj), SceneableInterface(), -m_fileMode(obj.m_fileMode), -m_brain(obj.m_brain) +m_mappableDataFile(obj.m_mappableDataFile), +m_fileMode(obj.m_fileMode) { this->copyHelperCaretDataFileSelectionModel(obj); } @@ -231,6 +228,7 @@ CaretDataFileSelectionModel& CaretDataFileSelectionModel::operator=(const CaretDataFileSelectionModel& obj) { if (this != &obj) { + CaretAssert(m_fileMode == obj.m_fileMode); CaretObject::operator=(obj); this->copyHelperCaretDataFileSelectionModel(obj); } @@ -245,7 +243,6 @@ CaretDataFileSelectionModel::operator=(const CaretDataFileSelectionModel& obj) void CaretDataFileSelectionModel::copyHelperCaretDataFileSelectionModel(const CaretDataFileSelectionModel& obj) { - m_brain = obj.m_brain; m_structure = obj.m_structure; m_dataFileTypes = obj.m_dataFileTypes; m_selectedFile = obj.m_selectedFile; @@ -300,20 +297,14 @@ CaretDataFileSelectionModel::getAvailableFiles() const switch (m_fileMode) { case FILE_MODE_DATA_FILE_TYPE_ENUM: { - m_brain->getAllDataFilesWithDataFileTypes(m_dataFileTypes, - caretDataFiles); + caretDataFiles = EventCaretDataFilesGet::getCaretDataFilesForTypes(m_dataFileTypes); } break; case FILE_MODE_CHARTABLE_MATRIX_PARCEL_INTERFACE: { - std::vector chartFiles; - m_brain->getAllChartableMatrixDataFiles(chartFiles); - - for (std::vector::iterator iter = chartFiles.begin(); - iter != chartFiles.end(); - iter++) { - ChartableMatrixInterface* chartFile = *iter; - ChartableMatrixParcelInterface* chartParcelFile = dynamic_cast(chartFile); + std::vector allFiles = EventCaretDataFilesGet::getAllCaretDataFiles(); + for (auto dataFile : allFiles) { + ChartableMatrixParcelInterface* chartParcelFile = dynamic_cast(dataFile); /* * Want files that ARE parcel chartable */ @@ -323,33 +314,40 @@ CaretDataFileSelectionModel::getAvailableFiles() const } } } - case FILE_MODE_CHARTABLE_MATRIX_SERIES_INTERFACE: + break; + case FILE_MODE_MAPS_TO_SAME_BRAINORDINATES: { - std::vector chartFiles; - m_brain->getAllChartableMatrixDataFiles(chartFiles); + CaretAssert(m_mappableDataFile); + EventCaretMappableDataFilesGet mapFilesGetEvent; + EventManager::get()->sendEvent(mapFilesGetEvent.getPointer()); + std::vector mapFiles; + mapFilesGetEvent.getAllFiles(mapFiles); - for (std::vector::iterator iter = chartFiles.begin(); - iter != chartFiles.end(); - iter++) { - ChartableMatrixInterface* chartFile = *iter; - ChartableMatrixParcelInterface* chartParcelFile = dynamic_cast(chartFile); - /* - * Want files that are NOT parcel chartable - */ - if (chartParcelFile == NULL) { - CaretMappableDataFile* mapFile = chartFile->getMatrixChartCaretMappableDataFile(); - caretDataFiles.push_back(mapFile); + /* + * Allow the same file to be included since this is used + * by thresholding and may want to threshold with a map + * in the same file. + */ + for (auto mf : mapFiles) { + switch (m_mappableDataFile->getBrainordinateMappingMatch(mf)) { + case CaretMappableDataFile::BrainordinateMappingMatch::EQUAL: + caretDataFiles.push_back(mf); + break; + case CaretMappableDataFile::BrainordinateMappingMatch::NO: + break; + case CaretMappableDataFile::BrainordinateMappingMatch::SUBSET: + caretDataFiles.push_back(mf); + break; } } } break; case FILE_MODE_MULTI_STRUCTURE_BORDER_FILES: { - const int numBorderFiles = m_brain->getNumberOfBorderFiles(); - for (int32_t i = 0; i < numBorderFiles; i++) { - BorderFile* borderFile = m_brain->getBorderFile(i); - if ( ! borderFile->isSingleStructure()) { - caretDataFiles.push_back(borderFile); + std::vector borderFiles = EventCaretDataFilesGet::getCaretDataFilesForType(DataFileTypeEnum::BORDER); + for (auto file : borderFiles) { + if ( ! file->isSingleStructure()) { + caretDataFiles.push_back(file); } } } @@ -534,17 +532,17 @@ CaretDataFileSelectionModel::restoreFromScene(const SceneAttributes* sceneAttrib } } + const AString selectedFileNameNoPath = sceneClass->getStringValue("selectedFileNameNoPath", + ""); if ( ! foundFileFlag) { - const AString selectedFileName = sceneClass->getStringValue("selectedFileNameNoPath", - ""); - if ( ! selectedFileName.isEmpty()) { + if ( ! selectedFileNameNoPath.isEmpty()) { std::vector caretDataFiles = getAvailableFiles(); for (std::vector::iterator iter = caretDataFiles.begin(); iter != caretDataFiles.end(); iter++) { CaretDataFile* cdf = *iter; - if (cdf->getFileNameNoPath() == selectedFileName) { + if (cdf->getFileNameNoPath() == selectedFileNameNoPath) { setSelectedFile(cdf); break; } diff --git a/src/Brain/CaretDataFileSelectionModel.h b/src/Files/CaretDataFileSelectionModel.h similarity index 88% rename from src/Brain/CaretDataFileSelectionModel.h rename to src/Files/CaretDataFileSelectionModel.h index 67edde979fa1ca505aa021a49941f1657fa45a67..023806a5123550ddb56a9f33e2366a480ddf27e0 100644 --- a/src/Brain/CaretDataFileSelectionModel.h +++ b/src/Files/CaretDataFileSelectionModel.h @@ -28,28 +28,26 @@ #include "StructureEnum.h" namespace caret { - class Brain; class CaretDataFile; + class CaretMappableDataFile; class SceneClassAssistant; class CaretDataFileSelectionModel : public CaretObject, public SceneableInterface { public: - static CaretDataFileSelectionModel* newInstanceForCaretDataFileType(Brain* brain, - const DataFileTypeEnum::Enum dataFileType); + static CaretDataFileSelectionModel* newInstanceMapsToSameBrainordinates(const CaretMappableDataFile* mappableDataFile); - static CaretDataFileSelectionModel* newInstanceForCaretDataFileTypes(Brain* brain, - const std::vector& dataFileTypes); + static CaretDataFileSelectionModel* newInstanceForCaretDataFileType(const DataFileTypeEnum::Enum dataFileType); - static CaretDataFileSelectionModel* newInstanceForCaretDataFileTypesInStructure(Brain* brain, + static CaretDataFileSelectionModel* newInstanceForCaretDataFileTypes(const std::vector& dataFileTypes); + + static CaretDataFileSelectionModel* newInstanceForCaretDataFileTypesInStructure( const StructureEnum::Enum structure, const std::vector& dataFileTypes); - static CaretDataFileSelectionModel* newInstanceForChartableMatrixParcelInterface(Brain* brain); - - static CaretDataFileSelectionModel* newInstanceForChartableMatrixSeriesInterface(Brain* brain); + static CaretDataFileSelectionModel* newInstanceForChartableMatrixParcelInterface(); - static CaretDataFileSelectionModel* newInstanceForMultiStructureBorderFiles(Brain* brain); + static CaretDataFileSelectionModel* newInstanceForMultiStructureBorderFiles(); virtual ~CaretDataFileSelectionModel(); @@ -114,12 +112,12 @@ namespace caret { private: enum FileMode { FILE_MODE_CHARTABLE_MATRIX_PARCEL_INTERFACE, - FILE_MODE_CHARTABLE_MATRIX_SERIES_INTERFACE, FILE_MODE_DATA_FILE_TYPE_ENUM, + FILE_MODE_MAPS_TO_SAME_BRAINORDINATES, FILE_MODE_MULTI_STRUCTURE_BORDER_FILES }; - CaretDataFileSelectionModel(Brain* brain, + CaretDataFileSelectionModel(const CaretMappableDataFile* mappableDataFile, const StructureEnum::Enum structure, const FileMode fileMode); @@ -127,14 +125,14 @@ namespace caret { void updateSelection() const; + const CaretMappableDataFile* m_mappableDataFile; + const FileMode m_fileMode; std::vector m_overrideOfAvailableFiles; bool m_overrideOfAvailableFilesValid; - Brain* m_brain; - StructureEnum::Enum m_structure; std::vector m_dataFileTypes; diff --git a/src/Files/CaretMappableDataFile.cxx b/src/Files/CaretMappableDataFile.cxx index 869f8128bbe1392eabebe28fc6fa1da974a8e5fb..35171523b87ea5edde95f23d3bc890217ac58c0f 100644 --- a/src/Files/CaretMappableDataFile.cxx +++ b/src/Files/CaretMappableDataFile.cxx @@ -27,17 +27,24 @@ #include "CaretLogger.h" #include "ChartDataCartesian.h" +#include "ChartableTwoFileDelegate.h" +#include "ChartableTwoFileHistogramChart.h" #include "CiftiMappableConnectivityMatrixDataFile.h" +#include "CiftiXML.h" #include "DataFileContentInformation.h" +#include "EventManager.h" #include "FastStatistics.h" #include "FileInformation.h" #include "GiftiLabelTable.h" +#include "GiftiMetaDataXmlElements.h" #include "Histogram.h" #include "LabelDrawingProperties.h" +#include "NodeAndVoxelColoring.h" #include "PaletteColorMapping.h" #include "SceneClass.h" #include "SceneClassArray.h" #include "SceneAttributes.h" +#include "ScenePrimitive.h" #include "StringTableModel.h" #include "VolumeFile.h" @@ -51,9 +58,7 @@ using namespace caret; CaretMappableDataFile::CaretMappableDataFile(const DataFileTypeEnum::Enum dataFileType) : CaretDataFile(dataFileType) { - m_labelDrawingProperties.grabNew(new LabelDrawingProperties()); - - m_paletteNormalizationMode = PaletteNormalizationModeEnum::NORMALIZATION_SELECTED_MAP_DATA; + initializeCaretMappableDataFileInstance(dataFileType); } /** @@ -72,11 +77,13 @@ CaretMappableDataFile::~CaretMappableDataFile() CaretMappableDataFile::CaretMappableDataFile(const CaretMappableDataFile& cmdf) : CaretDataFile(cmdf) { + initializeCaretMappableDataFileInstance(cmdf.getDataFileType()); + this->copyCaretMappableDataFile(cmdf); } /** - * Constructor. + * Assignment operator. * @param cmdf * Instance that is assigned to this. * @return @@ -92,13 +99,28 @@ CaretMappableDataFile::operator=(const CaretMappableDataFile& cmdf) return *this; } +/** + * Initialize a new instance. + * + * @param dataFileType + * Type of data file. + */ +void +CaretMappableDataFile::initializeCaretMappableDataFileInstance(const DataFileTypeEnum::Enum /*dataFileType*/) +{ + m_labelDrawingProperties = std::unique_ptr(new LabelDrawingProperties()); + m_applyToAllMapsSelected = false; +} + + /** * Assists with copying instances of this class. */ void CaretMappableDataFile::copyCaretMappableDataFile(const CaretMappableDataFile& cmdf) { - m_paletteNormalizationMode = cmdf.m_paletteNormalizationMode; + *m_labelDrawingProperties = *cmdf.m_labelDrawingProperties; + m_mapThresholdFileSelectionModels.clear(); } // note: method is documented in header file @@ -183,13 +205,14 @@ CaretMappableDataFile::getMapIndexFromUniqueID(const AString& uniqueID) const // note: method is documented in header file void -CaretMappableDataFile::updateScalarColoringForAllMaps(const PaletteFile* paletteFile) +CaretMappableDataFile::updateScalarColoringForAllMaps() { const int32_t numMaps = getNumberOfMaps(); for (int32_t iMap = 0; iMap < numMaps; iMap++) { - updateScalarColoringForMap(iMap, - paletteFile); + updateScalarColoringForMap(iMap); } + + invalidateHistogramChartColoring(); } /** @@ -237,6 +260,43 @@ CaretMappableDataFile::isPaletteColorMappingEqualForAllMaps() const return true; } +/** + * Apply palette coloring from the given map to all other maps in the file. + * + * @param mapIndex + * Index of the map. + */ +void +CaretMappableDataFile::applyPaletteColorMappingToAllMaps(const int32_t mapIndex) +{ + if ( ! isMappedWithPalette()) { + return; + } + + const int32_t numMaps = getNumberOfMaps(); + if (numMaps <= 1) { + return; + } + + const PaletteColorMapping* mapColoring = getMapPaletteColorMapping(mapIndex); + for (int32_t i = 0; i < numMaps; i++) { + if (i != mapIndex) { + PaletteColorMapping* pcm = getMapPaletteColorMapping(i); + pcm->copy(*mapColoring, + false); + } + } +} + +/** + * Invalidate all histogram coloring for this file. + */ +void +CaretMappableDataFile::invalidateHistogramChartColoring() +{ + getChartingDelegate()->getHistogramCharting()->invalidateAllColoring(); +} + // note: method is documented in header file NiftiTimeUnitsEnum::Enum CaretMappableDataFile::getMapIntervalUnits() const @@ -301,9 +361,32 @@ CaretMappableDataFile::saveFileDataToScene(const SceneAttributes* sceneAttribute sceneClass->addClass(m_labelDrawingProperties->saveToScene(sceneAttributes, "m_labelDrawingProperties")); + + if (m_chartingDelegate != NULL) { + SceneClass* chartDelegateScene = m_chartingDelegate->saveToScene(sceneAttributes, + "m_chartingDelegate"); + if (chartDelegateScene != NULL) { + sceneClass->addClass(chartDelegateScene); + } + } + if (isMappedWithPalette()) { - sceneClass->addEnumeratedType("m_paletteNormalizationMode", - m_paletteNormalizationMode); + /* + * 03 March 2017 + * Note: Palette (m_paletteNormalizationMode) normalization is no + * longer added to scenes since the palette normalization is a file + * property stored in the file's metadata. + */ + /* + * WB-690 request normalization mode be saved to scenes + * so adding it back in. + */ + sceneClass->addEnumeratedType("m_paletteNormalizationMode", + getPaletteNormalizationMode()); + + sceneClass->addBoolean("m_applyToAllMapsSelected", + m_applyToAllMapsSelected); if (sceneAttributes->isModifiedPaletteSettingsSavedToScene()) { std::vector pcmClassVector; @@ -311,7 +394,18 @@ CaretMappableDataFile::saveFileDataToScene(const SceneAttributes* sceneAttribute const int32_t numMaps = getNumberOfMaps(); for (int32_t i = 0; i < numMaps; i++) { const PaletteColorMapping* pcmConst = getMapPaletteColorMapping(i); - if (pcmConst->isModified()) { + bool savePaletteFlag = false; + switch (pcmConst->getModifiedStatus()) { + case PaletteModifiedStatusEnum::MODIFIED: + savePaletteFlag = true; + break; + case PaletteModifiedStatusEnum::MODIFIED_BY_SHOW_SCENE: + savePaletteFlag = true; + break; + case PaletteModifiedStatusEnum::UNMODIFIED: + break; + } + if (savePaletteFlag) { PaletteColorMapping* pcm = const_cast(pcmConst); try { @@ -348,6 +442,29 @@ CaretMappableDataFile::saveFileDataToScene(const SceneAttributes* sceneAttribute sceneClass->addChild(pcmArray); } } + + { + /* + * Save thresholds for each map + */ + SceneObjectMapIntegerKey* sceneThreshMap = new SceneObjectMapIntegerKey("m_mapThresholdFileSelectionModels", + SceneObjectDataTypeEnum::SCENE_CLASS); + const int32_t numMaps = getNumberOfMaps(); + for (int32_t iMap = 0; iMap < numMaps; iMap++) { + CaretMappableDataFileAndMapSelectionModel* threshSel = getMapThresholdFileSelectionModel(iMap); + if ((threshSel->getSelectedFile() != this) + || (threshSel->getSelectedMapIndex() != iMap)) { + sceneThreshMap->addClass(iMap, threshSel->saveToScene(sceneAttributes, "threshSelElement")); + } + } + + if (sceneThreshMap->isEmpty()) { + delete sceneThreshMap; + } + else { + sceneClass->addChild(sceneThreshMap); + } + } } } @@ -374,14 +491,45 @@ CaretMappableDataFile::restoreFileDataFromScene(const SceneAttributes* sceneAttr m_labelDrawingProperties->restoreFromScene(sceneAttributes, sceneClass->getClass("m_labelDrawingProperties")); + const SceneClass* chartingDelegateClass = sceneClass->getClass("m_chartingDelegate"); + ChartableTwoFileDelegate* chartDelegate = getChartingDelegate(); + chartDelegate->updateAfterFileChanged(); + if (chartingDelegateClass != NULL) { + CaretAssert(chartDelegate); + chartDelegate->restoreFromScene(sceneAttributes, + chartingDelegateClass); + } if (isMappedWithPalette()) { + /* + * Palette normalization was no longer saved to scenes after + * palette normalization was saved in mappable files's metadata. + */ std::vector paletteNormalizationModes; getPaletteNormalizationModesSupported(paletteNormalizationModes); if ( ! paletteNormalizationModes.empty()) { - const PaletteNormalizationModeEnum::Enum defValue = paletteNormalizationModes[0]; - m_paletteNormalizationMode = sceneClass->getEnumeratedTypeValue("m_paletteNormalizationMode", - defValue); + const AString paletteNormStringValue = sceneClass->getEnumeratedTypeValueAsString("m_paletteNormalizationMode"); + if ( ! paletteNormStringValue.isEmpty()) { + bool validFlag = false; + const PaletteNormalizationModeEnum::Enum palNormValue = PaletteNormalizationModeEnum::fromName(paletteNormStringValue, + &validFlag); + if (validFlag) { + if (std::find(paletteNormalizationModes.begin(), + paletteNormalizationModes.end(), + palNormValue) != paletteNormalizationModes.end()) { + /* + * Do not allow the metadata's modification status + * to change or else many files will have a modified + * status after restoration of the scene. + */ + const bool metadataModFlag = getFileMetaData()->isModified(); + setPaletteNormalizationMode(palNormValue); + if ( ! metadataModFlag) { + getFileMetaData()->clearModified(); + } + } + } + } } const int32_t numMaps = getNumberOfMaps(); @@ -463,14 +611,15 @@ CaretMappableDataFile::restoreFileDataFromScene(const SceneAttributes* sceneAttr pcm.decodeFromStringXML(pcmString); PaletteColorMapping* pcmMap = getMapPaletteColorMapping(restoreMapIndex); - pcmMap->copy(pcm); + pcmMap->copy(pcm, + true); pcmMap->clearModified(); /* * WB-522 When palette loaded from scene, * mark it as modified. */ - pcmMap->setModified(); + pcmMap->setSceneModified(); /* * Volume file needs it's map coloring updated since @@ -478,8 +627,7 @@ CaretMappableDataFile::restoreFileDataFromScene(const SceneAttributes* sceneAttr */ VolumeFile* volumeFile = dynamic_cast(this); if (volumeFile != NULL) { - volumeFile->updateScalarColoringForMap(restoreMapIndex, - NULL); + volumeFile->updateScalarColoringForMap(restoreMapIndex); } } catch (const XmlException& e) { @@ -504,6 +652,54 @@ CaretMappableDataFile::restoreFileDataFromScene(const SceneAttributes* sceneAttr } } } + + { + m_mapThresholdFileSelectionModels.clear(); + + const SceneObjectMapIntegerKey* sceneThreshMap = sceneClass->getMapIntegerKey("m_mapThresholdFileSelectionModels"); + if (sceneThreshMap != NULL) { + const std::vector keys = sceneThreshMap->getKeys(); + for (auto mapIndex : keys) { + CaretAssert(mapIndex < getNumberOfMaps()); + const SceneClass* threshSel = dynamic_cast(sceneThreshMap->getObject(mapIndex)); + CaretAssert(threshSel); + getMapThresholdFileSelectionModel(mapIndex)->restoreFromScene(sceneAttributes, + threshSel); + } + } + } + + updateAfterFileDataChanges(); + + /* + * Must restore after call to updateAfterFileDataChanges() to since + * that method initializes 'm_applyToAllMapsSelected'. + * + * This was added by WB-781 Apply to All Maps for ColorBar so that + * the 'apply to all maps' status is saved to and restored from scenes. + */ + const ScenePrimitive* applyToAllMapsPrimitive = sceneClass->getPrimitive("m_applyToAllMapsSelected"); + if (applyToAllMapsPrimitive != NULL) { + m_applyToAllMapsSelected = applyToAllMapsPrimitive->booleanValue(); + } + + /* + * README ABOUT IMPORTANCE OF MODIFIED COLOR PALLETTE MAPPING STATUS MUST REMAIN ON + * + * (1) The user may modify the palette color mapping for a map/file. This modified palette + * color mapping is saved to the scene so that the user does not need to save the actual + * data file. When the scene is restored, the palette color mapping is restored from the + * scene and applied to the file. As a result, the file will contain a 'modified palette + * color mapping status'. + * + * (2) This 'modified palette color mapping status' must remain ON so that if the user + * saves a scene, the modified status is added to the scene and will be restored from + * the scene at a later time. As a result, the scene will display correctly. + * + * (3) If the 'modified palette color mapping status' was NOT left on and the user + * saved a scene, the scene would not display correctly due to the palette color mapping + * no longer being added to the scene. + */ } } @@ -747,6 +943,40 @@ CaretMappableDataFile::addToDataFileContentInformation(DataFileContentInformatio } } +/** + * @return File histogram number of buckets. + */ +int32_t +CaretMappableDataFile::getFileHistogramNumberOfBuckets() const +{ + /* + * Metadata returns zero if integer value not found + */ + const GiftiMetaData* metadata = getFileMetaData(); + CaretAssert(metadata); + int32_t numBuckets = metadata->getInt(GiftiMetaDataXmlElements::HISTOGRAM_NUMBER_OF_BUCKETS); + if (numBuckets <= 0) { + numBuckets = 100; + } + return numBuckets; +} + +/** + * Set the file histogram number of buckets. + * + * @param numberOfBuckets + * Number of buckets. + */ +void +CaretMappableDataFile::setFileHistogramNumberOfBuckets(const int32_t numberOfBuckets) +{ + GiftiMetaData* metadata = getFileMetaData(); + CaretAssert(metadata); + metadata->setInt(GiftiMetaDataXmlElements::HISTOGRAM_NUMBER_OF_BUCKETS, + numberOfBuckets); +} + + /** * @return True if any of the maps in this file contain a * color mapping that possesses a modified status. @@ -767,6 +997,42 @@ CaretMappableDataFile::isModifiedPaletteColorMapping() const return false; } +/** + * @return The modified status for aall palettes in this file. + * Note that 'modified' overrides any 'modified by show scene'. + */ +PaletteModifiedStatusEnum::Enum +CaretMappableDataFile::getPaletteColorMappingModifiedStatus() const +{ + PaletteModifiedStatusEnum::Enum modStatus = PaletteModifiedStatusEnum::UNMODIFIED; + + if (isMappedWithPalette()) { + const int32_t numMaps = getNumberOfMaps(); + for (int32_t i = 0; i < numMaps; i++) { + switch (getMapPaletteColorMapping(i)->getModifiedStatus()) { + case PaletteModifiedStatusEnum::MODIFIED: + modStatus = PaletteModifiedStatusEnum::MODIFIED; + break; + case PaletteModifiedStatusEnum::MODIFIED_BY_SHOW_SCENE: + modStatus = PaletteModifiedStatusEnum::MODIFIED_BY_SHOW_SCENE; + break; + case PaletteModifiedStatusEnum::UNMODIFIED: + break; + } + + if (modStatus == PaletteModifiedStatusEnum::MODIFIED) { + /* + * 'MODIFIED' overrides 'MODIFIED_BY_SHOW_SCENE' + * so no need to continue loop + */ + break; + } + } + } + + return modStatus; +} + /** * @return True if the file is modified in any way EXCEPT for * the palette color mapping. Also see isModified(). @@ -805,6 +1071,32 @@ CaretMappableDataFile::isModified() const return false; } +/** + * Clear data in this file. + */ +void +CaretMappableDataFile::clear() +{ + CaretDataFile::clear(); + + m_chartingDelegate.reset(); + + m_mapThresholdFileSelectionModels.clear(); +} + +/** + * Clear the modified status of this file. + */ +void +CaretMappableDataFile::clearModified() +{ + CaretDataFile::clearModified(); + + if (m_chartingDelegate != NULL) { + m_chartingDelegate->clearModified(); + } +} + /** * Create cartesian chart data from the given data. * @@ -848,12 +1140,12 @@ CaretMappableDataFile::helpCreateCartesianChartData(const std::vector& da ChartDataCartesian* chartData = NULL; if (timeSeriesFlag) { - chartData = new ChartDataCartesian(ChartDataTypeEnum::CHART_DATA_TYPE_LINE_TIME_SERIES, + chartData = new ChartDataCartesian(ChartOneDataTypeEnum::CHART_DATA_TYPE_LINE_TIME_SERIES, ChartAxisUnitsEnum::CHART_AXIS_UNITS_TIME_SECONDS, ChartAxisUnitsEnum::CHART_AXIS_UNITS_NONE); } else { - chartData = new ChartDataCartesian(ChartDataTypeEnum::CHART_DATA_TYPE_LINE_DATA_SERIES, + chartData = new ChartDataCartesian(ChartOneDataTypeEnum::CHART_DATA_TYPE_LINE_DATA_SERIES, ChartAxisUnitsEnum::CHART_AXIS_UNITS_NONE, ChartAxisUnitsEnum::CHART_AXIS_UNITS_NONE); } @@ -903,29 +1195,29 @@ CaretMappableDataFile::helpCreateCartesianChartData(const std::vector& da * Chart types supported by this file. */ void -CaretMappableDataFile::helpGetSupportedLineSeriesChartDataTypes(std::vector& chartDataTypesOut) const +CaretMappableDataFile::helpGetSupportedLineSeriesChartDataTypes(std::vector& chartDataTypesOut) const { chartDataTypesOut.clear(); switch (getMapIntervalUnits()) { case NiftiTimeUnitsEnum::NIFTI_UNITS_HZ: - chartDataTypesOut.push_back(ChartDataTypeEnum::CHART_DATA_TYPE_LINE_FREQUENCY_SERIES); + chartDataTypesOut.push_back(ChartOneDataTypeEnum::CHART_DATA_TYPE_LINE_FREQUENCY_SERIES); break; case NiftiTimeUnitsEnum::NIFTI_UNITS_MSEC: - chartDataTypesOut.push_back(ChartDataTypeEnum::CHART_DATA_TYPE_LINE_TIME_SERIES); + chartDataTypesOut.push_back(ChartOneDataTypeEnum::CHART_DATA_TYPE_LINE_TIME_SERIES); break; case NiftiTimeUnitsEnum::NIFTI_UNITS_PPM: CaretLogSevere("Units - PPM not supported"); CaretAssertMessage(0, "Units - PPM not supported"); break; case NiftiTimeUnitsEnum::NIFTI_UNITS_SEC: - chartDataTypesOut.push_back(ChartDataTypeEnum::CHART_DATA_TYPE_LINE_TIME_SERIES); + chartDataTypesOut.push_back(ChartOneDataTypeEnum::CHART_DATA_TYPE_LINE_TIME_SERIES); break; case NiftiTimeUnitsEnum::NIFTI_UNITS_UNKNOWN: - chartDataTypesOut.push_back(ChartDataTypeEnum::CHART_DATA_TYPE_LINE_DATA_SERIES); + chartDataTypesOut.push_back(ChartOneDataTypeEnum::CHART_DATA_TYPE_LINE_DATA_SERIES); break; case NiftiTimeUnitsEnum::NIFTI_UNITS_USEC: - chartDataTypesOut.push_back(ChartDataTypeEnum::CHART_DATA_TYPE_LINE_TIME_SERIES); + chartDataTypesOut.push_back(ChartOneDataTypeEnum::CHART_DATA_TYPE_LINE_TIME_SERIES); break; } } @@ -963,7 +1255,7 @@ CaretMappableDataFile::isMedialWallLabelInMapLabelTable(const int32_t mapIndex) LabelDrawingProperties* CaretMappableDataFile::getLabelDrawingProperties() { - return m_labelDrawingProperties; + return m_labelDrawingProperties.get(); } /** @@ -973,7 +1265,117 @@ CaretMappableDataFile::getLabelDrawingProperties() const LabelDrawingProperties* CaretMappableDataFile::getLabelDrawingProperties() const { - return m_labelDrawingProperties; + return m_labelDrawingProperties.get(); +} + +/** + * @return The charting delegate for this file. Pointer + * will never be NULL, even if file does not support charting. + */ +ChartableTwoFileDelegate* +CaretMappableDataFile::getChartingDelegate() +{ + if (m_chartingDelegate == NULL) { + m_chartingDelegate = std::unique_ptr(new ChartableTwoFileDelegate(this)); + + m_chartingDelegate->updateAfterFileChanged(); + } + return m_chartingDelegate.get(); +} + +/* + * @return The charting delegate for this file. Pointer + * will never be NULL, even if file does not support charting. + */ +const ChartableTwoFileDelegate* +CaretMappableDataFile::getChartingDelegate() const +{ + if (m_chartingDelegate == NULL) { + CaretMappableDataFile* thisFile = const_cast(this); + m_chartingDelegate = std::unique_ptr(new ChartableTwoFileDelegate(thisFile)); + + m_chartingDelegate->updateAfterFileChanged(); + } + return m_chartingDelegate.get(); +} + +/** + * Update the map threshold file selection models. + */ +void +CaretMappableDataFile::updateMapThresholdFileSelectionModels() +{ + const int32_t numMaps = getNumberOfMaps(); + const int32_t numThresh = static_cast(m_mapThresholdFileSelectionModels.size()); + if (numMaps > numThresh) { + for (int32_t i = numThresh; i < numMaps; i++) { + std::unique_ptr threshSel(new CaretMappableDataFileAndMapSelectionModel(this)); + threshSel->setSelectedFile(this); + threshSel->setSelectedMapIndex(i); + m_mapThresholdFileSelectionModels.push_back(std::move(threshSel)); + } + } + else if (numThresh > numMaps) { + m_mapThresholdFileSelectionModels.resize(numMaps); + } +} + +/** + * @return The thresholding file selection model for the given map index. + */ +CaretMappableDataFileAndMapSelectionModel* +CaretMappableDataFile::getMapThresholdFileSelectionModel(const int32_t mapIndex) +{ + updateMapThresholdFileSelectionModels(); + + CaretAssertVectorIndex(m_mapThresholdFileSelectionModels, mapIndex); + return m_mapThresholdFileSelectionModels[mapIndex].get(); +} + +/** + * Update the charting delegate after changes (add a row/column, etc.) + * are made to the data file. + */ +void +CaretMappableDataFile::updateAfterFileDataChanges() +{ + if (m_chartingDelegate) { + m_chartingDelegate->updateAfterFileChanged(); + } + + m_applyToAllMapsSelected = isPaletteColorMappingEqualForAllMaps(); +} + +/** + * @return Is apply palette color mapping to all maps selected. + */ +bool +CaretMappableDataFile::isApplyPaletteColorMappingToAllMaps() const +{ + return m_applyToAllMapsSelected; +} + +/** + * Set apply palette color mapping to all maps. Only sets the status, + * it does not change any palette color mapping. + * + * @param selected + * New selected status. + */ +void +CaretMappableDataFile::setApplyPaletteColorMappingToAllMaps(const bool selected) +{ + m_applyToAllMapsSelected = selected; +} + +/** + * @return True if file is mapped with a palette and one + * palette is used for all maps. + */ +bool +CaretMappableDataFile::isOnePaletteUsedForAllMaps() const +{ + return false; } /** @@ -983,7 +1385,26 @@ CaretMappableDataFile::getLabelDrawingProperties() const PaletteNormalizationModeEnum::Enum CaretMappableDataFile::getPaletteNormalizationMode() const { - return m_paletteNormalizationMode; + PaletteNormalizationModeEnum::Enum modeValue = PaletteNormalizationModeEnum::NORMALIZATION_SELECTED_MAP_DATA; + + const AString textValue = getFileMetaData()->get(GiftiMetaDataXmlElements::METADATA_PALETTE_NORMALIZATION_MODE); + bool validFlag = false; + modeValue = PaletteNormalizationModeEnum::fromName(textValue, &validFlag); + + if ( ! validFlag) { + std::vector validModes; + getPaletteNormalizationModesSupported(validModes); + + if ( ! validModes.empty()) { + CaretAssertVectorIndex(validModes, 0); + modeValue = validModes[0]; + } + else { + modeValue = PaletteNormalizationModeEnum::NORMALIZATION_SELECTED_MAP_DATA; + } + } + + return modeValue; } /** @@ -995,7 +1416,8 @@ CaretMappableDataFile::getPaletteNormalizationMode() const void CaretMappableDataFile::setPaletteNormalizationMode(const PaletteNormalizationModeEnum::Enum mode) { - m_paletteNormalizationMode = mode; + getFileMetaData()->set(GiftiMetaDataXmlElements::METADATA_PALETTE_NORMALIZATION_MODE, + PaletteNormalizationModeEnum::toName(mode)); } /** @@ -1008,5 +1430,13 @@ CaretMappableDataFile::isMappedWithRGBA() const return false; } +bool CaretMappableDataFile::hasCiftiXML() const +{ + return false; +} +const CiftiXML CaretMappableDataFile::getCiftiXML() const +{ + return CiftiXML(); +} diff --git a/src/Files/CaretMappableDataFile.h b/src/Files/CaretMappableDataFile.h index 039e3d8abe03c18b8303a276e22ce59cdd7b4d6d..cdd6966e059754a2cda94c1ba73e2a2a1f3faf1b 100644 --- a/src/Files/CaretMappableDataFile.h +++ b/src/Files/CaretMappableDataFile.h @@ -21,23 +21,28 @@ */ /*LICENSE_END*/ +#include #include "CaretDataFile.h" -#include "ChartDataTypeEnum.h" +#include "CaretMappableDataFileAndMapSelectionModel.h" +#include "ChartOneDataTypeEnum.h" #include "CaretPointer.h" +#include "CiftiXML.h" #include "NiftiEnums.h" +#include "PaletteModifiedStatusEnum.h" #include "PaletteNormalizationModeEnum.h" namespace caret { class ChartDataCartesian; + class ChartableTwoFileDelegate; class FastStatistics; class GiftiMetaData; class GiftiLabelTable; class Histogram; class LabelDrawingProperties; + class MapFileDataSelector; class PaletteColorMapping; - class PaletteFile; /** * \class caret::CaretMappableDataFile @@ -57,6 +62,18 @@ namespace caret { class CaretMappableDataFile : public CaretDataFile { public: + /** + * Result for brainordinate mapping match + */ + enum class BrainordinateMappingMatch { + /** Both files map to exact same brainordinates */ + EQUAL, + /** File do not match to same brainordinates */ + NO, + /** File matches to a subset of the other file's brainordinates **/ + SUBSET + }; + CaretMappableDataFile(const DataFileTypeEnum::Enum dataFileType); virtual ~CaretMappableDataFile(); @@ -201,6 +218,8 @@ namespace caret { */ virtual bool isMappedWithPalette() const = 0; + virtual bool isOnePaletteUsedForAllMaps() const; + /** * @return The estimated size of data after it is uncompressed * and loaded into RAM. A negative value indicates that the @@ -268,6 +287,7 @@ namespace caret { * not mapped using a palette). */ virtual const FastStatistics* getFileFastStatistics() = 0; + /** * Get histogram describing the distribution of data * mapped with a color palette for all data within @@ -304,6 +324,10 @@ namespace caret { const float mostNegativeValueInclusive, const bool includeZeroValues) = 0; + int32_t getFileHistogramNumberOfBuckets() const; + + void setFileHistogramNumberOfBuckets(const int32_t numberOfBuckets); + /** * Get the palette color mapping for the map at the given index. * @@ -371,7 +395,7 @@ namespace caret { * is more than one suppported mode, the first mode in the * vector is assumed to be the default mode. */ - virtual void getPaletteNormalizationModesSupported(std::vector& modesSupportedOut) = 0; + virtual void getPaletteNormalizationModesSupported(std::vector& modesSupportedOut) const = 0; /** * @return The palette normalization mode for the file. @@ -392,7 +416,7 @@ namespace caret { * @param paletteFile * Palette file containing palettes. */ - virtual void updateScalarColoringForAllMaps(const PaletteFile* paletteFile); + virtual void updateScalarColoringForAllMaps(); /** * Update coloring for a map. @@ -402,10 +426,15 @@ namespace caret { * @param paletteFile * Palette file containing palettes. */ - virtual void updateScalarColoringForMap(const int32_t mapIndex, - const PaletteFile* paletteFile) = 0; + virtual void updateScalarColoringForMap(const int32_t mapIndex) = 0; + + void invalidateHistogramChartColoring(); + + void applyPaletteColorMappingToAllMaps(const int32_t mapIndex); - virtual bool isPaletteColorMappingEqualForAllMaps() const; + bool isApplyPaletteColorMappingToAllMaps() const; + + void setApplyPaletteColorMappingToAllMaps(const bool selected); /** * @return The units for the 'interval' between two consecutive maps. @@ -437,12 +466,53 @@ namespace caret { virtual bool isModifiedPaletteColorMapping() const; /* documented in cxx file. */ - bool isModified() const; + virtual PaletteModifiedStatusEnum::Enum getPaletteColorMappingModifiedStatus() const; + + /** + * Check whether the file contains Cifti XML (all cifti types and also wbsparse have it) + */ + virtual bool hasCiftiXML() const; + + /** + * Get the Cifti XML, if the file has it + * This could be faster if it returned a reference, but then it would have to throw when there is no xml object + */ + virtual const CiftiXML getCiftiXML() const; + + /* documented in cxx file. */ + virtual bool isModified() const override final; + + virtual void clearModified() override; + + virtual void clear(); + + void initializeCaretMappableDataFileInstance(const DataFileTypeEnum::Enum dataFileType); LabelDrawingProperties* getLabelDrawingProperties(); const LabelDrawingProperties* getLabelDrawingProperties() const; + ChartableTwoFileDelegate* getChartingDelegate(); + + const ChartableTwoFileDelegate* getChartingDelegate() const; + + virtual void getDataForSelector(const MapFileDataSelector& mapFileDataSelector, + std::vector& dataOut) const = 0; + + CaretMappableDataFileAndMapSelectionModel* getMapThresholdFileSelectionModel(const int32_t mapIndex); + + /** + * Are all brainordinates in this file also in the given file? + * That is, the brainordinates are equal to or a subset of the brainordinates + * in the given file. + * + * @param mapFile + * The given map file. + * @return + * Match status. + */ + virtual BrainordinateMappingMatch getBrainordinateMappingMatch(const CaretMappableDataFile* mapFile) const = 0; + protected: CaretMappableDataFile(const CaretMappableDataFile&); @@ -450,7 +520,9 @@ namespace caret { ChartDataCartesian* helpCreateCartesianChartData(const std::vector& data); - void helpGetSupportedLineSeriesChartDataTypes(std::vector& chartDataTypesOut) const; + void helpGetSupportedLineSeriesChartDataTypes(std::vector& chartDataTypesOut) const; + + void updateAfterFileDataChanges(); virtual void saveFileDataToScene(const SceneAttributes* sceneAttributes, SceneClass* sceneClass); @@ -462,9 +534,21 @@ namespace caret { void copyCaretMappableDataFile(const CaretMappableDataFile&); - CaretPointer m_labelDrawingProperties; + bool isPaletteColorMappingEqualForAllMaps() const; + + void updateMapThresholdFileSelectionModels(); + + std::unique_ptr m_labelDrawingProperties; - PaletteNormalizationModeEnum::Enum m_paletteNormalizationMode; + mutable std::unique_ptr m_chartingDelegate; + + std::vector> m_mapThresholdFileSelectionModels; + + /** + * Added by WB-781 Apply to All Maps for ColorBar. + * This value is saved to scenes but NOT to the data file. + */ + bool m_applyToAllMapsSelected = false; }; #ifdef __CARET_MAPPABLE_DATA_FILE_DECLARE__ diff --git a/src/Brain/CaretMappableDataFileAndMapSelectionModel.cxx b/src/Files/CaretMappableDataFileAndMapSelectionModel.cxx similarity index 75% rename from src/Brain/CaretMappableDataFileAndMapSelectionModel.cxx rename to src/Files/CaretMappableDataFileAndMapSelectionModel.cxx index 49aabc5703b4682954bfc1e68068f290665e216e..e2c833b314240f8edbc78443911ead476f78b21d 100644 --- a/src/Brain/CaretMappableDataFileAndMapSelectionModel.cxx +++ b/src/Files/CaretMappableDataFileAndMapSelectionModel.cxx @@ -23,7 +23,6 @@ #include "CaretMappableDataFileAndMapSelectionModel.h" #undef __CARET_MAPPABLE_DATA_FILE_AND_MAP_SELECTION_MODEL_DECLARE__ -#include "Brain.h" #include "CaretAssert.h" #include "CaretDataFileSelectionModel.h" #include "CaretLogger.h" @@ -38,41 +37,53 @@ using namespace caret; /** * \class caret::CaretMappableDataFileAndMapSelectionModel * \brief Model for selection of a CaretMappableDataFile and map index. - * \ingroup Brain + * \ingroup Files */ /** - * Constructor. + * Constructor for files that map to the same brainordinates as the given file. + * + * @param caretMappableDataFile + * Mappable data file. + */ +CaretMappableDataFileAndMapSelectionModel::CaretMappableDataFileAndMapSelectionModel(const CaretMappableDataFile* caretMappableDataFile) +: CaretObject(), +m_mode(Mode::MAP_TO_SAME_BRAINORDINATES), +m_mappableDataFile(caretMappableDataFile) +{ + std::vector dataFileTypesVector; + performConstruction(dataFileTypesVector); +} + + +/** + * Constructor for instance that will contain files of the given data type. * - * @param brain - * Brain from which files are obtained. * @param dataFileType * Type of data files available for selection. */ -CaretMappableDataFileAndMapSelectionModel::CaretMappableDataFileAndMapSelectionModel(Brain* brain, - const DataFileTypeEnum::Enum dataFileType) -: CaretObject() +CaretMappableDataFileAndMapSelectionModel::CaretMappableDataFileAndMapSelectionModel(const DataFileTypeEnum::Enum dataFileType) +: CaretObject(), +m_mode(Mode::MATCH_DATA_FILE_TYPES), +m_mappableDataFile(NULL) { std::vector dataFileTypesVector; dataFileTypesVector.push_back(dataFileType); - performConstruction(brain, - dataFileTypesVector); + performConstruction(dataFileTypesVector); } /** - * Constructor for multiple types of files. + * Constructor for instance that will contain files of the given data types. * - * @param brain - * Brain from which files are obtained. * @param dataFileTypes * Types of data files available for selection. */ -CaretMappableDataFileAndMapSelectionModel::CaretMappableDataFileAndMapSelectionModel(Brain* brain, - const std::vector& dataFileTypes) -: CaretObject() +CaretMappableDataFileAndMapSelectionModel::CaretMappableDataFileAndMapSelectionModel(const std::vector& dataFileTypes) +: CaretObject(), +m_mode(Mode::MATCH_DATA_FILE_TYPES), +m_mappableDataFile(NULL) { - performConstruction(brain, - dataFileTypes); + performConstruction(dataFileTypes); } /** @@ -85,25 +96,15 @@ CaretMappableDataFileAndMapSelectionModel::~CaretMappableDataFileAndMapSelection } /** - * Finish construction for an instance of this class. - * - * @param brain - * Brain from which files are obtained. - * @param dataFileTypes - * Types of data files available for selection. + * Validate that any data file types are mappable data files + * and remove any non-mappable data file types. */ void -CaretMappableDataFileAndMapSelectionModel::performConstruction(Brain* brain, - const std::vector& dataFileTypes) +CaretMappableDataFileAndMapSelectionModel::validateDataFileTypes() { - m_brain = brain; - m_dataFileTypes = dataFileTypes; + std::vector mappableDataFileTypes; - for (std::vector::const_iterator typeIter = dataFileTypes.begin(); - typeIter != dataFileTypes.end(); - typeIter++) { - const DataFileTypeEnum::Enum dataFileType = *typeIter; - + for (const auto dataFileType : m_dataFileTypes) { bool isMappableFile = false; switch (dataFileType) { case DataFileTypeEnum::ANNOTATION: @@ -176,17 +177,50 @@ CaretMappableDataFileAndMapSelectionModel::performConstruction(Brain* brain, case DataFileTypeEnum::UNKNOWN: break; case DataFileTypeEnum::VOLUME: + isMappableFile = true; break; } + CaretAssert(isMappableFile); - if ( ! isMappableFile) { + if (isMappableFile) { + mappableDataFileTypes.push_back(dataFileType); + } + else { CaretLogSevere(DataFileTypeEnum::toGuiName(dataFileType) + " is not a valid mappable data file."); } } - m_caretDataFileSelectionModel = CaretDataFileSelectionModel::newInstanceForCaretDataFileTypes(brain, - dataFileTypes); + if (m_dataFileTypes.size() != mappableDataFileTypes.size()) { + /* + * Update without non-mappable data file types. + */ + m_dataFileTypes = mappableDataFileTypes; + } +} + +/** + * Finish construction for an instance of this class. + * + * @param dataFileTypes + * Types of data files available for selection. + */ +void +CaretMappableDataFileAndMapSelectionModel::performConstruction(const std::vector& dataFileTypes) +{ + m_dataFileTypes = dataFileTypes; + + switch (m_mode) { + case Mode::MAP_TO_SAME_BRAINORDINATES: + CaretAssert(m_mappableDataFile); + m_caretDataFileSelectionModel = CaretDataFileSelectionModel::newInstanceMapsToSameBrainordinates(m_mappableDataFile); + break; + case Mode::MATCH_DATA_FILE_TYPES: + validateDataFileTypes(); + m_caretDataFileSelectionModel = CaretDataFileSelectionModel::newInstanceForCaretDataFileTypes(dataFileTypes); + break; + } + m_selectedMapIndex = -1; m_sceneAssistant = new SceneClassAssistant(); @@ -205,7 +239,9 @@ CaretMappableDataFileAndMapSelectionModel::performConstruction(Brain* brain, */ CaretMappableDataFileAndMapSelectionModel::CaretMappableDataFileAndMapSelectionModel(const CaretMappableDataFileAndMapSelectionModel& obj) : CaretObject(obj), -SceneableInterface(obj) +SceneableInterface(obj), +m_mode(obj.m_mode), +m_mappableDataFile(obj.m_mappableDataFile) { this->copyHelperCaretMappableDataFileAndMapSelectionModel(obj); } @@ -301,6 +337,13 @@ CaretMappableDataFileAndMapSelectionModel::getSelectedMapIndex() const std::vector CaretMappableDataFileAndMapSelectionModel::getAvailableFiles() const { + switch (m_mode) { + case Mode::MAP_TO_SAME_BRAINORDINATES: + break; + case Mode::MATCH_DATA_FILE_TYPES: + break; + } + std::vector caretDataFiles = m_caretDataFileSelectionModel->getAvailableFiles(); std::vector mappableFiles; for (std::vector::iterator iter = caretDataFiles.begin(); @@ -342,26 +385,34 @@ void CaretMappableDataFileAndMapSelectionModel::setSelectedFile(CaretMappableDataFile* selectedFile) { if (selectedFile != NULL) { - const DataFileTypeEnum::Enum fileType = selectedFile->getDataFileType(); - if (std::find(m_dataFileTypes.begin(), - m_dataFileTypes.end(), - fileType) == m_dataFileTypes.end()) { - AString validFileTypeNames; - for (std::vector::const_iterator typeIter = m_dataFileTypes.begin(); - typeIter != m_dataFileTypes.end(); - typeIter++) { - const DataFileTypeEnum::Enum dataFileType = *typeIter; - validFileTypeNames.append(DataFileTypeEnum::toName(dataFileType) + " "); + switch (m_mode) { + case Mode::MAP_TO_SAME_BRAINORDINATES: + break; + case Mode::MATCH_DATA_FILE_TYPES: + { + const DataFileTypeEnum::Enum fileType = selectedFile->getDataFileType(); + if (std::find(m_dataFileTypes.begin(), + m_dataFileTypes.end(), + fileType) == m_dataFileTypes.end()) { + AString validFileTypeNames; + for (std::vector::const_iterator typeIter = m_dataFileTypes.begin(); + typeIter != m_dataFileTypes.end(); + typeIter++) { + const DataFileTypeEnum::Enum dataFileType = *typeIter; + validFileTypeNames.append(DataFileTypeEnum::toName(dataFileType) + " "); + } + + + const AString msg("Attempting to set file that is of type " + + DataFileTypeEnum::toGuiName(fileType) + + " but model is for type(s) " + + validFileTypeNames); + CaretAssertMessage(0, msg); + CaretLogSevere(msg); + return; + } } - - - const AString msg("Attempting to set file that is of type " - + DataFileTypeEnum::toGuiName(fileType) - + " but model is for type(s) " - + validFileTypeNames); - CaretAssertMessage(0, msg); - CaretLogSevere(msg); - return; + break; } } diff --git a/src/Brain/CaretMappableDataFileAndMapSelectionModel.h b/src/Files/CaretMappableDataFileAndMapSelectionModel.h similarity index 88% rename from src/Brain/CaretMappableDataFileAndMapSelectionModel.h rename to src/Files/CaretMappableDataFileAndMapSelectionModel.h index df3fe1b4b16daa8f8b5e0e46183938279797ebad..c233cd24490744f91c7cb21259b68e811d7ea790 100644 --- a/src/Brain/CaretMappableDataFileAndMapSelectionModel.h +++ b/src/Files/CaretMappableDataFileAndMapSelectionModel.h @@ -28,7 +28,6 @@ namespace caret { - class Brain; class CaretDataFileSelectionModel; class CaretMappableDataFile; class SceneClassAssistant; @@ -36,11 +35,11 @@ namespace caret { class CaretMappableDataFileAndMapSelectionModel : public CaretObject, public SceneableInterface { public: - CaretMappableDataFileAndMapSelectionModel(Brain* brain, - const DataFileTypeEnum::Enum dataFileType); + CaretMappableDataFileAndMapSelectionModel(const CaretMappableDataFile* caretMappableDataFile); - CaretMappableDataFileAndMapSelectionModel(Brain* brain, - const std::vector& dataFileTypes); + CaretMappableDataFileAndMapSelectionModel(const DataFileTypeEnum::Enum dataFileType); + + CaretMappableDataFileAndMapSelectionModel(const std::vector& dataFileTypes); virtual ~CaretMappableDataFileAndMapSelectionModel(); @@ -102,15 +101,23 @@ namespace caret { // const SceneClass* sceneClass) = 0; private: + enum class Mode { + MAP_TO_SAME_BRAINORDINATES, + MATCH_DATA_FILE_TYPES + }; + void copyHelperCaretMappableDataFileAndMapSelectionModel(const CaretMappableDataFileAndMapSelectionModel& obj); - void performConstruction(Brain* brain, - const std::vector& dataFileTypes); + void performConstruction(const std::vector& dataFileTypes); + + void validateDataFileTypes(); + + const Mode m_mode; + + const CaretMappableDataFile* m_mappableDataFile; SceneClassAssistant* m_sceneAssistant; - Brain* m_brain; - std::vector m_dataFileTypes; CaretDataFileSelectionModel* m_caretDataFileSelectionModel; diff --git a/src/Files/CaretVolumeExtension.cxx b/src/Files/CaretVolumeExtension.cxx index 07394d7c378242964617284eed54750863e5db40..d068447d3bbb28645215767fafd2bebc5b002d5a 100644 --- a/src/Files/CaretVolumeExtension.cxx +++ b/src/Files/CaretVolumeExtension.cxx @@ -20,11 +20,16 @@ #include "CaretVolumeExtension.h" #include "XmlSaxParser.h" +#include "GiftiMetaData.h" +#include "GiftiMetaDataSaxReader.h" +#include "GiftiMetaDataXmlElements.h" #include "GiftiXmlElements.h" #include "PaletteColorMapping.h" #include "PaletteColorMappingSaxReader.h" #include "PaletteColorMappingXmlElements.h" +#include "PaletteNormalizationModeEnum.h" #include "CaretLogger.h" +#include "XmlUnexpectedElementSaxParser.h" #include using namespace caret; @@ -46,6 +51,7 @@ CaretVolumeExtension::clear() { m_comment.clear(); m_date.clear(); m_attributes.clear(); + m_metadata.clear(); } void CaretVolumeExtension::readFromXmlString(const AString& s) @@ -75,6 +81,29 @@ void CaretVolumeExtension::writeAsXML(XmlWriter& xmlWriter) { m_attributes[i]->writeAsXML(xmlWriter, i); } + if ( ! m_metadata.isEmpty()) { + /* + * Prior to "WB-664 Data normalization should be saved in file", VolumeFile did not contain + * file metadata. If one attempts to read a volume file containing metadata with older + * versions of wb_view/wb_command or Caret5, XML parsing of the CaretVolumeExtension will + * immediately cease if an unexpected element is encountered. So, to minimize isses with + * older versions of software, DO NOT write file metadata if it contains only one + * element that is the default value (selected map) of palette normalization. + */ + bool writeFileMetaDataFlag = true; + if (m_metadata.getNumberOfMetaData() == 1) { + const AString normalizationValueString = m_metadata.get(GiftiMetaDataXmlElements::METADATA_PALETTE_NORMALIZATION_MODE); + if ( ! normalizationValueString.isEmpty()) { + if (normalizationValueString == + PaletteNormalizationModeEnum::toName(PaletteNormalizationModeEnum::NORMALIZATION_SELECTED_MAP_DATA)) { + writeFileMetaDataFlag = false; + } + } + } + if (writeFileMetaDataFlag) { + m_metadata.writeAsXML(xmlWriter); + } + } xmlWriter.writeEndElement();//just to make it clean xmlWriter.writeEndDocument();//so, this just flushes } @@ -114,6 +143,9 @@ void SubvolumeAttributes::writeAsXML(XmlWriter& xmlWriter, int index) typeString = "Unknown"; } xmlWriter.writeElementCData(CARET_VOL_EXT_VI_TYPE, typeString); + if ( ! m_metadata.isEmpty()) { + m_metadata.writeAsXML(xmlWriter); + } xmlWriter.writeEndElement(); } @@ -128,6 +160,7 @@ CaretVolumeExtensionXMLReader::CaretVolumeExtensionXMLReader(CaretVolumeExtensio CaretAssert(toFill != NULL); m_toFill = toFill; m_viIndex = -1; + m_unexpectedXmlElementSaxParser.grabNew(NULL); } void CaretVolumeExtensionXMLReader::characters(const char* ch) @@ -144,6 +177,15 @@ void CaretVolumeExtensionXMLReader::characters(const char* ch) CaretAssert(m_paletteReader != NULL); m_paletteReader->characters(ch); break; + case ROOT_META_DATA: + case VI_META_DATA: + CaretAssert(m_metadataReader); + m_metadataReader->characters(ch); + break; + case UNEXPECTED_XML: + CaretAssert(m_unexpectedXmlElementSaxParser); + m_unexpectedXmlElementSaxParser->characters(ch); + break; default: m_charDataStack.back() += ch; } @@ -217,6 +259,18 @@ void CaretVolumeExtensionXMLReader::endElement(const AString& namespaceURI, cons popState = false; } break; + case ROOT_META_DATA: + case VI_META_DATA: + CaretAssert(m_metadataReader); + m_metadataReader->endElement(namespaceURI, localName, qualifiedName); + if (qualifiedName == GiftiXmlElements::TAG_METADATA) { + m_metadataReader->endDocument(); + m_metadataReader.grabNew(NULL); + } + else { + popState = false; + } + break; case VOLUME_TYPE: CaretAssertVectorIndex(m_toFill->m_attributes, m_viIndex); if (elemCharData == "Anatomy") @@ -236,6 +290,23 @@ void CaretVolumeExtensionXMLReader::endElement(const AString& namespaceURI, cons m_toFill->m_attributes[m_viIndex]->m_type = SubvolumeAttributes::UNKNOWN; } break; + case UNEXPECTED_XML: + CaretAssert(m_unexpectedXmlElementSaxParser); + switch (m_unexpectedXmlElementSaxParser->endElement(namespaceURI, localName, qualifiedName)) { + case XmlUnexpectedElementSaxParser::ReturnCodeEnum::DONE: + warning(XmlSaxParserException("Unexpected XML ignored:\n" + + m_unexpectedXmlElementSaxParser->getUnexpectedContentXML())); + m_unexpectedXmlElementSaxParser.grabNew(NULL); + break; + case XmlUnexpectedElementSaxParser::ReturnCodeEnum::ERROR: + throw XmlSaxParserException("Processing of unexpected elements failed: " + + m_unexpectedXmlElementSaxParser->getUnexpectedContentXML()); + break; + case XmlUnexpectedElementSaxParser::ReturnCodeEnum::NOT_DONE: + popState = false; + break; + } + break; } if (popState) { @@ -300,6 +371,11 @@ void CaretVolumeExtensionXMLReader::startElement(const AString& uri, const AStri m_toFill->m_attributes.resize(m_viIndex + 1);//don't worry, CaretPointer copy is relatively cheap } m_toFill->m_attributes[m_viIndex].grabNew(new SubvolumeAttributes()); + } else if (qName == GiftiXmlElements::TAG_METADATA) { + nextState = ROOT_META_DATA; + m_metadataReader.grabNew(new GiftiMetaDataSaxReader(&m_toFill->m_metadata)); + m_metadataReader->startDocument(); + m_metadataReader->startElement(uri, localName, qName, atts); }//anything else gets caught in INVALID below break; case VOLUME_INFORMATION: @@ -324,6 +400,12 @@ void CaretVolumeExtensionXMLReader::startElement(const AString& uri, const AStri m_paletteReader.grabNew(new PaletteColorMappingSaxReader(m_toFill->m_attributes[m_viIndex]->m_palette)); m_paletteReader->startDocument(); m_paletteReader->startElement(uri, localName, qName, atts); + } else if (qName == GiftiXmlElements::TAG_METADATA) { + nextState = VI_META_DATA; + CaretAssertVectorIndex(m_toFill->m_attributes, m_viIndex); + m_metadataReader.grabNew(new GiftiMetaDataSaxReader(&m_toFill->m_attributes[m_viIndex]->m_metadata)); + m_metadataReader->startDocument(); + m_metadataReader->startElement(uri, localName, qName, atts); } else if (qName == CARET_VOL_EXT_VI_TYPE) { nextState = VOLUME_TYPE; } @@ -341,13 +423,32 @@ void CaretVolumeExtensionXMLReader::startElement(const AString& uri, const AStri CaretAssert(m_paletteReader != NULL); m_paletteReader->startElement(uri, localName, qName, atts); break; + case ROOT_META_DATA: + case VI_META_DATA: + addState = false; + CaretAssert(m_metadataReader); + m_metadataReader->startElement(uri, localName, qName, atts); + break; + case UNEXPECTED_XML: + addState = false; + CaretAssert(m_unexpectedXmlElementSaxParser); + m_unexpectedXmlElementSaxParser->startElement(uri, localName, qName, atts); + break; } } if (addState) { if (nextState == INVALID) { - throw XmlSaxParserException(AString("CaretVolumeExtension encountered an unexpected element: ") + invalidInfo); + /* + * If the invalid element name is not empty, + * then we are processing child elements of the + * invalid element. + */ + CaretAssert(m_unexpectedXmlElementSaxParser == NULL); + m_unexpectedXmlElementSaxParser.grabNew(new XmlUnexpectedElementSaxParser()); + m_unexpectedXmlElementSaxParser->startElement(uri, localName, qName, atts); + nextState = UNEXPECTED_XML; } m_stateStack.push_back(nextState); m_charDataStack.push_back(AString()); diff --git a/src/Files/CaretVolumeExtension.h b/src/Files/CaretVolumeExtension.h index 3ac8c33d63bbef4d65e72f93c2d8a25e40138765..f866f970906e5a3bd3845bb00920cc21dbad8d5d 100644 --- a/src/Files/CaretVolumeExtension.h +++ b/src/Files/CaretVolumeExtension.h @@ -29,9 +29,13 @@ #include "XmlSaxParserHandlerInterface.h" #include "PaletteColorMappingSaxReader.h" #include "GiftiLabelTableSaxReader.h" +#include "GiftiMetaData.h" +#include "GiftiMetaDataSaxReader.h" + namespace caret { + class XmlUnexpectedElementSaxParser; struct StudyMetadataLinkSet { @@ -53,6 +57,7 @@ namespace caret AString m_comment; AString m_guiLabel; CaretPointer m_labelTable; + GiftiMetaData m_metadata; StudyMetadataLinkSet m_studyMetadata; CaretPointer m_palette; VolumeType m_type; @@ -62,6 +67,7 @@ namespace caret struct CaretVolumeExtension { + GiftiMetaData m_metadata; AString m_comment; AString m_date;//TODO: make a class to handle ISO-8601 dates std::vector > m_attributes; @@ -84,7 +90,10 @@ namespace caret LABEL_TABLE, STUDY_META_DATA_LINK_SET, PALETTE_COLOR_MAPPING, - VOLUME_TYPE + VOLUME_TYPE, + ROOT_META_DATA, + VI_META_DATA, + UNEXPECTED_XML }; std::vector m_stateStack; CaretVolumeExtension* m_toFill; @@ -92,6 +101,8 @@ namespace caret int m_viIndex; CaretPointer m_paletteReader; CaretPointer m_labelReader; + CaretPointer m_metadataReader; + CaretPointer m_unexpectedXmlElementSaxParser; CaretVolumeExtensionXMLReader();//disallow default construction CaretVolumeExtensionXMLReader(const CaretVolumeExtensionXMLReader&);//disallow copy CaretVolumeExtensionXMLReader& operator=(const CaretVolumeExtensionXMLReader&);//disallow assignment diff --git a/src/Files/ChartableLineSeriesInterface.cxx b/src/Files/ChartableLineSeriesInterface.cxx index 39633077fe5d0886e8207b145a02edc42f14ce24..efe73d88ae90a3bac2b3abf5bc8c56c57ec192ff 100644 --- a/src/Files/ChartableLineSeriesInterface.cxx +++ b/src/Files/ChartableLineSeriesInterface.cxx @@ -42,9 +42,9 @@ using namespace caret; * True if chart data type is supported by the file, else false. */ bool -ChartableLineSeriesInterface::isLineSeriesChartDataTypeSupported(const ChartDataTypeEnum::Enum chartDataType) const +ChartableLineSeriesInterface::isLineSeriesChartDataTypeSupported(const ChartOneDataTypeEnum::Enum chartDataType) const { - std::vector validTypes; + std::vector validTypes; getSupportedLineSeriesChartDataTypes(validTypes); if (std::find(validTypes.begin(), diff --git a/src/Files/ChartableLineSeriesInterface.h b/src/Files/ChartableLineSeriesInterface.h index b4328a165cf6710f8eaa935d606f798604915fd7..f31e91c2cf46f51dce7535ce6edace2d6ea1dbf9 100644 --- a/src/Files/ChartableLineSeriesInterface.h +++ b/src/Files/ChartableLineSeriesInterface.h @@ -21,7 +21,7 @@ */ /*LICENSE_END*/ -#include "ChartDataTypeEnum.h" +#include "ChartOneDataTypeEnum.h" #include "StructureEnum.h" namespace caret { @@ -76,9 +76,9 @@ namespace caret { * @param chartDataTypesOut * Chart types supported by this file. */ - virtual void getSupportedLineSeriesChartDataTypes(std::vector& chartDataTypesOut) const = 0; + virtual void getSupportedLineSeriesChartDataTypes(std::vector& chartDataTypesOut) const = 0; - bool isLineSeriesChartDataTypeSupported(const ChartDataTypeEnum::Enum chartDataType) const; + bool isLineSeriesChartDataTypeSupported(const ChartOneDataTypeEnum::Enum chartDataType) const; private: // ChartableLineSeriesInterface(const ChartableLineSeriesInterface&); diff --git a/src/Files/ChartableMatrixInterface.cxx b/src/Files/ChartableMatrixInterface.cxx index 9505d93ac3e03b245335237881296d4f33684443..5d2d3cc7cbec3bad4bd96fa17bceea8f45626c34 100644 --- a/src/Files/ChartableMatrixInterface.cxx +++ b/src/Files/ChartableMatrixInterface.cxx @@ -43,9 +43,9 @@ using namespace caret; * True if chart data type is supported by the file, else false. */ bool -ChartableMatrixInterface::isMatrixChartDataTypeSupported(const ChartDataTypeEnum::Enum chartDataType) const +ChartableMatrixInterface::isMatrixChartDataTypeSupported(const ChartOneDataTypeEnum::Enum chartDataType) const { - std::vector validTypes; + std::vector validTypes; getSupportedMatrixChartDataTypes(validTypes); if (std::find(validTypes.begin(), diff --git a/src/Files/ChartableMatrixInterface.h b/src/Files/ChartableMatrixInterface.h index 91572ec5810dde15f491ca0f358dbaf4bcfc131f..fe6f458418bb9667eb684b25c98402ad3fdb6128 100644 --- a/src/Files/ChartableMatrixInterface.h +++ b/src/Files/ChartableMatrixInterface.h @@ -22,7 +22,7 @@ /*LICENSE_END*/ #include "CaretColorEnum.h" -#include "ChartDataTypeEnum.h" +#include "ChartOneDataTypeEnum.h" #include "ChartMatrixLoadingDimensionEnum.h" #include "CiftiParcelColoringModeEnum.h" #include "YokingGroupEnum.h" @@ -155,9 +155,9 @@ namespace caret { * @param chartDataTypesOut * Chart types supported by this file. */ - virtual void getSupportedMatrixChartDataTypes(std::vector& chartDataTypesOut) const = 0; + virtual void getSupportedMatrixChartDataTypes(std::vector& chartDataTypesOut) const = 0; - bool isMatrixChartDataTypeSupported(const ChartDataTypeEnum::Enum chartDataType) const; + bool isMatrixChartDataTypeSupported(const ChartOneDataTypeEnum::Enum chartDataType) const; // ADD_NEW_METHODS_HERE diff --git a/src/Files/ChartableMatrixSeriesInterface.h b/src/Files/ChartableMatrixSeriesInterface.h index 1a8fe334c0bfdc941569a5573850711c1cc35542..7e11a55961aa9da9dabc296faf74f2194f26b2c5 100644 --- a/src/Files/ChartableMatrixSeriesInterface.h +++ b/src/Files/ChartableMatrixSeriesInterface.h @@ -40,7 +40,7 @@ namespace caret { virtual void setMatrixRowColumnMapYokingGroup(const int32_t tabIndex, const MapYokingGroupEnum::Enum yokingType) = 0; - virtual int32_t getSelectedMapIndex(const int32_t tabIndex) = 0; + virtual int32_t getSelectedMapIndex(const int32_t tabIndex) const = 0; virtual void setSelectedMapIndex(const int32_t tabIndex, const int32_t mapIndex) = 0; diff --git a/src/Files/ChartableTwoFileBaseChart.cxx b/src/Files/ChartableTwoFileBaseChart.cxx new file mode 100644 index 0000000000000000000000000000000000000000..c4363a5742dee1dc66c64a39724ed462dd67da09 --- /dev/null +++ b/src/Files/ChartableTwoFileBaseChart.cxx @@ -0,0 +1,390 @@ + +/*LICENSE_START*/ +/* + * Copyright (C) 2017 Washington University School of Medicine + * + * 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 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ +/*LICENSE_END*/ + +#define __CHARTABLE_TWO_FILE_BASE_CHART_DECLARE__ +#include "ChartableTwoFileBaseChart.h" +#undef __CHARTABLE_TWO_FILE_BASE_CHART_DECLARE__ + +#include "AnnotationPercentSizeText.h" +#include "CaretAssert.h" +#include "CaretMappableDataFile.h" +#include "CiftiMappableDataFile.h" +#include "EventManager.h" +#include "SceneClass.h" +#include "SceneClassAssistant.h" + +using namespace caret; + + + +/** + * \class caret::ChartableTwoFileBaseChart + * \brief Base class for File Charting Delegates. + * \ingroup Files + * + * The first version of charting used interfaces for each + * charting type. This required some files to subclass + * several interfaces and significant modification to any + * class that supported charting. + * + * The reasons for using this 'delegate' are to avoid + * extensive modifications to many file classes and to + * avoid name clashes with the version one of charting. + * Some of the original charting needs to be preserved + * to support loading of older scenes. + */ + + +/** + * Constructor. + * + * @param chartType + * Type of chart provided by this delegate. + * @param parentCaretMappableDataFile + * Parent caret mappable data file that this delegate supports. + */ +ChartableTwoFileBaseChart::ChartableTwoFileBaseChart(const ChartTwoDataTypeEnum::Enum chartType, + CaretMappableDataFile* parentCaretMappableDataFile) +: CaretObjectTracksModification(), +m_chartType(chartType), +m_parentCaretMappableDataFile(parentCaretMappableDataFile) +{ + CaretAssert(m_chartType != ChartTwoDataTypeEnum::CHART_DATA_TYPE_INVALID); + CaretAssert(m_parentCaretMappableDataFile); + + /* will be NULL if file is not a cifti mappable file */ + m_parentCiftiMappableDataFile = dynamic_cast(m_parentCaretMappableDataFile); + + setDefaultAxisTitles(); + + m_sceneAssistant = new SceneClassAssistant(); + m_sceneAssistant->add("m_bottomAxisTitleText", + &m_bottomAxisTitleText); + m_sceneAssistant->add("m_leftRightAxisTitleText", + &m_leftRightAxisTitleText); + + EventManager::get()->addEventListener(this, EventTypeEnum::EVENT_ANNOTATION_CHART_LABEL_GET); +} + +/** + * Destructor. + */ +ChartableTwoFileBaseChart::~ChartableTwoFileBaseChart() +{ + EventManager::get()->removeAllEventsFromListener(this); + delete m_sceneAssistant; +} + +/** + * Set the default titles for the axes. + */ +void +ChartableTwoFileBaseChart::setDefaultAxisTitles() +{ + m_bottomAxisTitleText = setDefaultAxisTitle(ChartAxisLocationEnum::CHART_AXIS_LOCATION_BOTTOM); + m_leftRightAxisTitleText = setDefaultAxisTitle(ChartAxisLocationEnum::CHART_AXIS_LOCATION_LEFT); +} + +/** + * Set the default axis title. + * + * @param axisLocation + * Location of the axis. + */ +AString +ChartableTwoFileBaseChart::setDefaultAxisTitle(const ChartAxisLocationEnum::Enum axisLocation) +{ + AString title; + + switch (axisLocation) { + case ChartAxisLocationEnum::CHART_AXIS_LOCATION_BOTTOM: + case ChartAxisLocationEnum::CHART_AXIS_LOCATION_TOP: + switch (m_chartType) { + case ChartTwoDataTypeEnum::CHART_DATA_TYPE_INVALID: + break; + case ChartTwoDataTypeEnum::CHART_DATA_TYPE_HISTOGRAM: + title = "Data"; + break; + case ChartTwoDataTypeEnum::CHART_DATA_TYPE_LINE_SERIES: + title = CaretUnitsTypeEnum::toGuiName(m_compoundChartDataType.getLineChartUnitsAxisX()); + switch (m_compoundChartDataType.getLineChartUnitsAxisX()) { + case CaretUnitsTypeEnum::NONE: + title = "Data"; + break; + case CaretUnitsTypeEnum::HERTZ: + break; + case CaretUnitsTypeEnum::METERS: + break; + case CaretUnitsTypeEnum::PARTS_PER_MILLION: + break; + case CaretUnitsTypeEnum::RADIANS: + break; + case CaretUnitsTypeEnum::SECONDS: + break; + } + break; + case ChartTwoDataTypeEnum::CHART_DATA_TYPE_MATRIX: + break; + } + break; + case ChartAxisLocationEnum::CHART_AXIS_LOCATION_LEFT: + case ChartAxisLocationEnum::CHART_AXIS_LOCATION_RIGHT: + { + switch (m_chartType) { + case ChartTwoDataTypeEnum::CHART_DATA_TYPE_INVALID: + break; + case ChartTwoDataTypeEnum::CHART_DATA_TYPE_HISTOGRAM: + title = "Counts"; + break; + case ChartTwoDataTypeEnum::CHART_DATA_TYPE_LINE_SERIES: + title = "Value"; + break; + case ChartTwoDataTypeEnum::CHART_DATA_TYPE_MATRIX: + break; + } + } + break; + } + + return title; +} + + +/** + * Get a description of this object's content. + * @return String describing this object's content. + */ +AString +ChartableTwoFileBaseChart::toString() const +{ + return "ChartableTwoFileBaseChart"; +} + +/** + * Receive an event. + * + * @param event + * An event for which this instance is listening. + */ +void +ChartableTwoFileBaseChart::receiveEvent(Event* /*event*/) +{ +} + +/** + * @return The CaretMappableDataFile that provided this delegate. + */ +CaretMappableDataFile* +ChartableTwoFileBaseChart::getCaretMappableDataFile() +{ + return m_parentCaretMappableDataFile; +} + +/** + * @return The CaretMappableDataFile that provided this delegate. + */ +const CaretMappableDataFile* +ChartableTwoFileBaseChart::getCaretMappableDataFile() const +{ + return m_parentCaretMappableDataFile; +} + +/** + * @return The CiftiMappableDataFile that provided this delegate. + * NULL is returned if not a CiftiMappableDataFile. + */ +CiftiMappableDataFile* +ChartableTwoFileBaseChart::getCiftiMappableDataFile() +{ + return m_parentCiftiMappableDataFile; +} + +/** + * @return The CiftiMappableDataFile that provided this delegate. + * NULL is returned if not a CiftiMappableDataFile. + */ +const CiftiMappableDataFile* +ChartableTwoFileBaseChart::getCiftiMappableDataFile() const +{ + return m_parentCiftiMappableDataFile; +} + +/** + * @return Chart version two data type + */ +ChartTwoDataTypeEnum::Enum +ChartableTwoFileBaseChart::getChartTwoDataType() const +{ + return m_chartType; +} + +/** + * @return Chart compound data type supported by subclass. + */ +ChartTwoCompoundDataType +ChartableTwoFileBaseChart::getChartTwoCompoundDataType() const +{ + CaretAssertMessage((m_compoundChartDataType.getChartTwoDataType() != ChartTwoDataTypeEnum::CHART_DATA_TYPE_INVALID), + "Data type is invalid, was updateChartCompoundDataTypeAfterFileChanges() called by " + "implementing class."); + return m_compoundChartDataType; +} + +/** + * Set the chart compound data type. Subclasses of this base class MUST + * call this method whenever the content of the delegatee file changes. + * + * @param compoundChartDataType + * Updated chart compound data type. Its basic data type MUST + * match the data type passed to the constructor. + */ +void +ChartableTwoFileBaseChart::updateChartTwoCompoundDataTypeAfterFileChanges(const ChartTwoCompoundDataType compoundChartDataType) +{ + CaretAssert(m_chartType == compoundChartDataType.getChartTwoDataType()); + + m_compoundChartDataType = compoundChartDataType; + + setDefaultAxisTitles(); +} + +/** + * @return Annotation for the bottom axis title (const method) + */ +AString +ChartableTwoFileBaseChart::getBottomAxisTitle() const +{ + return m_bottomAxisTitleText; +} + +/** + * Set the bottom axis title. + * + * @param title + * New bottom axis title. + */ +void +ChartableTwoFileBaseChart::setBottomAxisTitle(const AString& title) +{ + if (title != m_bottomAxisTitleText) { + m_bottomAxisTitleText = title; + setModified(); + } +} + +/** + * @return Annotation for the bottom axis title (const method) + */ +AString +ChartableTwoFileBaseChart::getLeftRightAxisTitle() const +{ + return m_leftRightAxisTitleText; +} + +/** + * Set the left/right axis title. + * + * @param title + * New left/right axis title. + */ +void +ChartableTwoFileBaseChart::setLeftRightAxisTitle(const AString& title) +{ + if (title != m_leftRightAxisTitleText) { + m_leftRightAxisTitleText = title; + setModified(); + } +} + + +/** + * Save information specific to this type of model to the scene. + * + * @param sceneAttributes + * Attributes for the scene. Scenes may be of different types + * (full, generic, etc) and the attributes should be checked when + * saving the scene. + * + * @param instanceName + * Name of instance in the scene. + */ +SceneClass* +ChartableTwoFileBaseChart::saveToScene(const SceneAttributes* sceneAttributes, + const AString& instanceName) +{ + SceneClass* sceneClass = new SceneClass(instanceName, + "ChartableTwoFileBaseChart", + 3); + m_sceneAssistant->saveMembers(sceneAttributes, + sceneClass); + + saveSubClassDataToScene(sceneAttributes, + sceneClass); + + return sceneClass; +} + +/** + * Restore information specific to the type of model from the scene. + * + * @param sceneAttributes + * Attributes for the scene. Scenes may be of different types + * (full, generic, etc) and the attributes should be checked when + * restoring the scene. + * + * @param sceneClass + * sceneClass from which model specific information is obtained. + */ +void +ChartableTwoFileBaseChart::restoreFromScene(const SceneAttributes* sceneAttributes, + const SceneClass* sceneClass) +{ + if (sceneClass == NULL) { + return; + } + + setDefaultAxisTitles(); + + m_sceneAssistant->restoreMembers(sceneAttributes, + sceneClass); + + restoreSubClassDataFromScene(sceneAttributes, + sceneClass); + + if (sceneClass->getVersionNumber() <= 2) { + AnnotationPercentSizeText bottomTitle(AnnotationAttributesDefaultTypeEnum::NORMAL, + AnnotationTextFontSizeTypeEnum::PERCENTAGE_OF_VIEWPORT_HEIGHT); + bottomTitle.restoreFromScene(sceneAttributes, + sceneClass->getClass("m_bottomAxisTitle")); + if ( ! bottomTitle.getText().isEmpty()) { + m_bottomAxisTitleText = bottomTitle.getText(); + } + + AnnotationPercentSizeText leftRightTitle(AnnotationAttributesDefaultTypeEnum::NORMAL, + AnnotationTextFontSizeTypeEnum::PERCENTAGE_OF_VIEWPORT_HEIGHT); + leftRightTitle.restoreFromScene(sceneAttributes, + sceneClass->getClass("m_leftRightAxisTitle")); + if ( ! leftRightTitle.getText().isEmpty()) { + m_leftRightAxisTitleText = leftRightTitle.getText(); + } + } +} + diff --git a/src/Files/ChartableTwoFileBaseChart.h b/src/Files/ChartableTwoFileBaseChart.h new file mode 100644 index 0000000000000000000000000000000000000000..010fc8bd8635ef9009f9ed17756b21fc6b2028f2 --- /dev/null +++ b/src/Files/ChartableTwoFileBaseChart.h @@ -0,0 +1,139 @@ +#ifndef __CHARTABLE_TWO_FILE_BASE_CHART_H__ +#define __CHARTABLE_TWO_FILE_BASE_CHART_H__ + +/*LICENSE_START*/ +/* + * Copyright (C) 2017 Washington University School of Medicine + * + * 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 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ +/*LICENSE_END*/ + +#include + +#include "ChartAxisLocationEnum.h" +#include "CaretObjectTracksModification.h" +#include "ChartTwoCompoundDataType.h" +#include "EventListenerInterface.h" +#include "SceneableInterface.h" + + +namespace caret { + class CaretMappableDataFile; + class CiftiMappableDataFile; + class SceneClassAssistant; + + class ChartableTwoFileBaseChart : public CaretObjectTracksModification, public EventListenerInterface, public SceneableInterface { + + protected: + ChartableTwoFileBaseChart(const ChartTwoDataTypeEnum::Enum chartType, + CaretMappableDataFile* parentCaretMappableDataFile); + + public: + virtual ~ChartableTwoFileBaseChart(); + + /** + * @return Is this charting valid ? + */ + virtual bool isValid() const = 0; + + /** + * @return Is this charting empty (no data at this time) + */ + virtual bool isEmpty() const = 0; + + CaretMappableDataFile* getCaretMappableDataFile(); + + const CaretMappableDataFile* getCaretMappableDataFile() const; + + CiftiMappableDataFile* getCiftiMappableDataFile(); + + const CiftiMappableDataFile* getCiftiMappableDataFile() const; + + ChartTwoDataTypeEnum::Enum getChartTwoDataType() const; + + ChartTwoCompoundDataType getChartTwoCompoundDataType() const; + + AString getBottomAxisTitle() const; + + void setBottomAxisTitle(const AString& title); + + AString getLeftRightAxisTitle() const; + + void setLeftRightAxisTitle(const AString& title); + + // ADD_NEW_METHODS_HERE + + virtual AString toString() const override; + + virtual void receiveEvent(Event* event) override; + + virtual SceneClass* saveToScene(const SceneAttributes* sceneAttributes, + const AString& instanceName) override; + + virtual void restoreFromScene(const SceneAttributes* sceneAttributes, + const SceneClass* sceneClass) override; + + + + + + + protected: + void updateChartTwoCompoundDataTypeAfterFileChanges(const ChartTwoCompoundDataType compoundChartDataType); + + virtual void saveSubClassDataToScene(const SceneAttributes* sceneAttributes, + SceneClass* sceneClass) = 0; + + virtual void restoreSubClassDataFromScene(const SceneAttributes* sceneAttributes, + const SceneClass* sceneClass) = 0; + + private: + ChartableTwoFileBaseChart() = delete; + + ChartableTwoFileBaseChart(const ChartableTwoFileBaseChart&); + + ChartableTwoFileBaseChart& operator=(const ChartableTwoFileBaseChart&); + + void setDefaultAxisTitles(); + + AString setDefaultAxisTitle(const ChartAxisLocationEnum::Enum axisLocation); + + const ChartTwoDataTypeEnum::Enum m_chartType; + + CaretMappableDataFile* m_parentCaretMappableDataFile; + + CiftiMappableDataFile* m_parentCiftiMappableDataFile; + + SceneClassAssistant* m_sceneAssistant; + + ChartTwoCompoundDataType m_compoundChartDataType; + + AString m_bottomAxisTitleText; + + AString m_leftRightAxisTitleText; + + static float constexpr s_defaultFontPercentViewportSize = 5.0; + + // ADD_NEW_MEMBERS_HERE + + }; + +#ifdef __CHARTABLE_TWO_FILE_BASE_CHART_DECLARE__ + // +#endif // __CHARTABLE_TWO_FILE_BASE_CHART_DECLARE__ + +} // namespace +#endif //__CHARTABLE_TWO_FILE_BASE_CHART_H__ diff --git a/src/Files/ChartableTwoFileDelegate.cxx b/src/Files/ChartableTwoFileDelegate.cxx new file mode 100644 index 0000000000000000000000000000000000000000..e8f3384d46fe483bf1a63b2835f26da1d7beaa2f --- /dev/null +++ b/src/Files/ChartableTwoFileDelegate.cxx @@ -0,0 +1,570 @@ + +/*LICENSE_START*/ +/* + * Copyright (C) 2016 Washington University School of Medicine + * + * 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 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ +/*LICENSE_END*/ + +#include + +#define __CHARTABLE_TWO_FILE_DELEGATE_DECLARE__ +#include "ChartableTwoFileDelegate.h" +#undef __CHARTABLE_TWO_INTERFACE_DECLARE__ + +#include "CaretAssert.h" +#include "CaretMappableDataFile.h" +#include "ChartableTwoFileHistogramChart.h" +#include "ChartableTwoFileLineSeriesChart.h" +#include "ChartableTwoFileMatrixChart.h" +#include "CiftiConnectivityMatrixParcelFile.h" +#include "CiftiMappableDataFile.h" +#include "CiftiParcelLabelFile.h" +#include "CiftiParcelReordering.h" +#include "CiftiParcelScalarFile.h" +#include "SceneClassAssistant.h" + +using namespace caret; + + + +/** + * \class caret::ChartableTwoFileDelegate + * \brief Interface for files that are chartable. Contains functionality but no data. + * \ingroup Files + */ + +/** + * Constructor. + * + * @param caretMappableDataFile + * Caret Mappable Data File this delegate charts. + */ +ChartableTwoFileDelegate::ChartableTwoFileDelegate(CaretMappableDataFile* caretMappableDataFile) +: CaretObjectTracksModification(), +SceneableInterface(), +m_caretMappableDataFile(caretMappableDataFile) +{ + CaretAssert(m_caretMappableDataFile); + + m_sceneAssistant = std::unique_ptr(new SceneClassAssistant()); + + clear(); +} + +/** + * Destructor. + */ +ChartableTwoFileDelegate::~ChartableTwoFileDelegate() +{ +} + +/** + * Update after the file has been read or content has changed. + */ +void +ChartableTwoFileDelegate::updateAfterFileChanged() +{ + CaretAssert(m_caretMappableDataFile); + + ChartTwoHistogramContentTypeEnum::Enum histogramType = ChartTwoHistogramContentTypeEnum::HISTOGRAM_CONTENT_TYPE_UNSUPPORTED; + ChartTwoLineSeriesContentTypeEnum::Enum lineSeriesType = ChartTwoLineSeriesContentTypeEnum::LINE_SERIES_CONTENT_UNSUPPORTED; + ChartTwoMatrixContentTypeEnum::Enum matrixType = ChartTwoMatrixContentTypeEnum::MATRIX_CONTENT_UNSUPPORTED; + + std::vector validMatrixRowColumnSelectionDimensions; + + switch (m_caretMappableDataFile->getDataFileType()) { + case DataFileTypeEnum::CONNECTIVITY_DENSE: + histogramType = ChartTwoHistogramContentTypeEnum::HISTOGRAM_CONTENT_TYPE_MAP_DATA; + break; + case DataFileTypeEnum::CONNECTIVITY_DENSE_DYNAMIC: + histogramType = ChartTwoHistogramContentTypeEnum::HISTOGRAM_CONTENT_TYPE_MAP_DATA; + break; + case DataFileTypeEnum::CONNECTIVITY_DENSE_LABEL: + break; + case DataFileTypeEnum::CONNECTIVITY_DENSE_PARCEL: + histogramType = ChartTwoHistogramContentTypeEnum::HISTOGRAM_CONTENT_TYPE_MAP_DATA; + break; + case DataFileTypeEnum::CONNECTIVITY_DENSE_SCALAR: + histogramType = ChartTwoHistogramContentTypeEnum::HISTOGRAM_CONTENT_TYPE_MAP_DATA; + lineSeriesType = ChartTwoLineSeriesContentTypeEnum::LINE_SERIES_CONTENT_BRAINORDINATE_DATA; + break; + case DataFileTypeEnum::CONNECTIVITY_DENSE_TIME_SERIES: + histogramType = ChartTwoHistogramContentTypeEnum::HISTOGRAM_CONTENT_TYPE_MAP_DATA; + lineSeriesType = ChartTwoLineSeriesContentTypeEnum::LINE_SERIES_CONTENT_BRAINORDINATE_DATA; + break; + case DataFileTypeEnum::CONNECTIVITY_PARCEL: + histogramType = ChartTwoHistogramContentTypeEnum::HISTOGRAM_CONTENT_TYPE_MAP_DATA; + matrixType = ChartTwoMatrixContentTypeEnum::MATRIX_CONTENT_BRAINORDINATE_MAPPABLE; + validMatrixRowColumnSelectionDimensions.push_back(ChartTwoMatrixLoadingDimensionEnum::CHART_MATRIX_LOADING_BY_ROW); + validMatrixRowColumnSelectionDimensions.push_back(ChartTwoMatrixLoadingDimensionEnum::CHART_MATRIX_LOADING_BY_COLUMN); + break; + case DataFileTypeEnum::CONNECTIVITY_PARCEL_DENSE: + histogramType = ChartTwoHistogramContentTypeEnum::HISTOGRAM_CONTENT_TYPE_MAP_DATA; + break; + case DataFileTypeEnum::CONNECTIVITY_PARCEL_LABEL: + matrixType = ChartTwoMatrixContentTypeEnum::MATRIX_CONTENT_BRAINORDINATE_MAPPABLE; + validMatrixRowColumnSelectionDimensions.push_back(ChartTwoMatrixLoadingDimensionEnum::CHART_MATRIX_LOADING_BY_COLUMN); + break; + case DataFileTypeEnum::CONNECTIVITY_PARCEL_SCALAR: + histogramType = ChartTwoHistogramContentTypeEnum::HISTOGRAM_CONTENT_TYPE_MAP_DATA; + lineSeriesType = ChartTwoLineSeriesContentTypeEnum::LINE_SERIES_CONTENT_BRAINORDINATE_DATA; + matrixType = ChartTwoMatrixContentTypeEnum::MATRIX_CONTENT_BRAINORDINATE_MAPPABLE; + validMatrixRowColumnSelectionDimensions.push_back(ChartTwoMatrixLoadingDimensionEnum::CHART_MATRIX_LOADING_BY_COLUMN); + break; + case DataFileTypeEnum::CONNECTIVITY_PARCEL_SERIES: + histogramType = ChartTwoHistogramContentTypeEnum::HISTOGRAM_CONTENT_TYPE_MAP_DATA; + lineSeriesType = ChartTwoLineSeriesContentTypeEnum::LINE_SERIES_CONTENT_BRAINORDINATE_DATA; + matrixType = ChartTwoMatrixContentTypeEnum::MATRIX_CONTENT_BRAINORDINATE_MAPPABLE; + validMatrixRowColumnSelectionDimensions.push_back(ChartTwoMatrixLoadingDimensionEnum::CHART_MATRIX_LOADING_BY_COLUMN); + break; + case DataFileTypeEnum::CONNECTIVITY_SCALAR_DATA_SERIES: + lineSeriesType = ChartTwoLineSeriesContentTypeEnum::LINE_SERIES_CONTENT_ROW_SCALAR_DATA; + matrixType = ChartTwoMatrixContentTypeEnum::MATRIX_CONTENT_SCALARS; + validMatrixRowColumnSelectionDimensions.push_back(ChartTwoMatrixLoadingDimensionEnum::CHART_MATRIX_LOADING_BY_ROW); + break; + case DataFileTypeEnum::ANNOTATION: + break; + case DataFileTypeEnum::BORDER: + break; + case DataFileTypeEnum::CONNECTIVITY_FIBER_ORIENTATIONS_TEMPORARY: + break; + case DataFileTypeEnum::CONNECTIVITY_FIBER_TRAJECTORY_TEMPORARY: + break; + case DataFileTypeEnum::FOCI: + break; + case DataFileTypeEnum::IMAGE: + break; + case DataFileTypeEnum::LABEL: + break; + case DataFileTypeEnum::METRIC: + histogramType = ChartTwoHistogramContentTypeEnum::HISTOGRAM_CONTENT_TYPE_MAP_DATA; + lineSeriesType = ChartTwoLineSeriesContentTypeEnum::LINE_SERIES_CONTENT_BRAINORDINATE_DATA; + break; + case DataFileTypeEnum::PALETTE: + break; + case DataFileTypeEnum::RGBA: + break; + case DataFileTypeEnum::SCENE: + break; + case DataFileTypeEnum::SPECIFICATION: + break; + case DataFileTypeEnum::SURFACE: + break; + case DataFileTypeEnum::UNKNOWN: + break; + case DataFileTypeEnum::VOLUME: + if ( ! m_caretMappableDataFile->isMappedWithLabelTable()) { + histogramType = ChartTwoHistogramContentTypeEnum::HISTOGRAM_CONTENT_TYPE_MAP_DATA; + lineSeriesType = ChartTwoLineSeriesContentTypeEnum::LINE_SERIES_CONTENT_BRAINORDINATE_DATA; + } + break; + + } + + if (m_histogramCharting) { + if (histogramType != m_histogramCharting->getHistogramContentType()) { + m_histogramCharting.reset(); + } + } + if ( ! m_histogramCharting) { + m_histogramCharting = std::unique_ptr(new ChartableTwoFileHistogramChart(histogramType, + m_caretMappableDataFile)); + } + + if (m_lineSeriesCharting) { + if (lineSeriesType != m_lineSeriesCharting->getLineSeriesContentType()) { + m_lineSeriesCharting.reset(); + } + } + if ( ! m_lineSeriesCharting) { + m_lineSeriesCharting = std::unique_ptr(new ChartableTwoFileLineSeriesChart(lineSeriesType, + m_caretMappableDataFile)); + } + + if (m_matrixCharting) { + if (matrixType != m_matrixCharting->getMatrixContentType()) { + m_matrixCharting.reset(); + } + else { + /* + * Test for change in valid dimensions but do not alter content of + * "validMatrixRowColumnSelectionDimensions" since it order is important + */ + std::vector newDims(validMatrixRowColumnSelectionDimensions); + std::sort(newDims.begin(), + newDims.end()); + std::vector currentDims; + m_matrixCharting->getValidRowColumnSelectionDimensions(currentDims); + std::sort(currentDims.begin(), + currentDims.end()); + + if ( ! std::equal(currentDims.begin(), + currentDims.end(), + newDims.begin())) { + m_matrixCharting.reset(); + } + } + } + + if ( ! m_matrixCharting) { + m_matrixCharting = std::unique_ptr(new ChartableTwoFileMatrixChart(matrixType, + m_caretMappableDataFile, + validMatrixRowColumnSelectionDimensions)); + } + + clearModified(); +} + +/** + * Clear the content. + */ +void +ChartableTwoFileDelegate::clear() +{ + m_histogramCharting = std::unique_ptr(new ChartableTwoFileHistogramChart(ChartTwoHistogramContentTypeEnum::HISTOGRAM_CONTENT_TYPE_UNSUPPORTED, + m_caretMappableDataFile)); + + m_lineSeriesCharting = std::unique_ptr(new ChartableTwoFileLineSeriesChart(ChartTwoLineSeriesContentTypeEnum::LINE_SERIES_CONTENT_UNSUPPORTED, + m_caretMappableDataFile)); + + std::vector validMatrixRowColumnSelectionDimensions; + m_matrixCharting = std::unique_ptr(new ChartableTwoFileMatrixChart(ChartTwoMatrixContentTypeEnum::MATRIX_CONTENT_UNSUPPORTED, + m_caretMappableDataFile, + validMatrixRowColumnSelectionDimensions)); +} + +/** + * Clear the modification status of this instance. + */ +void +ChartableTwoFileDelegate::clearModified() +{ + m_histogramCharting->clearModified(); + m_lineSeriesCharting->clearModified(); + m_matrixCharting->clearModified(); +} + +/** + * @return True if this instance is modified, else false. + */ +bool +ChartableTwoFileDelegate::isModified() const +{ + if (m_histogramCharting->isModified()) { + return true; + } + if (m_matrixCharting->isModified()) { + return true; + } + if (m_lineSeriesCharting->isModified()) { + return true; + } + + return false; +} + +/** + * @return The CaretMappableDataFile that implements this interface. + * NULL is returned if not a CaretMappableDataFile. + */ +CaretMappableDataFile* +ChartableTwoFileDelegate::getCaretMappableDataFile() +{ + return m_caretMappableDataFile; +} + +/** + * @return The CaretMappableDataFile that implements this interface. + * NULL is returned if not a CaretMappableDataFile. + */ +const CaretMappableDataFile* +ChartableTwoFileDelegate::getCaretMappableDataFile() const +{ + return m_caretMappableDataFile; +} + +/** + * @return Histogram charting. + */ +ChartableTwoFileHistogramChart* +ChartableTwoFileDelegate::getHistogramCharting() +{ + return m_histogramCharting.get(); +} + +/** + * @return Histogram charting. + */ +const ChartableTwoFileHistogramChart* +ChartableTwoFileDelegate::getHistogramCharting() const +{ + return m_histogramCharting.get(); +} + +/** + * @return Line series charting. + */ +ChartableTwoFileLineSeriesChart* +ChartableTwoFileDelegate::getLineSeriesCharting() +{ + return m_lineSeriesCharting.get(); +} + +/** + * @return Line series charting. + */ +const ChartableTwoFileLineSeriesChart* +ChartableTwoFileDelegate::getLineSeriesCharting() const +{ + return m_lineSeriesCharting.get(); +} + +/** + * @return Matrix charting. + */ +ChartableTwoFileMatrixChart* +ChartableTwoFileDelegate::getMatrixCharting() +{ + return m_matrixCharting.get(); +} + +/** + * @return Matrix charting. + */ +const ChartableTwoFileMatrixChart* +ChartableTwoFileDelegate::getMatrixCharting() const +{ + return m_matrixCharting.get(); +} + +/** + * Does this file support any type of charting? + */ +bool +ChartableTwoFileDelegate::isChartingTwoSupported() const +{ + std::vector chartDataTypes; + getSupportedChartTwoDataTypes(chartDataTypes); + + return ( ! chartDataTypes.empty()); +} + +/** + * Test for support of the given chart data type. + * + * @param chartDataType + * Type of chart data. + * @return + * True if the chart data type is supported, else false. + */ +bool +ChartableTwoFileDelegate::isChartingSupportedForChartTwoDataType(const ChartTwoDataTypeEnum::Enum chartDataType) const +{ + std::vector chartDataTypes; + getSupportedChartTwoDataTypes(chartDataTypes); + + if (std::find(chartDataTypes.begin(), + chartDataTypes.end(), + chartDataType) != chartDataTypes.end()) { + return true; + } + + return false; +} + +/** + * Test for support of the given chart compound data type. + * + * @param chartCompoundDataType + * Type of chart compound data. + * @return + * True if the chart compound data type is supported, else false. + */ +bool +ChartableTwoFileDelegate::isChartingSupportedForChartTwoCompoundDataType(const ChartTwoCompoundDataType& chartCompoundDataType) const +{ + std::vector chartCompoundDataTypes; + getSupportedChartTwoCompoundDataTypes(chartCompoundDataTypes); + + for (auto& ccdt : chartCompoundDataTypes) { + if (ccdt == chartCompoundDataType) { + return true; + } + } + + return false; +} + +/** + * Get chart data types supported by this file. + * + * @param chartDataTypesOut + * Output containing all chart data types supported by this data file. + */ +void +ChartableTwoFileDelegate::getSupportedChartTwoDataTypes(std::vector& chartDataTypesOut) const +{ + chartDataTypesOut.clear(); + + if (m_histogramCharting != NULL) { + chartDataTypesOut.push_back(m_histogramCharting->getChartTwoDataType()); + } + if (m_lineSeriesCharting!= NULL) { + chartDataTypesOut.push_back(m_lineSeriesCharting->getChartTwoDataType()); + } + if (m_matrixCharting != NULL) { + chartDataTypesOut.push_back(m_matrixCharting->getChartTwoDataType()); + } +} + +/** + * Get chart data types supported by this file. + * + * @param chartDataTypesOut + * Output containing all chart data types supported by this data file. + */ +void +ChartableTwoFileDelegate::getSupportedChartTwoCompoundDataTypes(std::vector& chartCompoundDataTypesOut) const +{ + chartCompoundDataTypesOut.clear(); + + if (m_histogramCharting->getHistogramContentType() != ChartTwoHistogramContentTypeEnum::HISTOGRAM_CONTENT_TYPE_UNSUPPORTED) { + chartCompoundDataTypesOut.push_back(m_histogramCharting->getChartTwoCompoundDataType()); + } + if (m_lineSeriesCharting->getLineSeriesContentType() != ChartTwoLineSeriesContentTypeEnum::LINE_SERIES_CONTENT_UNSUPPORTED) { + chartCompoundDataTypesOut.push_back(m_lineSeriesCharting->getChartTwoCompoundDataType()); + } + if (m_matrixCharting->getMatrixContentType() != ChartTwoMatrixContentTypeEnum::MATRIX_CONTENT_UNSUPPORTED) { + chartCompoundDataTypesOut.push_back(m_matrixCharting->getChartTwoCompoundDataType()); + } +} + +/** + * Get the chart compound data type supported by this file that uses the given + * chart data type. + * + * @param chartDataType + * The chart data type. + * @param chartCompoundDataTypeOut + * Output with the chart compound data type. + * @return + * True if there is output chart compound data type is valid. + * False if output chart compound data type is invalid OR if chartDataType is invalid. + */ +bool +ChartableTwoFileDelegate::getChartTwoCompoundDataTypeForChartTwoDataType(const ChartTwoDataTypeEnum::Enum chartDataType, + ChartTwoCompoundDataType& chartCompoundDataTypeOut) const +{ + std::vector chartCompoundDataTypes; + getSupportedChartTwoCompoundDataTypes(chartCompoundDataTypes); + for (auto& cdt : chartCompoundDataTypes) { + if (cdt.getChartTwoDataType() == chartDataType) { + chartCompoundDataTypeOut = cdt; + return true; + } + } + + /* default constructor is invalid data type */ + chartCompoundDataTypeOut = ChartTwoCompoundDataType(); + return false; +} + +/** + * Save information specific to this type of model to the scene. + * + * @param sceneAttributes + * Attributes for the scene. Scenes may be of different types + * (full, generic, etc) and the attributes should be checked when + * saving the scene. + * + * @param instanceName + * Name of instance in the scene. + */ +SceneClass* +ChartableTwoFileDelegate::saveToScene(const SceneAttributes* sceneAttributes, + const AString& instanceName) +{ + if ( ! isChartingTwoSupported()) { + return NULL; + } + + SceneClass* sceneClass = new SceneClass(instanceName, + "ChartableTwoFileDelegate", + 1); + m_sceneAssistant->saveMembers(sceneAttributes, + sceneClass); + + if (m_histogramCharting != NULL) { + sceneClass->addClass(m_histogramCharting->saveToScene(sceneAttributes, + "m_histogramCharting")); + } + if (m_lineSeriesCharting != NULL) { + sceneClass->addClass(m_lineSeriesCharting->saveToScene(sceneAttributes, + "m_lineSeriesCharting")); + } + if (m_matrixCharting != NULL) { + sceneClass->addClass(m_matrixCharting->saveToScene(sceneAttributes, + "m_matrixCharting")); + } + + return sceneClass; +} + +/** + * Restore information specific to the type of model from the scene. + * + * @param sceneAttributes + * Attributes for the scene. Scenes may be of different types + * (full, generic, etc) and the attributes should be checked when + * restoring the scene. + * + * @param sceneClass + * sceneClass from which model specific information is obtained. + */ +void +ChartableTwoFileDelegate::restoreFromScene(const SceneAttributes* sceneAttributes, + const SceneClass* sceneClass) +{ + if (sceneClass == NULL) { + return; + } + + m_sceneAssistant->restoreMembers(sceneAttributes, + sceneClass); + + const SceneClass* histogramClass = sceneClass->getClass("m_histogramCharting"); + if (histogramClass != NULL) { + CaretAssert(m_histogramCharting); + m_histogramCharting->restoreFromScene(sceneAttributes, + histogramClass); + } + + const SceneClass* lineSeriesClass = sceneClass->getClass("m_lineSeriesCharting"); + if (lineSeriesClass != NULL) { + CaretAssert(m_lineSeriesCharting); + m_lineSeriesCharting->restoreFromScene(sceneAttributes, + lineSeriesClass); + } + + const SceneClass* matrixClass = sceneClass->getClass("m_matrixCharting"); + if (matrixClass != NULL) { + CaretAssert(m_matrixCharting); + m_matrixCharting->restoreFromScene(sceneAttributes, + matrixClass); + } +} + diff --git a/src/Files/ChartableTwoFileDelegate.h b/src/Files/ChartableTwoFileDelegate.h new file mode 100644 index 0000000000000000000000000000000000000000..ab0947b8aaf5df80be85d96c4c46684c1e703c83 --- /dev/null +++ b/src/Files/ChartableTwoFileDelegate.h @@ -0,0 +1,117 @@ +#ifndef __CHARTABLE_TWO_FILE_DELEGATE_H__ +#define __CHARTABLE_TWO_FILE_DELEGATE_H__ + +/*LICENSE_START*/ +/* + * Copyright (C) 2016 Washington University School of Medicine + * + * 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 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ +/*LICENSE_END*/ + +#include + +#include "CaretObjectTracksModification.h" +#include "ChartTwoCompoundDataType.h" +#include "ChartTwoDataTypeEnum.h" +#include "SceneableInterface.h" + +namespace caret { + + class CaretMappableDataFile; + class ChartableTwoFileHistogramChart; + class ChartableTwoFileLineSeriesChart; + class ChartableTwoFileMatrixChart; + + class ChartableTwoFileDelegate : public CaretObjectTracksModification, public SceneableInterface { + + public: + ChartableTwoFileDelegate(CaretMappableDataFile* caretMappableDataFile); + + virtual ~ChartableTwoFileDelegate(); + + void updateAfterFileChanged(); + + // do not make this virtual since called from constructor + void clear(); + + virtual void clearModified() override; + + virtual bool isModified() const override; + + CaretMappableDataFile* getCaretMappableDataFile(); + + const CaretMappableDataFile* getCaretMappableDataFile() const; + + ChartableTwoFileHistogramChart* getHistogramCharting(); + + const ChartableTwoFileHistogramChart* getHistogramCharting() const; + + ChartableTwoFileLineSeriesChart* getLineSeriesCharting(); + + const ChartableTwoFileLineSeriesChart* getLineSeriesCharting() const; + + ChartableTwoFileMatrixChart* getMatrixCharting(); + + const ChartableTwoFileMatrixChart* getMatrixCharting() const; + + bool isChartingTwoSupported() const; + + bool isChartingSupportedForChartTwoDataType(const ChartTwoDataTypeEnum::Enum chartDataType) const; + + bool isChartingSupportedForChartTwoCompoundDataType(const ChartTwoCompoundDataType& chartCompoundDataType) const; + + void getSupportedChartTwoDataTypes(std::vector& chartDataTypesOut) const; + + void getSupportedChartTwoCompoundDataTypes(std::vector& chartCompoundDataTypesOut) const; + + bool getChartTwoCompoundDataTypeForChartTwoDataType(const ChartTwoDataTypeEnum::Enum chartDataType, + ChartTwoCompoundDataType& chartCompoundDataTypeOut) const; + + + virtual SceneClass* saveToScene(const SceneAttributes* sceneAttributes, + const AString& instanceName) override; + + virtual void restoreFromScene(const SceneAttributes* sceneAttributes, + const SceneClass* sceneClass) override; + + // ADD_NEW_METHODS_HERE + + private: + ChartableTwoFileDelegate(const ChartableTwoFileDelegate&); + + ChartableTwoFileDelegate& operator=(const ChartableTwoFileDelegate&); + + std::unique_ptr m_sceneAssistant; + + /** points to parent caret mappable data file */ + CaretMappableDataFile* m_caretMappableDataFile = NULL; + + std::unique_ptr m_histogramCharting; + + std::unique_ptr m_lineSeriesCharting; + + std::unique_ptr m_matrixCharting; + + // ADD_NEW_MEMBERS_HERE + + }; + +#ifdef __CHARTABLE_TWO_FILE_DELEGATE_DECLARE__ + // +#endif // __CHARTABLE_TWO_FILE_DELEGATE_DECLARE__ + +} // namespace +#endif //__CHARTABLE_TWO_FILE_DELEGATE_H__ diff --git a/src/Files/ChartableTwoFileHistogramChart.cxx b/src/Files/ChartableTwoFileHistogramChart.cxx new file mode 100644 index 0000000000000000000000000000000000000000..0d66578200e282ea5a2e8fd6dec7d9b31d6bba63 --- /dev/null +++ b/src/Files/ChartableTwoFileHistogramChart.cxx @@ -0,0 +1,670 @@ + +/*LICENSE_START*/ +/* + * Copyright (C) 2017 Washington University School of Medicine + * + * 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 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ +/*LICENSE_END*/ + +#define __CHARTABLE_TWO_FILE_HISTOGRAM_CHART_DECLARE__ +#include "ChartableTwoFileHistogramChart.h" +#undef __CHARTABLE_TWO_FILE_HISTOGRAM_CHART_DECLARE__ + +#include "CaretAssert.h" +#include "CaretMappableDataFile.h" +#include "CaretLogger.h" +#include "CaretPreferences.h" +#include "EventCaretPreferencesGet.h" +#include "EventManager.h" +#include "FastStatistics.h" +#include "Histogram.h" +#include "NodeAndVoxelColoring.h" +#include "PaletteColorMapping.h" +#include "SceneClass.h" +#include "SceneClassAssistant.h" + +using namespace caret; + + + +/** + * \class caret::ChartableTwoFileHistogramChart + * \brief Implementation of base chart delegate for histogram charts. + * \ingroup Files + */ + +/** + * Constructor. + * + * @param histogramContentType + * Content of histogram. + * @param parentCaretMappableDataFile + * Parent caret mappable data file that this delegate supports. + */ +ChartableTwoFileHistogramChart::ChartableTwoFileHistogramChart(const ChartTwoHistogramContentTypeEnum::Enum histogramContentType, + CaretMappableDataFile* parentCaretMappableDataFile) +: ChartableTwoFileBaseChart(ChartTwoDataTypeEnum::CHART_DATA_TYPE_HISTOGRAM, + parentCaretMappableDataFile), +m_histogramContentType(histogramContentType) +{ + m_sceneAssistant = new SceneClassAssistant(); + + int32_t histogramNumberOfBuckets = 0; + switch (m_histogramContentType) { + case ChartTwoHistogramContentTypeEnum::HISTOGRAM_CONTENT_TYPE_UNSUPPORTED: + break; + case ChartTwoHistogramContentTypeEnum::HISTOGRAM_CONTENT_TYPE_MAP_DATA: + { + CaretMappableDataFile* cmdf = getCaretMappableDataFile(); + if (cmdf->getNumberOfMaps() > 0) { + const Histogram* histogram = cmdf->getMapHistogram(0); + if (histogram != NULL) { + histogramNumberOfBuckets = histogram->getNumberOfBuckets(); + } + } + } + break; + } + + updateChartTwoCompoundDataTypeAfterFileChanges(ChartTwoCompoundDataType::newInstanceForHistogram(histogramNumberOfBuckets)); +} + +/** + * Destructor. + */ +ChartableTwoFileHistogramChart::~ChartableTwoFileHistogramChart() +{ + delete m_sceneAssistant; +} + +/** + * @return Content type of histogram. + */ +ChartTwoHistogramContentTypeEnum::Enum +ChartableTwoFileHistogramChart::getHistogramContentType() const +{ + return m_histogramContentType; +} + +/** + * @return Is this charting valid ? + */ +bool +ChartableTwoFileHistogramChart::isValid() const +{ + return (m_histogramContentType != ChartTwoHistogramContentTypeEnum::HISTOGRAM_CONTENT_TYPE_UNSUPPORTED); +} + +/** + * @retrurn Is this charting empty (no data at this time) + */ +bool +ChartableTwoFileHistogramChart::isEmpty() const +{ + if ( ! isValid()) { + return true; + } + + return m_mapHistogramPrimitives.empty(); +} + +/** + * Invalidate coloring for all histograms. + */ +void +ChartableTwoFileHistogramChart::invalidateAllColoring() +{ + m_mapHistogramPrimitives.clear(); +} + +/** + * Get the histogram for drawing histogram as a chart + * + * @param mapIndex + * Index of the map. + * @param useDataFromAllMapsFlag + * If true, include data from all maps for histogram chart. + * @return + * Histogram for histogram chart drawing. May be NULL if map is not + * mapped with a color palette or the map index is invalid. + */ +const Histogram* +ChartableTwoFileHistogramChart::getHistogramForChartDrawing(const int32_t mapIndex, + const bool useDataFromAllMapsFlag) +{ + CaretMappableDataFile* myMapFile = getCaretMappableDataFile(); + CaretAssert(myMapFile); + + const Histogram* histogramOut= NULL; + + if ( ! myMapFile->isMappedWithPalette()) { + return histogramOut; + } + if ((mapIndex < 0) + || (mapIndex >= myMapFile->getNumberOfMaps())) { + return histogramOut; + } + + PaletteColorMapping* paletteColorMapping = myMapFile->getMapPaletteColorMapping(mapIndex); + CaretAssert(paletteColorMapping); + + FastStatistics* statistics = NULL; + if (useDataFromAllMapsFlag) { + statistics = const_cast(myMapFile->getFileFastStatistics()); + } + else { + switch (myMapFile->getPaletteNormalizationMode()) { + case PaletteNormalizationModeEnum::NORMALIZATION_ALL_MAP_DATA: + statistics = const_cast(myMapFile->getFileFastStatistics()); + break; + case PaletteNormalizationModeEnum::NORMALIZATION_SELECTED_MAP_DATA: + statistics = const_cast(myMapFile->getMapFastStatistics(mapIndex)); + break; + } + } + + /* + * Statistics may be NULL for connectivity files (dense, dense dynamic) + * that have not yet loaded any data caused by the user clicking + * brainordinates. + */ + if (statistics == NULL) { + return histogramOut; + } + + CaretAssert(statistics); + float mostPos = 0.0; + float leastPos = 0.0; + float leastNeg = 0.0; + float mostNeg = 0.0; + bool matchFlag = false; + + switch (paletteColorMapping->getHistogramRangeMode()) { + case PaletteHistogramRangeModeEnum::PALETTE_HISTOGRAM_RANGE_ALL: + { + float dummy; + statistics->getNonzeroRanges(mostNeg, dummy, dummy, mostPos); + } + break; + case PaletteHistogramRangeModeEnum::PALETTE_HISTOGRAM_RANGE_MATCH_PALETTE: + { + matchFlag = true; + switch (paletteColorMapping->getScaleMode()) { + case PaletteScaleModeEnum::MODE_AUTO_SCALE: + mostPos = statistics->getMax(); + leastPos = 0.0; + leastNeg = 0.0; + mostNeg = statistics->getMin(); + break; + case PaletteScaleModeEnum::MODE_AUTO_SCALE_ABSOLUTE_PERCENTAGE: + mostPos = statistics->getApproxAbsolutePercentile(paletteColorMapping->getAutoScaleAbsolutePercentageMaximum()); + leastPos = statistics->getApproxAbsolutePercentile(paletteColorMapping->getAutoScaleAbsolutePercentageMinimum()); + leastNeg = -statistics->getApproxAbsolutePercentile(paletteColorMapping->getAutoScaleAbsolutePercentageMinimum()); + mostNeg = -statistics->getApproxAbsolutePercentile(paletteColorMapping->getAutoScaleAbsolutePercentageMaximum()); + break; + case PaletteScaleModeEnum::MODE_AUTO_SCALE_PERCENTAGE: + mostPos = statistics->getApproxPositivePercentile(paletteColorMapping->getAutoScalePercentagePositiveMaximum()); + leastPos = statistics->getApproxPositivePercentile(paletteColorMapping->getAutoScalePercentagePositiveMinimum()); + leastNeg = statistics->getApproxNegativePercentile(paletteColorMapping->getAutoScalePercentageNegativeMinimum()); + mostNeg = statistics->getApproxNegativePercentile(paletteColorMapping->getAutoScalePercentageNegativeMaximum()); + break; + case PaletteScaleModeEnum::MODE_USER_SCALE: + mostPos = paletteColorMapping->getUserScalePositiveMaximum(); + leastPos = paletteColorMapping->getUserScalePositiveMinimum(); + leastNeg = paletteColorMapping->getUserScaleNegativeMinimum(); + mostNeg = paletteColorMapping->getUserScaleNegativeMaximum(); + break; + } + } + break; + } + + /* + * Remove data that is not displayed + */ + bool isZeroIncluded = true; + if (matchFlag) { + isZeroIncluded = paletteColorMapping->isDisplayZeroDataFlag(); + + if ( ! paletteColorMapping->isDisplayNegativeDataFlag()) { + mostNeg = 0.0; + leastNeg = 0.0; + } + if ( ! paletteColorMapping->isDisplayPositiveDataFlag()) { + mostPos = 0.0; + leastPos = 0.0; + } + + if (useDataFromAllMapsFlag) { + histogramOut = myMapFile->getFileHistogram(mostPos, + leastPos, + leastNeg, + mostNeg, + isZeroIncluded); + } + else { + switch (myMapFile->getPaletteNormalizationMode()) { + case PaletteNormalizationModeEnum::NORMALIZATION_ALL_MAP_DATA: + histogramOut = myMapFile->getFileHistogram(mostPos, + leastPos, + leastNeg, + mostNeg, + isZeroIncluded); + break; + case PaletteNormalizationModeEnum::NORMALIZATION_SELECTED_MAP_DATA: + histogramOut = myMapFile->getMapHistogram(mapIndex, + mostPos, + leastPos, + leastNeg, + mostNeg, + isZeroIncluded); + break; + } + } + } + else { + if (useDataFromAllMapsFlag) { + histogramOut = myMapFile->getFileHistogram(); + } + else { + switch (myMapFile->getPaletteNormalizationMode()) { + case PaletteNormalizationModeEnum::NORMALIZATION_ALL_MAP_DATA: + histogramOut = myMapFile->getFileHistogram(); + break; + case PaletteNormalizationModeEnum::NORMALIZATION_SELECTED_MAP_DATA: + histogramOut = myMapFile->getMapHistogram(mapIndex); + break; + } + } + } + + CaretAssert(histogramOut); + + return histogramOut; +} + +/** + * Get the graphics primitive for drawing bars for the given map index. + * + * @param mapIndex + * Index of the map. + * @return + * Primitives for drawing the histogram, bars, and envelope. May be NULL if map is not + * mapped with a color palette or the map index is invalid. + */ +ChartableTwoFileHistogramChart::HistogramPrimitives* +ChartableTwoFileHistogramChart::getMapHistogramDrawingPrimitives(const int32_t mapIndex, + const bool useDataFromAllMapsFlag) +{ + MapIndexPrimitiveContainer::iterator iter = m_mapHistogramPrimitives.find(mapIndex); + if (iter != m_mapHistogramPrimitives.end()) { + HistogramPrimitives* histogramPrimitives = iter->second.get(); + if (histogramPrimitives != NULL) { + return histogramPrimitives; + } + } + + CaretMappableDataFile* myMapFile = getCaretMappableDataFile(); + CaretAssert(myMapFile); + + const Histogram* histogram = getHistogramForChartDrawing(mapIndex, + useDataFromAllMapsFlag); + if (histogram == NULL) { + return NULL; + } + + CaretAssert(histogram); + + FastStatistics* statistics = NULL; + if (useDataFromAllMapsFlag) { + statistics = const_cast(myMapFile->getFileFastStatistics()); + } + else { + switch (myMapFile->getPaletteNormalizationMode()) { + case PaletteNormalizationModeEnum::NORMALIZATION_ALL_MAP_DATA: + statistics = const_cast(myMapFile->getFileFastStatistics()); + break; + case PaletteNormalizationModeEnum::NORMALIZATION_SELECTED_MAP_DATA: + statistics = const_cast(myMapFile->getMapFastStatistics(mapIndex)); + break; + } + } + + PaletteColorMapping* paletteColorMapping = myMapFile->getMapPaletteColorMapping(mapIndex); + CaretAssert(paletteColorMapping); + + float minValueX = 0.0; + float maxValueX = 0.0; + histogram->getRange(minValueX, + maxValueX); + const float valueRangeX = maxValueX - minValueX; + if (valueRangeX <= 0.0) { + CaretLogSevere("Range of histogram X-values is zero"); + return NULL; + } + + if ((paletteColorMapping != NULL) + && (statistics != NULL) + && (histogram != NULL)) { + std::vector histogramBuckets = histogram->getHistogramDisplay(); + const int32_t numBucketValues = static_cast(histogramBuckets.size()); + if (numBucketValues < 2) { + CaretLogSevere("Histogram must contain two or more values"); + return NULL; + } + + const float bucketWidth = valueRangeX / (numBucketValues - 1); + std::vector xValuesAtBuckets; + xValuesAtBuckets.reserve(numBucketValues); + for (int32_t i = 0; i < numBucketValues; i++) { + xValuesAtBuckets.push_back(minValueX + (bucketWidth * i)); + } + CaretAssert(xValuesAtBuckets.size() == histogramBuckets.size()); + + std::vector histogramRGBA; + histogramRGBA.resize(numBucketValues * 4, + 0.0f); + NodeAndVoxelColoring::colorScalarsWithPalette(statistics, + paletteColorMapping, + &xValuesAtBuckets[0], + paletteColorMapping, + &xValuesAtBuckets[0], + numBucketValues, + &histogramRGBA[0], + true); // ignore thresholding + + + const int32_t estimatedNumberOfVerticesForQuads = (numBucketValues * 4); // four vertices per quad + const int32_t estimatedNumberOfVerticesForEnvelope = (numBucketValues * 2) + 4; // 2 vertices per line segment plus start/end + + /* + * Use Quads when drawing bars (four vertices quad). + * Use Lines when drawing envelope (two vertices per line) + * Using quads and lines simplifies identification of individual bars in the histogram + */ + GraphicsPrimitiveV3fC4f* barsPrimitive = new GraphicsPrimitiveV3fC4f(GraphicsPrimitive::PrimitiveType::OPENGL_TRIANGLES); + barsPrimitive->reserveForNumberOfVertices(estimatedNumberOfVerticesForQuads); + GraphicsPrimitiveV3fC4f* envelopePrimitive = new GraphicsPrimitiveV3fC4f(GraphicsPrimitive::PrimitiveType::POLYGONAL_LINE_STRIP_BEVEL_JOIN); + envelopePrimitive->reserveForNumberOfVertices(estimatedNumberOfVerticesForEnvelope); + GraphicsPrimitiveV3fC4f* thresholdPrimitive = NULL; + + bool setThresholdingFlag = false; + switch (paletteColorMapping->getThresholdType()) { + case PaletteThresholdTypeEnum::THRESHOLD_TYPE_MAPPED: + break; + case PaletteThresholdTypeEnum::THRESHOLD_TYPE_MAPPED_AVERAGE_AREA: + break; + case PaletteThresholdTypeEnum::THRESHOLD_TYPE_NORMAL: + setThresholdingFlag = true; + break; + case PaletteThresholdTypeEnum::THRESHOLD_TYPE_FILE: + break; + case PaletteThresholdTypeEnum::THRESHOLD_TYPE_OFF: + break; + } + + if (setThresholdingFlag) { + /* + * RGBA for thresholding + */ + EventCaretPreferencesGet preferencesEvent; + EventManager::get()->sendEvent(preferencesEvent.getPointer()); + CaretPreferences* caretPreferences = preferencesEvent.getCaretPreferences(); + float threshRGBA[4] = { 1.0, 0.0, 0.0, 1.0 }; + if (caretPreferences != NULL) { + uint8_t threshByteRGBA[4]; + caretPreferences->getBackgroundAndForegroundColors()->getColorChartHistogramThreshold(threshByteRGBA); + threshRGBA[0] = static_cast(threshByteRGBA[0]) / 255.0f; + threshRGBA[1] = static_cast(threshByteRGBA[1]) / 255.0f; + threshRGBA[2] = static_cast(threshByteRGBA[2]) / 255.0f; + threshRGBA[3] = 1.0; + } + const float minX = -1000000.0f; + const float maxX = 1000000.0f; + const float minY = 0.0; + const float maxY = *std::max_element(histogramBuckets.begin(), + histogramBuckets.end()); + + float threshMinValue = paletteColorMapping->getThresholdNormalMinimum(); + float threshMaxValue = paletteColorMapping->getThresholdNormalMaximum(); + + thresholdPrimitive = new GraphicsPrimitiveV3fC4f(GraphicsPrimitive::PrimitiveType::OPENGL_TRIANGLES); + + switch (paletteColorMapping->getThresholdTest()) { + case PaletteThresholdTestEnum::THRESHOLD_TEST_SHOW_INSIDE: + { + thresholdPrimitive->reserveForNumberOfVertices(12); + + thresholdPrimitive->addVertex(minX, maxY, threshRGBA); + thresholdPrimitive->addVertex(minX, minY, threshRGBA); + thresholdPrimitive->addVertex(threshMinValue, minY, threshRGBA); + + thresholdPrimitive->addVertex(minX, maxY, threshRGBA); + thresholdPrimitive->addVertex(threshMinValue, minY, threshRGBA); + thresholdPrimitive->addVertex(threshMinValue, maxY, threshRGBA); + + + thresholdPrimitive->addVertex(threshMaxValue, maxY, threshRGBA); + thresholdPrimitive->addVertex(threshMaxValue, minY, threshRGBA); + thresholdPrimitive->addVertex(maxX, minY, threshRGBA); + + thresholdPrimitive->addVertex(threshMaxValue, maxY, threshRGBA); + thresholdPrimitive->addVertex(maxX, minY, threshRGBA); + thresholdPrimitive->addVertex(maxX, maxY, threshRGBA); + } + break; + case PaletteThresholdTestEnum::THRESHOLD_TEST_SHOW_OUTSIDE: + { + thresholdPrimitive->reserveForNumberOfVertices(6); + + thresholdPrimitive->addVertex(threshMinValue, maxY, threshRGBA); + thresholdPrimitive->addVertex(threshMinValue, minY, threshRGBA); + thresholdPrimitive->addVertex(threshMaxValue, minY, threshRGBA); + + thresholdPrimitive->addVertex(threshMinValue, maxY, threshRGBA); + thresholdPrimitive->addVertex(threshMaxValue, minY, threshRGBA); + thresholdPrimitive->addVertex(threshMaxValue, maxY, threshRGBA); + } + break; + } + } + + /* + * If selected, apply solid color to envelope histogram + */ + const CaretColorEnum::Enum histogramEnvelopeColor = paletteColorMapping->getHistogramEnvelopeColor(); + float envelopeSolidColorRGBA[4] = { 0.0, 0.0, 0.0, 0.0 }; + bool envelopeSolidColorFlag = false; + if (histogramEnvelopeColor != CaretColorEnum::CUSTOM) { + CaretColorEnum::toRGBAFloat(histogramEnvelopeColor, + envelopeSolidColorRGBA); + envelopeSolidColorFlag = true; + } + + /* + * If selected, apply solid color to bars histogram + */ + const CaretColorEnum::Enum histogramBarsColor = paletteColorMapping->getHistogramBarsColor(); + float barsSolidColorRGBA[4] = { 0.0, 0.0, 0.0, 0.0 }; + bool barsSolidColorFlag = false; + if (histogramBarsColor != CaretColorEnum::CUSTOM) { + CaretColorEnum::toRGBAFloat(histogramBarsColor, + barsSolidColorRGBA); + barsSolidColorFlag = true; + } + + /* + * Move start one-half of bucket width so that bar's + * X-value is centered within the bar + */ + const float halfBucketWidth = bucketWidth / 2.0f; + const float startX = minValueX - halfBucketWidth; + + + float x = startX; + for (int32_t i = 0; i < numBucketValues; i++) { + CaretAssertVectorIndex(histogramBuckets, i); + const float xMin = x; + const float xMax = x + bucketWidth; + const float yMin = 0.0f; + float yMax = histogramBuckets[i]; + + CaretAssertVectorIndex(histogramRGBA, i*4+3); + const float* rgba = &histogramRGBA[i * 4]; + const float alpha = rgba[3]; + + /* + * Use a height of zero when there is no coloring + * (apha == 0) for a bar since the bars will be + * smoothed to create the envelope. + */ + if (alpha <= 0.0) { + yMax = 0.0; + } + + if (barsSolidColorFlag) { + barsSolidColorRGBA[3] = rgba[3]; + } + const float* cellRGBA = (barsSolidColorFlag ? barsSolidColorRGBA : rgba); + + /* Triangle One for Cell*/ + barsPrimitive->addVertex(xMin, yMax, cellRGBA); + barsPrimitive->addVertex(xMin, yMin, cellRGBA); + barsPrimitive->addVertex(xMax, yMin, cellRGBA); + + /* Triangle Two for Cell */ + barsPrimitive->addVertex(xMin, yMax, cellRGBA); + barsPrimitive->addVertex(xMax, yMin, cellRGBA); + barsPrimitive->addVertex(xMax, yMax, cellRGBA); + + if (envelopeSolidColorFlag) { + const float envelopeX = x + halfBucketWidth; + + if (i == 0) { + envelopePrimitive->addVertex(envelopeX, yMin, envelopeSolidColorRGBA); + } + envelopePrimitive->addVertex(envelopeX, yMax, envelopeSolidColorRGBA); + if (i == (numBucketValues - 1)) { + envelopePrimitive->addVertex(envelopeX, yMin, envelopeSolidColorRGBA); + } + } + else { + const float envelopeX = x + halfBucketWidth; + + if (i == 0) { + envelopePrimitive->addVertex(envelopeX, yMin, rgba); + } + envelopePrimitive->addVertex(envelopeX, yMax, rgba); + if (i == (numBucketValues - 1)) { + envelopePrimitive->addVertex(envelopeX, yMin, rgba); + } + } + + x += bucketWidth; + } + + HistogramPrimitives* histogramPrimitives = new HistogramPrimitives(thresholdPrimitive, + barsPrimitive, + envelopePrimitive, + paletteColorMapping->getHistogramEnvelopeLineWidthPercentage()); + + m_mapHistogramPrimitives.insert(std::make_pair(mapIndex, + std::unique_ptr(histogramPrimitives))); + + return histogramPrimitives; + + } + + return NULL; +} + +/** + * Save subclass data to the scene. + * + * @param sceneAttributes + * Attributes for the scene. Scenes may be of different types + * (full, generic, etc) and the attributes should be checked when + * restoring the scene. + * + * @param sceneClass + * sceneClass to which data members should be added. Will always + * be valid (non-NULL). + */ +void +ChartableTwoFileHistogramChart::saveSubClassDataToScene(const SceneAttributes* sceneAttributes, + SceneClass* sceneClass) +{ + m_sceneAssistant->saveMembers(sceneAttributes, + sceneClass); +} + +/** + * Restore file data from the scene. + * + * @param sceneAttributes + * Attributes for the scene. Scenes may be of different types + * (full, generic, etc) and the attributes should be checked when + * restoring the scene. + * + * @param sceneClass + * sceneClass for the instance of a class that implements + * this interface. Will NEVER be NULL. + */ +void +ChartableTwoFileHistogramChart::restoreSubClassDataFromScene(const SceneAttributes* sceneAttributes, + const SceneClass* sceneClass) +{ + m_sceneAssistant->restoreMembers(sceneAttributes, + sceneClass); +} + +/** + * Default constructor. + */ +ChartableTwoFileHistogramChart::HistogramPrimitives::HistogramPrimitives() +{ +} + +/** + * Constructor for primitives + * + * @param thresholdPrimitive + * Graphics Primitive for drawing threshold ranges. + * @param barsPrimitive + * Graphics Primitive for drawing histogram bars. + * @param envelopePrimitive + * Graphics Primitive for drawing histogram envelope. + * @param envelopeLineWidthPercentage + * Line width percentage for drawing envelope. + */ +ChartableTwoFileHistogramChart::HistogramPrimitives::HistogramPrimitives(GraphicsPrimitiveV3fC4f* thresholdPrimitive, + GraphicsPrimitiveV3fC4f* barsPrimitive, + GraphicsPrimitiveV3fC4f* envelopePrimitive, + const float envelopeLineWidthPercentage) +{ + m_thresholdPrimitive.reset(thresholdPrimitive); + m_barsPrimitive.reset(barsPrimitive); + m_envelopePrimitive.reset(envelopePrimitive); + m_envelopeLineWidthPercentage = envelopeLineWidthPercentage; +} + +/* + * Destructor. + */ +ChartableTwoFileHistogramChart::HistogramPrimitives::~HistogramPrimitives() +{ + +} diff --git a/src/Files/ChartableTwoFileHistogramChart.h b/src/Files/ChartableTwoFileHistogramChart.h new file mode 100644 index 0000000000000000000000000000000000000000..9e28bb43f93b88efacbda9f597cbf36e4d807eb2 --- /dev/null +++ b/src/Files/ChartableTwoFileHistogramChart.h @@ -0,0 +1,132 @@ +#ifndef __CHARTABLE_TWO_FILE_HISTOGRAM_CHART_H__ +#define __CHARTABLE_TWO_FILE_HISTOGRAM_CHART_H__ + +/*LICENSE_START*/ +/* + * Copyright (C) 2017 Washington University School of Medicine + * + * 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 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ +/*LICENSE_END*/ + +#include +#include + +#include "ChartableTwoFileBaseChart.h" +#include "ChartTwoHistogramContentTypeEnum.h" +#include "GraphicsPrimitiveV3fC4f.h" // forward declaration has problems + +namespace caret { + class Histogram; + + class ChartableTwoFileHistogramChart : public ChartableTwoFileBaseChart { + + public: + /** + * Contains the graphics primitives for drawing + * the histogram threshold, bars, and envelope. + */ + class HistogramPrimitives { + public: + HistogramPrimitives(); + + HistogramPrimitives(GraphicsPrimitiveV3fC4f* thresholdPrimitive, + GraphicsPrimitiveV3fC4f* barsPrimitive, + GraphicsPrimitiveV3fC4f* envelopePrimitive, + const float envelopeLineWidthPercentage); + + ~HistogramPrimitives(); + + /* + * @return Graphics primitive for drawing threshlold. + * NULL if invalid. + */ + GraphicsPrimitiveV3fC4f* getThresholdPrimitive() { return m_thresholdPrimitive.get(); } + + /* + * @return Graphics primitive for drawing histogram bars. + * NULL if invalid. + */ + GraphicsPrimitiveV3fC4f* getBarsPrimitive() { return m_barsPrimitive.get(); } + + /* + * @return Graphics primitive for drawing histogram envelope. + * NULL if invalid. + */ + GraphicsPrimitiveV3fC4f* getEnvelopePrimitive() { return m_envelopePrimitive.get(); } + + /** + * @return Line width percentage for drawing envelope + */ + float getEnvelopeLineWidthPercentage() const { return m_envelopeLineWidthPercentage; } + + private: + std::unique_ptr m_thresholdPrimitive; + std::unique_ptr m_barsPrimitive; + std::unique_ptr m_envelopePrimitive; + float m_envelopeLineWidthPercentage = 1.0f; + }; + + ChartableTwoFileHistogramChart(const ChartTwoHistogramContentTypeEnum::Enum histogramContentType, + CaretMappableDataFile* parentCaretMappableDataFile); + + virtual ~ChartableTwoFileHistogramChart(); + + ChartTwoHistogramContentTypeEnum::Enum getHistogramContentType() const; + + virtual bool isValid() const override; + + virtual bool isEmpty() const override; + + const Histogram* getHistogramForChartDrawing(const int32_t mapIndex, + const bool useDataFromAllMapsFlag); + + HistogramPrimitives* getMapHistogramDrawingPrimitives(const int32_t mapIndex, + const bool useDataFromAllMapsFlag); + + void invalidateAllColoring(); + + // ADD_NEW_METHODS_HERE + + protected: + virtual void saveSubClassDataToScene(const SceneAttributes* sceneAttributes, + SceneClass* sceneClass) override; + + virtual void restoreSubClassDataFromScene(const SceneAttributes* sceneAttributes, + const SceneClass* sceneClass) override; + + private: + ChartableTwoFileHistogramChart(const ChartableTwoFileHistogramChart&); + + ChartableTwoFileHistogramChart& operator=(const ChartableTwoFileHistogramChart&); + + SceneClassAssistant* m_sceneAssistant; + + const ChartTwoHistogramContentTypeEnum::Enum m_histogramContentType; + + typedef std::map> MapIndexPrimitiveContainer; + + MapIndexPrimitiveContainer m_mapHistogramPrimitives; + + // ADD_NEW_MEMBERS_HERE + + }; + +#ifdef __CHARTABLE_TWO_FILE_HISTOGRAM_CHART_DECLARE__ + // +#endif // __CHARTABLE_TWO_FILE_HISTOGRAM_CHART_DECLARE__ + +} // namespace +#endif //__CHARTABLE_TWO_FILE_HISTOGRAM_CHART_H__ diff --git a/src/Files/ChartableTwoFileLineSeriesChart.cxx b/src/Files/ChartableTwoFileLineSeriesChart.cxx new file mode 100644 index 0000000000000000000000000000000000000000..7aec0abaa8537ba1ac8d47eeed887e0cc0447e51 --- /dev/null +++ b/src/Files/ChartableTwoFileLineSeriesChart.cxx @@ -0,0 +1,446 @@ + +/*LICENSE_START*/ +/* + * Copyright (C) 2017 Washington University School of Medicine + * + * 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 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ +/*LICENSE_END*/ + +#define __CHARTABLE_TWO_FILE_LINE_SERIES_CHART_DECLARE__ +#include "ChartableTwoFileLineSeriesChart.h" +#undef __CHARTABLE_TWO_FILE_LINE_SERIES_CHART_DECLARE__ + +#include "CaretAssert.h" +#include "ChartTwoDataCartesian.h" +#include "ChartTwoLineSeriesHistory.h" +#include "CiftiMappableDataFile.h" +#include "CiftiScalarDataSeriesFile.h" +#include "EventChartTwoLoadLineSeriesData.h" +#include "EventManager.h" +#include "SceneClass.h" +#include "SceneClassAssistant.h" + +using namespace caret; + +/** + * \class caret::ChartableTwoFileLineSeriesChart + * \brief Implementation of base chart delegate for line series charts. + * \ingroup Files + */ + +/** + * Constructor. + * + * @param lineSeriesContentType + * Content type of the line series data. + * @param parentCaretMappableDataFile + * Parent caret mappable data file that this delegate supports. + */ +ChartableTwoFileLineSeriesChart::ChartableTwoFileLineSeriesChart(const ChartTwoLineSeriesContentTypeEnum::Enum lineSeriesContentType, + CaretMappableDataFile* parentCaretMappableDataFile) +: ChartableTwoFileBaseChart(ChartTwoDataTypeEnum::CHART_DATA_TYPE_LINE_SERIES, + parentCaretMappableDataFile), +m_lineSeriesContentType(lineSeriesContentType) +{ + m_sceneAssistant = std::unique_ptr(new SceneClassAssistant()); + + CaretUnitsTypeEnum::Enum xAxisUnits = CaretUnitsTypeEnum::NONE; + int32_t xAxisNumberOfElements = 0; + + CaretMappableDataFile* cmdf = getCaretMappableDataFile(); + CaretAssert(cmdf); + + switch (lineSeriesContentType) { + case ChartTwoLineSeriesContentTypeEnum::LINE_SERIES_CONTENT_UNSUPPORTED: + break; + case ChartTwoLineSeriesContentTypeEnum::LINE_SERIES_CONTENT_BRAINORDINATE_DATA: + if (cmdf->getNumberOfMaps() > 1) { + const NiftiTimeUnitsEnum::Enum mapUnits = cmdf->getMapIntervalUnits(); + xAxisUnits = CaretUnitsTypeEnum::NONE; + switch (mapUnits) { + case NiftiTimeUnitsEnum::NIFTI_UNITS_HZ: + xAxisUnits = CaretUnitsTypeEnum::HERTZ; + break; + case NiftiTimeUnitsEnum::NIFTI_UNITS_MSEC: + xAxisUnits = CaretUnitsTypeEnum::SECONDS; + break; + case NiftiTimeUnitsEnum::NIFTI_UNITS_PPM: + xAxisUnits = CaretUnitsTypeEnum::PARTS_PER_MILLION; + break; + case NiftiTimeUnitsEnum::NIFTI_UNITS_SEC: + xAxisUnits = CaretUnitsTypeEnum::SECONDS; + break; + case NiftiTimeUnitsEnum::NIFTI_UNITS_USEC: + xAxisUnits = CaretUnitsTypeEnum::SECONDS; + break; + case NiftiTimeUnitsEnum::NIFTI_UNITS_UNKNOWN: + break; + } + xAxisNumberOfElements = cmdf->getNumberOfMaps(); + } + break; + case ChartTwoLineSeriesContentTypeEnum::LINE_SERIES_CONTENT_ROW_SCALAR_DATA: + { + const CiftiMappableDataFile* ciftiMapFile = getCiftiMappableDataFile(); + CaretAssert(ciftiMapFile); + std::vector dims; + ciftiMapFile->getMapDimensions(dims); + CaretAssertVectorIndex(dims, 1); + const int32_t numCols = dims[0]; + const int32_t numRows = dims[1]; + + if ((numRows > 0) + && (numCols > 1)) { + const NiftiTimeUnitsEnum::Enum mapUnits = ciftiMapFile->getMapIntervalUnits(); + xAxisUnits = CaretUnitsTypeEnum::NONE; + switch (mapUnits) { + case NiftiTimeUnitsEnum::NIFTI_UNITS_HZ: + xAxisUnits = CaretUnitsTypeEnum::HERTZ; + break; + case NiftiTimeUnitsEnum::NIFTI_UNITS_MSEC: + xAxisUnits = CaretUnitsTypeEnum::SECONDS; + break; + case NiftiTimeUnitsEnum::NIFTI_UNITS_PPM: + xAxisUnits = CaretUnitsTypeEnum::PARTS_PER_MILLION; + break; + case NiftiTimeUnitsEnum::NIFTI_UNITS_SEC: + xAxisUnits = CaretUnitsTypeEnum::SECONDS; + break; + case NiftiTimeUnitsEnum::NIFTI_UNITS_USEC: + xAxisUnits = CaretUnitsTypeEnum::SECONDS; + break; + case NiftiTimeUnitsEnum::NIFTI_UNITS_UNKNOWN: + break; + } + xAxisNumberOfElements = numCols; + } + } break; + } + + m_lineChartHistory = std::unique_ptr(new ChartTwoLineSeriesHistory(GraphicsPrimitive::PrimitiveType::POLYGONAL_LINE_STRIP_BEVEL_JOIN)); + m_sceneAssistant->add("m_lineChartHistory", + "ChartTwoLineSeriesHistory", + m_lineChartHistory.get()); + + /* + * Must have two or more elements + */ + if (xAxisNumberOfElements <= 1) { + m_lineSeriesContentType = ChartTwoLineSeriesContentTypeEnum::LINE_SERIES_CONTENT_UNSUPPORTED; + } + + updateChartTwoCompoundDataTypeAfterFileChanges(ChartTwoCompoundDataType::newInstanceForLineSeries(xAxisUnits, + xAxisNumberOfElements)); + + if (m_lineSeriesContentType != ChartTwoLineSeriesContentTypeEnum::LINE_SERIES_CONTENT_UNSUPPORTED) { + EventManager::get()->addEventListener(this, + EventTypeEnum::EVENT_CHART_TWO_LOAD_LINE_SERIES_DATA); + } +} + +/** + * Destructor. + */ +ChartableTwoFileLineSeriesChart::~ChartableTwoFileLineSeriesChart() +{ + EventManager::get()->removeEventFromListener(this, + EventTypeEnum::EVENT_CHART_TWO_LOAD_LINE_SERIES_DATA); +} + +/** + * Receive an event. + * + * @param event + * An event for which this instance is listening. + */ +void +ChartableTwoFileLineSeriesChart::receiveEvent(Event* event) +{ + if (event->getEventType() == EventTypeEnum::EVENT_CHART_TWO_LOAD_LINE_SERIES_DATA) { + const EventChartTwoLoadLineSeriesData* lineSeriesDataEvent = dynamic_cast(event); + CaretAssert(lineSeriesDataEvent); + + if (m_lineSeriesContentType != ChartTwoLineSeriesContentTypeEnum::LINE_SERIES_CONTENT_UNSUPPORTED) { + const std::vector tabIndicesForLoading = getTabIndicesForLoadingData(lineSeriesDataEvent->getValidTabIndices()); + if ( ! tabIndicesForLoading.empty()) { + + loadLineCharts(lineSeriesDataEvent); + } + } + + event->setEventProcessed(); + } + else { + ChartableTwoFileBaseChart::receiveEvent(event); + } +} + +/** + * Load line-series charts. + * + * @param lineSeriesDataEvent + * Event indicating data for loading. + */ +void +ChartableTwoFileLineSeriesChart::loadLineCharts(const EventChartTwoLoadLineSeriesData* lineSeriesDataEvent) +{ + const MapFileDataSelector mapFileDataSelector = lineSeriesDataEvent->getMapFileDataSelector(); + + int32_t scalarRowIndex = -1; + bool loadDataFlag = false; + switch (m_lineSeriesContentType) { + case ChartTwoLineSeriesContentTypeEnum::LINE_SERIES_CONTENT_UNSUPPORTED: + return; + break; + case ChartTwoLineSeriesContentTypeEnum::LINE_SERIES_CONTENT_BRAINORDINATE_DATA: + switch (mapFileDataSelector.getDataSelectionType()) { + case MapFileDataSelector::DataSelectionType::INVALID: + break; + case MapFileDataSelector::DataSelectionType::COLUMN_DATA: + break; + case MapFileDataSelector::DataSelectionType::ROW_DATA: + break; + case MapFileDataSelector::DataSelectionType::SURFACE_VERTEX: + loadDataFlag = true; + break; + case MapFileDataSelector::DataSelectionType::SURFACE_VERTICES_AVERAGE: + loadDataFlag = true; + break; + case MapFileDataSelector::DataSelectionType::VOLUME_XYZ: + loadDataFlag = true; + break; + } + break; + case ChartTwoLineSeriesContentTypeEnum::LINE_SERIES_CONTENT_ROW_SCALAR_DATA: + switch (mapFileDataSelector.getDataSelectionType()) { + case MapFileDataSelector::DataSelectionType::INVALID: + break; + case MapFileDataSelector::DataSelectionType::COLUMN_DATA: + { + CaretMappableDataFile* mapFile = NULL; + AString mapFileName; + int32_t columnIndex = -1; + mapFileDataSelector.getColumnIndex(mapFile, mapFileName, columnIndex); + if (mapFile == getCaretMappableDataFile()) { + loadDataFlag = true; + } + } + break; + case MapFileDataSelector::DataSelectionType::ROW_DATA: + { + CaretMappableDataFile* mapFile = NULL; + AString mapFileName; + int32_t rowIndex = -1; + mapFileDataSelector.getRowIndex(mapFile, mapFileName, rowIndex); + if (mapFile == getCaretMappableDataFile()) { + loadDataFlag = true; + } + scalarRowIndex = rowIndex; + } + break; + case MapFileDataSelector::DataSelectionType::SURFACE_VERTEX: + break; + case MapFileDataSelector::DataSelectionType::SURFACE_VERTICES_AVERAGE: + break; + case MapFileDataSelector::DataSelectionType::VOLUME_XYZ: + break; + } + break; + } + + if (loadDataFlag) { + std::vector data; + getCaretMappableDataFile()->getDataForSelector(mapFileDataSelector, + data); + if ( ! data.empty()) { + const CaretUnitsTypeEnum::Enum xUnits = getChartTwoCompoundDataType().getLineChartUnitsAxisX(); + CaretAssert(getChartTwoCompoundDataType().getLineChartNumberOfElementsAxisX() == static_cast(data.size())); + ChartTwoDataCartesian* cartesianData = new ChartTwoDataCartesian(ChartTwoDataTypeEnum::CHART_DATA_TYPE_LINE_SERIES, + xUnits, + CaretUnitsTypeEnum::NONE, + m_lineChartHistory->getDefaultGraphicsPrimitiveType()); + cartesianData->setMapFileDataSelector(mapFileDataSelector); + + float x = 0.0f; + float xStep = 0.0f; + getCaretMappableDataFile()->getMapIntervalStartAndStep(x, xStep); + + /* + * Note: Start at index one since line segments are used + */ + const int32_t numData = static_cast(data.size()); + for (int32_t i = 1; i < numData; i++) { + CaretAssertVectorIndex(data, i - 1); + cartesianData->addPoint(x, data[i - 1]); + x += xStep; +// CaretAssertVectorIndex(data, i); +// cartesianData->addPoint(x, data[i]); + } + + m_lineChartHistory->addHistoryItem(cartesianData); + + if (scalarRowIndex >= 0) { + CaretMappableDataFile* mapFile = getCaretMappableDataFile(); + CaretAssert(mapFile); + CiftiScalarDataSeriesFile* scalarFile = dynamic_cast(mapFile); + if (scalarFile != NULL) { + for (auto tabIndex : lineSeriesDataEvent->getValidTabIndices()) { + scalarFile->setSelectedMapIndex(tabIndex, scalarRowIndex); + } + } + } + } + } +} + +/** + * Load data for the given row or column. + * + * @param tabIndex + * Index of tab. + * @param rowOrColumnIndex + * Index of row/column for loading. + */ +void +ChartableTwoFileLineSeriesChart::loadDataForRowOrColumn(const int32_t tabIndex, + const int32_t rowOrColumnIndex) +{ + std::vector tabIndices; + tabIndices.push_back(tabIndex); + + MapFileDataSelector mapFileSelector; + mapFileSelector.setRowIndex(getCaretMappableDataFile(), + "", + rowOrColumnIndex); + + EventChartTwoLoadLineSeriesData lineSeriesDataEvent(tabIndices, + mapFileSelector); + loadLineCharts(&lineSeriesDataEvent); +} + +/** + * Find tab indices for which user has loading of data enabled in the given valid tab indices. + * + * @param validTabIndices + * All tabs that are valid. + * @return + * Tab indices for which data should be loaded. + */ +std::vector +ChartableTwoFileLineSeriesChart::getTabIndicesForLoadingData(const std::vector& validTabIndices) const +{ + std::vector tabIndicesOut; + + if ( ! validTabIndices.empty()) { + if (m_lineChartHistory->isLoadingEnabled()) { + tabIndicesOut = validTabIndices; + } + } + + return tabIndicesOut; +} + + +/** + * @return Content type of the line series data. + */ +ChartTwoLineSeriesContentTypeEnum::Enum +ChartableTwoFileLineSeriesChart::getLineSeriesContentType() const +{ + return m_lineSeriesContentType; +} + +/** + * @return History of line charts. + */ +ChartTwoLineSeriesHistory* +ChartableTwoFileLineSeriesChart::getHistory() +{ + return m_lineChartHistory.get(); +} + +/** + * @return History of line charts (const method) + */ +const ChartTwoLineSeriesHistory* +ChartableTwoFileLineSeriesChart::getHistory() const +{ + return m_lineChartHistory.get(); +} + + +/** + * @return Is this charting valid ? + */ +bool +ChartableTwoFileLineSeriesChart::isValid() const +{ + return (m_lineSeriesContentType != ChartTwoLineSeriesContentTypeEnum::LINE_SERIES_CONTENT_UNSUPPORTED); +} + +/** + * @retrurn Is this charting empty (no data at this time) + */ +bool +ChartableTwoFileLineSeriesChart::isEmpty() const +{ + if ( ! isValid()) { + return true; + } + + return (m_lineChartHistory->getHistoryCount() <= 0); +} + +/** + * Save subclass data to the scene. + * + * @param sceneAttributes + * Attributes for the scene. Scenes may be of different types + * (full, generic, etc) and the attributes should be checked when + * restoring the scene. + * + * @param sceneClass + * sceneClass to which data members should be added. Will always + * be valid (non-NULL). + */ +void +ChartableTwoFileLineSeriesChart::saveSubClassDataToScene(const SceneAttributes* sceneAttributes, + SceneClass* sceneClass) +{ + m_sceneAssistant->saveMembers(sceneAttributes, + sceneClass); +} + +/** + * Restore file data from the scene. + * + * @param sceneAttributes + * Attributes for the scene. Scenes may be of different types + * (full, generic, etc) and the attributes should be checked when + * restoring the scene. + * + * @param sceneClass + * sceneClass for the instance of a class that implements + * this interface. Will NEVER be NULL. + */ +void +ChartableTwoFileLineSeriesChart::restoreSubClassDataFromScene(const SceneAttributes* sceneAttributes, + const SceneClass* sceneClass) +{ + m_sceneAssistant->restoreMembers(sceneAttributes, + sceneClass); +} + diff --git a/src/Files/ChartableTwoFileLineSeriesChart.h b/src/Files/ChartableTwoFileLineSeriesChart.h new file mode 100644 index 0000000000000000000000000000000000000000..aaf86e24e2d8b2102ac13dfb4d914701f864cfb4 --- /dev/null +++ b/src/Files/ChartableTwoFileLineSeriesChart.h @@ -0,0 +1,97 @@ +#ifndef __CHARTABLE_TWO_FILE_LINE_SERIES_CHART_H__ +#define __CHARTABLE_TWO_FILE_LINE_SERIES_CHART_H__ + +/*LICENSE_START*/ +/* + * Copyright (C) 2017 Washington University School of Medicine + * + * 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 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ +/*LICENSE_END*/ + +#include + +#include "BrainConstants.h" +#include "ChartableTwoFileBaseChart.h" +#include "ChartTwoLineSeriesContentTypeEnum.h" + + +namespace caret { + + class ChartTwoLineSeriesHistory; + class EventChartTwoLoadLineSeriesData; + + class ChartableTwoFileLineSeriesChart : public ChartableTwoFileBaseChart { + + public: + ChartableTwoFileLineSeriesChart(const ChartTwoLineSeriesContentTypeEnum::Enum lineSeriesContentType, + CaretMappableDataFile* parentCaretMappableDataFile); + + virtual ~ChartableTwoFileLineSeriesChart(); + + ChartTwoLineSeriesContentTypeEnum::Enum getLineSeriesContentType() const; + + ChartTwoLineSeriesHistory* getHistory(); + + const ChartTwoLineSeriesHistory* getHistory() const; + + virtual bool isValid() const override; + + virtual bool isEmpty() const override; + + virtual void receiveEvent(Event* event) override; + + void loadDataForRowOrColumn(const int32_t tabIndex, + const int32_t rowOrColumnIndex); + + // ADD_NEW_METHODS_HERE + + + + + + + protected: + virtual void saveSubClassDataToScene(const SceneAttributes* sceneAttributes, + SceneClass* sceneClass) override; + + virtual void restoreSubClassDataFromScene(const SceneAttributes* sceneAttributes, + const SceneClass* sceneClass) override; + + private: + void loadLineCharts(const EventChartTwoLoadLineSeriesData* lineSeriesDataEvent); + + ChartableTwoFileLineSeriesChart(const ChartableTwoFileLineSeriesChart&); + + ChartableTwoFileLineSeriesChart& operator=(const ChartableTwoFileLineSeriesChart&); + + std::vector getTabIndicesForLoadingData(const std::vector& validTabIndices) const; + + std::unique_ptr m_sceneAssistant; + + ChartTwoLineSeriesContentTypeEnum::Enum m_lineSeriesContentType; + + std::unique_ptr m_lineChartHistory; + + // ADD_NEW_MEMBERS_HERE + + }; + +#ifdef __CHARTABLE_TWO_FILE_LINE_SERIES_CHART_DECLARE__ + // +#endif // __CHARTABLE_TWO_FILE_LINE_SERIES_CHART_DECLARE__ + +} // namespace +#endif //__CHARTABLE_TWO_FILE_LINE_SERIES_CHART_H__ diff --git a/src/Files/ChartableTwoFileMatrixChart.cxx b/src/Files/ChartableTwoFileMatrixChart.cxx new file mode 100644 index 0000000000000000000000000000000000000000..cfdc0bb97a0058f994a6114cf7f775f784ff5e19 --- /dev/null +++ b/src/Files/ChartableTwoFileMatrixChart.cxx @@ -0,0 +1,877 @@ + +/*LICENSE_START*/ +/* + * Copyright (C) 2017 Washington University School of Medicine + * + * 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 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ +/*LICENSE_END*/ + +#define __CHARTABLE_TWO_FILE_MATRIX_CHART_DECLARE__ +#include "ChartableTwoFileMatrixChart.h" +#undef __CHARTABLE_TWO_FILE_MATRIX_CHART_DECLARE__ + +#include "CaretAssert.h" +#include "CiftiConnectivityMatrixParcelFile.h" +#include "CiftiParcelLabelFile.h" +#include "CiftiParcelReordering.h" +#include "CiftiParcelScalarFile.h" +#include "CiftiParcelSeriesFile.h" +#include "CiftiXML.h" +#include "CiftiScalarDataSeriesFile.h" +#include "ConnectivityDataLoaded.h" +#include "EventCaretMappableDataFileMapsViewedInOverlays.h" +#include "EventManager.h" +#include "SceneClass.h" +#include "SceneClassAssistant.h" + +using namespace caret; + +/** + * \class caret::ChartableTwoFileMatrixChart + * \brief Implementation of base chart delegate for matrix charts. + * \ingroup Files + */ + +/** + * Constructor. + * + * @param matrixContentType + * Content type of the matrix. + * @param parentCaretMappableDataFile + * Parent caret mappable data file that this delegate supports. + * @param validRowColumnSelectionDimensions + * Valid row/column selection dimensions for loading and selection. + */ +ChartableTwoFileMatrixChart::ChartableTwoFileMatrixChart(const ChartTwoMatrixContentTypeEnum::Enum matrixContentType, + CaretMappableDataFile* parentCaretMappableDataFile, + std::vector& validRowColumnSelectionDimensions) +: ChartableTwoFileBaseChart(ChartTwoDataTypeEnum::CHART_DATA_TYPE_MATRIX, + parentCaretMappableDataFile), +m_matrixContentType(matrixContentType), +m_validRowColumnSelectionDimensions(validRowColumnSelectionDimensions) +{ + m_sceneAssistant = new SceneClassAssistant(); + + m_numberOfRows = 0; + m_numberOfColumns = 0; + + for (int32_t i = 0; i < BrainConstants::MAXIMUM_NUMBER_OF_BROWSER_TABS; i++) { + m_parcelLabelFileSelectedColumn[i] = 0; + m_parcelScalarFileSelectedColumn[i] = 0; + m_parcelSeriesFileSelectedColumn[i] = 0; + } + m_sceneAssistant->addTabIndexedIntegerArray("m_parcelLabelFileSelectedColumn", + m_parcelLabelFileSelectedColumn); + m_sceneAssistant->addTabIndexedIntegerArray("m_parcelScalarFileSelectedColumn", + m_parcelScalarFileSelectedColumn); + m_sceneAssistant->addTabIndexedIntegerArray("m_parcelSeriesFileSelectedColumn", + m_parcelSeriesFileSelectedColumn); + + if (m_matrixContentType != ChartTwoMatrixContentTypeEnum::MATRIX_CONTENT_UNSUPPORTED) { + CiftiMappableDataFile* ciftiMapFile = getCiftiMappableDataFile(); + CaretAssert(ciftiMapFile); + ciftiMapFile->helpMapFileGetMatrixDimensions(m_numberOfRows, + m_numberOfColumns); + + switch (ciftiMapFile->getDataFileType()) { + case DataFileTypeEnum::ANNOTATION: + break; + case DataFileTypeEnum::BORDER: + break; + case DataFileTypeEnum::CONNECTIVITY_DENSE: + break; + case DataFileTypeEnum::CONNECTIVITY_DENSE_DYNAMIC: + break; + case DataFileTypeEnum::CONNECTIVITY_DENSE_LABEL: + break; + case DataFileTypeEnum::CONNECTIVITY_DENSE_PARCEL: + break; + case DataFileTypeEnum::CONNECTIVITY_DENSE_SCALAR: + break; + case DataFileTypeEnum::CONNECTIVITY_DENSE_TIME_SERIES: + break; + case DataFileTypeEnum::CONNECTIVITY_FIBER_ORIENTATIONS_TEMPORARY: + break; + case DataFileTypeEnum::CONNECTIVITY_FIBER_TRAJECTORY_TEMPORARY: + break; + case DataFileTypeEnum::CONNECTIVITY_PARCEL: + m_matrixDataFileType = MatrixDataFileType::PARCEL; + break; + case DataFileTypeEnum::CONNECTIVITY_PARCEL_DENSE: + break; + case DataFileTypeEnum::CONNECTIVITY_PARCEL_LABEL: + m_matrixDataFileType = MatrixDataFileType::PARCEL_LABEL; + break; + case DataFileTypeEnum::CONNECTIVITY_PARCEL_SCALAR: + m_matrixDataFileType = MatrixDataFileType::PARCEL_SCALAR; + break; + case DataFileTypeEnum::CONNECTIVITY_PARCEL_SERIES: + m_matrixDataFileType = MatrixDataFileType::PARCEL_SERIES; + break; + case DataFileTypeEnum::CONNECTIVITY_SCALAR_DATA_SERIES: + m_matrixDataFileType = MatrixDataFileType::SCALAR_DATA_SERIES; + break; + case DataFileTypeEnum::FOCI: + break; + case DataFileTypeEnum::IMAGE: + break; + case DataFileTypeEnum::LABEL: + break; + case DataFileTypeEnum::METRIC: + break; + case DataFileTypeEnum::PALETTE: + break; + case DataFileTypeEnum::RGBA: + break; + case DataFileTypeEnum::SCENE: + break; + case DataFileTypeEnum::SPECIFICATION: + break; + case DataFileTypeEnum::SURFACE: + break; + case DataFileTypeEnum::UNKNOWN: + break; + case DataFileTypeEnum::VOLUME: + break; + } + + bool hasColumnParcelsFlag = false; + bool hasColumnMapNamesFlag = false; + bool hasRowParcelsFlag = false; + bool hasRowNumbersFlag = false; + switch (m_matrixDataFileType) { + case MatrixDataFileType::INVALID: + CaretAssert(0); + return; + break; + case MatrixDataFileType::PARCEL: + m_parcelFile = dynamic_cast(ciftiMapFile); + CaretAssert(m_parcelFile); + m_hasRowSelectionFlag = true; + hasRowParcelsFlag = true; + m_hasColumnSelectionFlag = true; + hasColumnParcelsFlag = true; + break; + case MatrixDataFileType::PARCEL_LABEL: + m_parcelLabelFile = dynamic_cast(ciftiMapFile); + CaretAssert(m_parcelLabelFile); + m_hasColumnSelectionFlag = true; + hasColumnMapNamesFlag = true; + break; + case MatrixDataFileType::PARCEL_SCALAR: + m_parcelScalarFile = dynamic_cast(ciftiMapFile); + CaretAssert(m_parcelScalarFile); + m_hasColumnSelectionFlag = true; + hasColumnMapNamesFlag = true; + break; + case MatrixDataFileType::PARCEL_SERIES: + m_parcelSeriesFile = dynamic_cast(ciftiMapFile); + CaretAssert(m_parcelSeriesFile); + m_hasColumnSelectionFlag = true; + hasColumnMapNamesFlag = true; + break; + case MatrixDataFileType::SCALAR_DATA_SERIES: + m_scalarDataSeriesFile = dynamic_cast(ciftiMapFile); + CaretAssert(m_scalarDataSeriesFile); + m_hasRowSelectionFlag = true; + break; + } + + if (m_hasRowSelectionFlag) { + if (hasRowParcelsFlag) { + const CiftiParcelsMap* rowParcelsMap = ciftiMapFile->getCiftiParcelsMapForDirection(CiftiXML::ALONG_COLUMN); + if (rowParcelsMap != NULL) { + const int32_t numRowParcels = rowParcelsMap->getLength(); + CaretAssert(numRowParcels == m_numberOfRows); + for (int32_t i = 0; i < numRowParcels; i++) { + m_rowNames.push_back(rowParcelsMap->getIndexName(i)); + m_rowNumberAndNames.push_back("Row " + AString::number(i + ROW_COLUMN_INDEX_BASE_OFFSET) + ": " + rowParcelsMap->getIndexName(i)); + } + } + else { + const CiftiParcelsMap* columnParcelsMap = ciftiMapFile->getCiftiParcelsMapForDirection(CiftiXML::ALONG_ROW); + if (columnParcelsMap != NULL) { + CaretAssertMessage(0, ("Trying to use row parcels but parcels are in columns. There are " + + AString::number(columnParcelsMap->getLength()) + + " parcels in the column for the file " + + getCaretMappableDataFile()->getFileNameNoPath())); + } + else { + CaretAssertMessage(0, "Trying to use row parcels but there are none for the file " + + getCaretMappableDataFile()->getFileNameNoPath()); + } + } + } + else if (hasRowNumbersFlag) { + for (int32_t i = 0; i < m_numberOfRows; i++) { + m_rowNames.push_back(AString::number(i)); + m_rowNumberAndNames.push_back("Row " + AString::number(i + ROW_COLUMN_INDEX_BASE_OFFSET) + ": " + AString::number(i + 1)); + } + } + } + + if (m_hasColumnSelectionFlag) { + if (hasColumnParcelsFlag) { + const CiftiParcelsMap* colParcelsMap = ciftiMapFile->getCiftiParcelsMapForDirection(CiftiXML::ALONG_ROW); + if (colParcelsMap != NULL) { + const int32_t numColParcels = colParcelsMap->getLength(); + CaretAssert(numColParcels == m_numberOfColumns); + for (int32_t i = 0; i < numColParcels; i++) { + m_columnNames.push_back(colParcelsMap->getIndexName(i)); + m_columnNumberAndNames.push_back("Column " + AString::number(i + ROW_COLUMN_INDEX_BASE_OFFSET) + ": " + colParcelsMap->getIndexName(i)); + } + } + else { + const CiftiParcelsMap* rowParcelsMap = ciftiMapFile->getCiftiParcelsMapForDirection(CiftiXML::ALONG_COLUMN); + if (rowParcelsMap != NULL) { + CaretAssertMessage(0, ("Trying to use column parcels but parcels are in rows. There are " + + AString::number(rowParcelsMap->getLength()) + + " parcels in the row for the file " + + getCaretMappableDataFile()->getFileNameNoPath())); + } + else { + CaretAssertMessage(0, "Trying to use column parcels but there are none for the file " + + getCaretMappableDataFile()->getFileNameNoPath()); + } + } + } + else if (hasColumnMapNamesFlag) { + const int32_t numMaps = ciftiMapFile->getNumberOfMaps(); + CaretAssert(m_numberOfColumns == numMaps); + for (int32_t i = 0; i < numMaps; i++) { + m_columnNames.push_back(ciftiMapFile->getMapName(i)); + m_columnNumberAndNames.push_back("Column " + AString::number(i + ROW_COLUMN_INDEX_BASE_OFFSET) + ": " + ciftiMapFile->getMapName(i)); + } + } + +// if (hasColumnMapSelectionFlag) { +// m_columnNames.clear(); +// for (int32_t i = 0; i < m_numberOfColumns; i++) { +// m_columnNames.push_back(ciftiMapFile->getMapName(i)); +// } +// if (m_parcelScalarFile != NULL) { +// for (int32_t i = 0; i < m_numberOfColumns; i++) { +// m_columnNames.push_back(m_parcelScalarFile->getMapName(i)); +// } +// } +// if (m_parcelSeriesFile != NULL) { +// for (int32_t i = 0; i < m_numberOfColumns; i++) { +// m_columnNames.push_back(m_parcelSeriesFile->getMapName(i)); +// } +// } +// } + } + } + + m_matrixTriangularViewingModeSupportedFlag = false; + if ((m_numberOfRows > 0) + && (m_numberOfColumns > 0)) { + const CiftiConnectivityMatrixParcelFile* matrixFile = dynamic_cast(parentCaretMappableDataFile); + if (matrixFile != NULL) { + m_matrixTriangularViewingModeSupportedFlag = matrixFile->hasSymetricRowColumnNames(); + } + } + else { + m_matrixContentType = ChartTwoMatrixContentTypeEnum::MATRIX_CONTENT_UNSUPPORTED; + } + + + updateChartTwoCompoundDataTypeAfterFileChanges(ChartTwoCompoundDataType::newInstanceForMatrix(m_numberOfRows, + m_numberOfColumns)); +} + +/** + * Destructor. + */ +ChartableTwoFileMatrixChart::~ChartableTwoFileMatrixChart() +{ + delete m_sceneAssistant; +} + +/** + * @return Content type of the matrix. + */ +ChartTwoMatrixContentTypeEnum::Enum +ChartableTwoFileMatrixChart::getMatrixContentType() const +{ + return m_matrixContentType; +} + +/** + * @return Is this charting valid ? + */ +bool +ChartableTwoFileMatrixChart::isValid() const +{ + return (m_matrixContentType != ChartTwoMatrixContentTypeEnum::MATRIX_CONTENT_UNSUPPORTED); +} + +/** + * @retrurn Is this charting empty (no data at this time) + */ +bool +ChartableTwoFileMatrixChart::isEmpty() const +{ + if ( ! isValid()) { + return true; + } + + int32_t numRows = 0; + int32_t numCols = 0; + getMatrixDimensions(numRows, + numCols); + if ((numRows > 0) + || (numCols > 0)) { + return false; + } + + return true; +} + +/** + * @return Is matrix triangular viewing modes supported? + */ +bool +ChartableTwoFileMatrixChart::isMatrixTriangularViewingModeSupported() const +{ + return m_matrixTriangularViewingModeSupportedFlag; +} + + +/** + * Get the matrix dimensions. + * + * @param numberOfRowsOut + * Number of rows in the matrix. + * @param numberOfColumnsOut + * Number of rows in the matrix. + */ +void +ChartableTwoFileMatrixChart::getMatrixDimensions(int32_t& numberOfRowsOut, + int32_t& numberOfColumnsOut) const +{ + numberOfRowsOut = 0; + numberOfColumnsOut = 0; + + if (m_matrixContentType == ChartTwoMatrixContentTypeEnum::MATRIX_CONTENT_UNSUPPORTED) { + return; + } + + const CiftiMappableDataFile* ciftiMapFile = getCiftiMappableDataFile(); + CaretAssert(ciftiMapFile); + ciftiMapFile->helpMapFileGetMatrixDimensions(numberOfRowsOut, + numberOfColumnsOut); +} + +/** + * Get the matrix RGBA coloring for this matrix data creator. + * + * @param numberOfRowsOut + * Number of rows in the coloring matrix. + * @param numberOfColumnsOut + * Number of rows in the coloring matrix. + * @param rgbaOut + * RGBA coloring output with number of elements + * (numberOfRowsOut * numberOfColumnsOut * 4). + * @return + * True if data output data is valid, else false. + */ +bool +ChartableTwoFileMatrixChart::getMatrixDataRGBA(int32_t& numberOfRowsOut, + int32_t& numberOfColumnsOut, + std::vector& rgbaOut) const +{ + + const CiftiMappableDataFile* ciftiMapFile = getCiftiMappableDataFile(); + CaretAssert(ciftiMapFile); + + return ciftiMapFile->getMatrixForChartingRGBA(numberOfRowsOut, + numberOfColumnsOut, + rgbaOut); +} + +/** + * @return The graphics primitive containing the matrix representation of the file. + * All cells are of dimension 1.0 x 1.0 + * + * @param matrixViewMode + * The matrix visualization mode (upper/lower). + * @param gridMode + * The grid mode (filled or outline) + */ +GraphicsPrimitiveV3fC4f* +ChartableTwoFileMatrixChart::getMatrixChartingGraphicsPrimitive(const ChartTwoMatrixTriangularViewingModeEnum::Enum matrixViewMode, + const CiftiMappableDataFile::MatrixGridMode gridMode) const +{ + const CiftiMappableDataFile* ciftiMapFile = getCiftiMappableDataFile(); + CaretAssert(ciftiMapFile); + + return ciftiMapFile->getMatrixChartingGraphicsPrimitive(matrixViewMode, + gridMode); +} + +/** + * @return Identifier for the matrix primitives alternative color used for the grid coloring + */ +int32_t +ChartableTwoFileMatrixChart::getMatrixChartGraphicsPrimitiveGridColorIdentifier() const +{ + const CiftiMappableDataFile* ciftiMapFile = getCiftiMappableDataFile(); + CaretAssert(ciftiMapFile); + + return ciftiMapFile->getMatrixChartGraphicsPrimitiveGridColorIdentifier(); +} + +/** + * @return The selected row/column dimension. + */ +ChartTwoMatrixLoadingDimensionEnum::Enum +ChartableTwoFileMatrixChart::getSelectedRowColumnDimension() const +{ + ChartTwoMatrixLoadingDimensionEnum::Enum loadDimension = ChartTwoMatrixLoadingDimensionEnum::CHART_MATRIX_LOADING_BY_ROW; + + switch (m_matrixDataFileType) { + case MatrixDataFileType::INVALID: + CaretAssert(0); + break; + case MatrixDataFileType::PARCEL: + CaretAssert(m_parcelFile); + switch (m_parcelFile->getMatrixLoadingDimension()) { + case ChartMatrixLoadingDimensionEnum::CHART_MATRIX_LOADING_BY_ROW: + loadDimension = ChartTwoMatrixLoadingDimensionEnum::CHART_MATRIX_LOADING_BY_ROW; + break; + case ChartMatrixLoadingDimensionEnum::CHART_MATRIX_LOADING_BY_COLUMN: + loadDimension = ChartTwoMatrixLoadingDimensionEnum::CHART_MATRIX_LOADING_BY_COLUMN; + break; + } + break; + case MatrixDataFileType::PARCEL_LABEL: + CaretAssert(m_parcelLabelFile); + loadDimension = ChartTwoMatrixLoadingDimensionEnum::CHART_MATRIX_LOADING_BY_COLUMN; + break; + case MatrixDataFileType::PARCEL_SCALAR: + CaretAssert(m_parcelScalarFile); + loadDimension = ChartTwoMatrixLoadingDimensionEnum::CHART_MATRIX_LOADING_BY_COLUMN; + break; + case MatrixDataFileType::PARCEL_SERIES: + CaretAssert(m_parcelSeriesFile); + loadDimension = ChartTwoMatrixLoadingDimensionEnum::CHART_MATRIX_LOADING_BY_COLUMN; + break; + case MatrixDataFileType::SCALAR_DATA_SERIES: + CaretAssert(m_scalarDataSeriesFile); + break; + } + + return loadDimension; +} + +/** + * Set the selected row/column dimension. + * + * @param rowColumnDimension + * New value for row/column dimension + */ +void +ChartableTwoFileMatrixChart::setSelectedRowColumnDimension(const ChartTwoMatrixLoadingDimensionEnum::Enum rowColumnDimension) +{ + switch (m_matrixDataFileType) { + case MatrixDataFileType::INVALID: + CaretAssert(0); + break; + case MatrixDataFileType::PARCEL: + { + CaretAssert(m_parcelFile); + ChartMatrixLoadingDimensionEnum::Enum oldLoadDim = ChartMatrixLoadingDimensionEnum::CHART_MATRIX_LOADING_BY_ROW; + switch (rowColumnDimension) { + case ChartTwoMatrixLoadingDimensionEnum::CHART_MATRIX_LOADING_BY_ROW: + oldLoadDim = ChartMatrixLoadingDimensionEnum::CHART_MATRIX_LOADING_BY_ROW; + break; + case ChartTwoMatrixLoadingDimensionEnum::CHART_MATRIX_LOADING_BY_COLUMN: + oldLoadDim = ChartMatrixLoadingDimensionEnum::CHART_MATRIX_LOADING_BY_COLUMN; + break; + } + m_parcelFile->setMatrixLoadingDimension(oldLoadDim); + } + break; + case MatrixDataFileType::PARCEL_LABEL: + CaretAssert(m_parcelLabelFile); + break; + case MatrixDataFileType::PARCEL_SCALAR: + CaretAssert(m_parcelScalarFile); + break; + case MatrixDataFileType::PARCEL_SERIES: + CaretAssert(m_parcelSeriesFile); + break; + case MatrixDataFileType::SCALAR_DATA_SERIES: + CaretAssert(m_scalarDataSeriesFile); + break; + } +} + +/** + * Get the valid row/column dimensions. + * + * @param validRowColumnSelectionDimensionsOut + * Output with valid row/column dimensions for selection. + */ +void +ChartableTwoFileMatrixChart::getValidRowColumnSelectionDimensions(std::vector& validRowColumnSelectionDimensionsOut) const +{ + validRowColumnSelectionDimensionsOut = m_validRowColumnSelectionDimensions; +} + +/** + * @param tabIndex + * Index of the tab. + * @param selectedRowIndicesOut + * Indices if indices are for rows or columns. + * @param selectedRowIndicesOut + * Output with row indices selected. + * @param selectedColumnIndicesOut + * Output with column indices selected. + */ +void +ChartableTwoFileMatrixChart::getSelectedRowColumnIndices(const int32_t tabIndex, + ChartTwoMatrixLoadingDimensionEnum::Enum& rowColumnDimensionOut, + std::vector& selectedRowIndicesOut, + std::vector& selectedColumnIndicesOut) const +{ + rowColumnDimensionOut = getSelectedRowColumnDimension(); + selectedRowIndicesOut.clear(); + selectedColumnIndicesOut.clear(); + + std::set rowIndicesSet; + std::set columnIndicesSet; + + switch (m_matrixDataFileType) { + case MatrixDataFileType::INVALID: + CaretAssert(0); + break; + case MatrixDataFileType::PARCEL: + { + CaretAssert(m_parcelFile); + const ConnectivityDataLoaded* connDataLoaded = m_parcelFile->getConnectivityDataLoaded(); + if (connDataLoaded != NULL) { + int64_t loadedRowIndex = -1; + int64_t loadedColumnIndex = -1; + connDataLoaded->getRowColumnLoading(loadedRowIndex, + loadedColumnIndex); + switch (rowColumnDimensionOut) { + case ChartTwoMatrixLoadingDimensionEnum::CHART_MATRIX_LOADING_BY_COLUMN: + if (loadedColumnIndex >= 0) { + columnIndicesSet.insert(loadedColumnIndex); + } + break; + case ChartTwoMatrixLoadingDimensionEnum::CHART_MATRIX_LOADING_BY_ROW: + if (loadedRowIndex >= 0) { + rowIndicesSet.insert(loadedRowIndex); + } + break; + } + } + } + break; + case MatrixDataFileType::PARCEL_LABEL: + { + CaretAssertArrayIndex(m_parcelLabelFileSelectedColumn, BrainConstants::MAXIMUM_NUMBER_OF_BROWSER_TABS, tabIndex); + columnIndicesSet.insert(m_parcelLabelFileSelectedColumn[tabIndex]); +// CaretAssert(m_parcelLabelFile); +// EventCaretMappableDataFileMapsViewedInOverlays mapOverlayEvent(m_parcelLabelFile); +// EventManager::get()->sendEvent(mapOverlayEvent.getPointer()); +// for (auto indx : mapOverlayEvent.getSelectedMapIndices()) { +// columnIndicesSet.insert(indx); +// } + } + break; + case MatrixDataFileType::PARCEL_SCALAR: + CaretAssertArrayIndex(m_parcelScalarFileSelectedColumn, BrainConstants::MAXIMUM_NUMBER_OF_BROWSER_TABS, tabIndex); + columnIndicesSet.insert(m_parcelScalarFileSelectedColumn[tabIndex]); + break; + case MatrixDataFileType::PARCEL_SERIES: + CaretAssertArrayIndex(m_parcelSeriesFileSelectedColumn, BrainConstants::MAXIMUM_NUMBER_OF_BROWSER_TABS, tabIndex); + columnIndicesSet.insert(m_parcelSeriesFileSelectedColumn[tabIndex]); + break; + case MatrixDataFileType::SCALAR_DATA_SERIES: + { + CaretAssert(m_scalarDataSeriesFile); + const int32_t scalarDataSeriesMapIndex = m_scalarDataSeriesFile->getSelectedMapIndex(tabIndex); + if (scalarDataSeriesMapIndex >= 0) { + rowIndicesSet.insert(scalarDataSeriesMapIndex); + } + } + break; + } + + selectedRowIndicesOut.insert(selectedRowIndicesOut.end(), + rowIndicesSet.begin(), + rowIndicesSet.end()); + selectedColumnIndicesOut.insert(selectedColumnIndicesOut.end(), + columnIndicesSet.begin(), + columnIndicesSet.end()); +} + +/** + * Set the row or column index using the currently selected row/column dimension. + * + * @param tabIndex + * Index of tab. + * @param rowColumnIndex + * New row/column index. + */ +void +ChartableTwoFileMatrixChart::setSelectedRowColumnIndex(const int32_t tabIndex, + const int32_t rowColumnIndex) +{ + /* + * Ignore an invalid row/column index. It may occur when the chart overlay + * is changed. + */ + if (rowColumnIndex < 0) { + return; + } + + switch (m_matrixDataFileType) { + case MatrixDataFileType::INVALID: + CaretAssert(0); + break; + case MatrixDataFileType::PARCEL: + { + CaretAssert(m_parcelFile); + int32_t numRows = -1; + int32_t numCols = -1; + getMatrixDimensions(numRows, numCols); + switch (getSelectedRowColumnDimension()) { + case ChartTwoMatrixLoadingDimensionEnum::CHART_MATRIX_LOADING_BY_COLUMN: + if ((rowColumnIndex >= 0) + && (rowColumnIndex < numCols)) { + m_parcelFile->loadDataForColumnIndex(rowColumnIndex); + m_parcelFile->invalidateColoringInAllMaps(); + } + break; + case ChartTwoMatrixLoadingDimensionEnum::CHART_MATRIX_LOADING_BY_ROW: + if ((rowColumnIndex >= 0) + && (rowColumnIndex < numRows)) { + m_parcelFile->loadDataForRowIndex(rowColumnIndex); + m_parcelFile->invalidateColoringInAllMaps(); + } + break; + } + } + break; + case MatrixDataFileType::PARCEL_LABEL: + { + CaretAssert(m_parcelLabelFile); + CaretAssertArrayIndex(m_parcelLabelFileSelectedColumn, BrainConstants::MAXIMUM_NUMBER_OF_BROWSER_TABS, tabIndex); + m_parcelLabelFileSelectedColumn[tabIndex] = rowColumnIndex; + // EventCaretMappableDataFileMapsViewedInOverlays mapOverlayEvent(m_parcelLabelFile); + // EventManager::get()->sendEvent(mapOverlayEvent.getPointer()); + // for (auto indx : mapOverlayEvent.getSelectedMapIndices()) { + // columnIndicesSet.insert(indx); + // } + } + break; + case MatrixDataFileType::PARCEL_SCALAR: + { + CaretAssert(m_parcelScalarFile); + CaretAssertArrayIndex(m_parcelScalarFileSelectedColumn, BrainConstants::MAXIMUM_NUMBER_OF_BROWSER_TABS, tabIndex); + m_parcelScalarFileSelectedColumn[tabIndex] = rowColumnIndex; + } + break; + case MatrixDataFileType::PARCEL_SERIES: + CaretAssert(m_parcelSeriesFile); + CaretAssertArrayIndex(m_parcelSeriesFileSelectedColumn, BrainConstants::MAXIMUM_NUMBER_OF_BROWSER_TABS, tabIndex); + m_parcelSeriesFileSelectedColumn[tabIndex] = rowColumnIndex; + break; + case MatrixDataFileType::SCALAR_DATA_SERIES: + CaretAssert(m_scalarDataSeriesFile); + m_scalarDataSeriesFile->setSelectedMapIndex(tabIndex, + rowColumnIndex); + break; + } +} + +/** + * Load a row or column if none are loaded. + * + */ +void +ChartableTwoFileMatrixChart::loadDefaultRowOrColumn() +{ + if (getChartTwoDataType() != ChartTwoDataTypeEnum::CHART_DATA_TYPE_MATRIX) { + return; + } + if (m_matrixDataFileType == MatrixDataFileType::INVALID) { + return; + } + + const int32_t tabIndex = BrainConstants::MAXIMUM_NUMBER_OF_BROWSER_TABS - 1; + ChartTwoMatrixLoadingDimensionEnum::Enum rowColumnDimension = getSelectedRowColumnDimension(); + std::vector selectedRowIndices; + std::vector selectedColumnIndices; + + getSelectedRowColumnIndices(tabIndex, + rowColumnDimension, + selectedRowIndices, + selectedColumnIndices); + + if (selectedRowIndices.empty() + && selectedRowIndices.empty()) { + setSelectedRowColumnIndex(tabIndex, 0); + } +} + +/** + * @return True if the file supports row selection. + */ +bool +ChartableTwoFileMatrixChart::hasRowSelection() const +{ + return m_hasRowSelectionFlag; +} + +/** + * @return True if the file supports column selection. + */ +bool +ChartableTwoFileMatrixChart::hasColumnSelection() const +{ + return m_hasColumnSelectionFlag; +} + +/** + * @return Name of row at the given index. + * + * @param rowIndex + * Index of the row. + */ +AString +ChartableTwoFileMatrixChart::getRowName(const int32_t rowIndex) const +{ + if ((rowIndex >= 0) + && (rowIndex < m_numberOfRows)) { + if (rowIndex < static_cast(m_rowNames.size())) { + CaretAssertVectorIndex(m_rowNames, rowIndex); + return m_rowNames[rowIndex]; + } + return ("Row: " + AString::number(rowIndex + ROW_COLUMN_INDEX_BASE_OFFSET)); + } + + return ""; +} + +/** + * @return Name of column at the given index. + * + * @param columnIndex + * Index of the row. + */ +AString +ChartableTwoFileMatrixChart::getColumnName(const int32_t columnIndex) const +{ + if ((columnIndex >= 0) + && (columnIndex < m_numberOfColumns)) { + if (columnIndex < static_cast(m_columnNames.size())) { + CaretAssertVectorIndex(m_columnNames, columnIndex); + return m_columnNames[columnIndex]; + } + return ("Column: " + AString::number(columnIndex + ROW_COLUMN_INDEX_BASE_OFFSET)); + } + + return ""; +} + +/** + * @return Name of row at the given index. + * + * @param rowIndex + * Index of the row. + */ +AString +ChartableTwoFileMatrixChart::getRowNumberAndName(const int32_t rowIndex) const +{ + if ((rowIndex >= 0) + && (rowIndex < m_numberOfRows)) { + if (rowIndex < static_cast(m_rowNumberAndNames.size())) { + CaretAssertVectorIndex(m_rowNumberAndNames, rowIndex); + return m_rowNumberAndNames[rowIndex]; + } + return ("Row: " + AString::number(rowIndex + ROW_COLUMN_INDEX_BASE_OFFSET)); + } + + return ""; +} + +/** + * @return Name of column at the given index. + * + * @param columnIndex + * Index of the row. + */ +AString +ChartableTwoFileMatrixChart::getColumnNumberAndName(const int32_t columnIndex) const +{ + if ((columnIndex >= 0) + && (columnIndex < m_numberOfColumns)) { + if (columnIndex < static_cast(m_columnNumberAndNames.size())) { + CaretAssertVectorIndex(m_columnNumberAndNames, columnIndex); + return m_columnNumberAndNames[columnIndex]; + } + return ("Column: " + AString::number(columnIndex + ROW_COLUMN_INDEX_BASE_OFFSET)); + } + + return ""; +} + +/** + * Save subclass data to the scene. + * + * @param sceneAttributes + * Attributes for the scene. Scenes may be of different types + * (full, generic, etc) and the attributes should be checked when + * restoring the scene. + * + * @param sceneClass + * sceneClass to which data members should be added. Will always + * be valid (non-NULL). + */ +void +ChartableTwoFileMatrixChart::saveSubClassDataToScene(const SceneAttributes* sceneAttributes, + SceneClass* sceneClass) +{ + m_sceneAssistant->saveMembers(sceneAttributes, + sceneClass); +} + +/** + * Restore file data from the scene. + * + * @param sceneAttributes + * Attributes for the scene. Scenes may be of different types + * (full, generic, etc) and the attributes should be checked when + * restoring the scene. + * + * @param sceneClass + * sceneClass for the instance of a class that implements + * this interface. Will NEVER be NULL. + */ +void +ChartableTwoFileMatrixChart::restoreSubClassDataFromScene(const SceneAttributes* sceneAttributes, + const SceneClass* sceneClass) +{ + m_sceneAssistant->restoreMembers(sceneAttributes, + sceneClass); +} + diff --git a/src/Files/ChartableTwoFileMatrixChart.h b/src/Files/ChartableTwoFileMatrixChart.h new file mode 100644 index 0000000000000000000000000000000000000000..b8da84f810bc4d3d030b1369a675534a6671a4c4 --- /dev/null +++ b/src/Files/ChartableTwoFileMatrixChart.h @@ -0,0 +1,167 @@ +#ifndef __CHARTABLE_TWO_FILE_MATRIX_CHART_H__ +#define __CHARTABLE_TWO_FILE_MATRIX_CHART_H__ + +/*LICENSE_START*/ +/* + * Copyright (C) 2017 Washington University School of Medicine + * + * 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 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ +/*LICENSE_END*/ + +#include "BrainConstants.h" +#include "ChartTwoMatrixContentTypeEnum.h" +#include "ChartTwoMatrixLoadingDimensionEnum.h" +#include "ChartTwoMatrixTriangularViewingModeEnum.h" +#include "ChartableTwoFileBaseChart.h" +#include "CiftiMappableDataFile.h" + +namespace caret { + + class CiftiConnectivityMatrixParcelFile; + class CiftiParcelLabelFile; + class CiftiParcelScalarFile; + class CiftiParcelSeriesFile; + class CiftiScalarDataSeriesFile; + class GraphicsPrimitiveV3fC4f; + + class ChartableTwoFileMatrixChart : public ChartableTwoFileBaseChart { + + public: + ChartableTwoFileMatrixChart(const ChartTwoMatrixContentTypeEnum::Enum matrixContentType, + CaretMappableDataFile* parentCaretMappableDataFile, + std::vector& validRowColumnSelectionDimensions); + + virtual ~ChartableTwoFileMatrixChart(); + + ChartTwoMatrixContentTypeEnum::Enum getMatrixContentType() const; + + virtual bool isValid() const override; + + virtual bool isEmpty() const override; + + void getMatrixDimensions(int32_t& numberOfRowsOut, + int32_t& numberOfColumnsOut) const; + + bool getMatrixDataRGBA(int32_t& numberOfRowsOut, + int32_t& numberOfColumnsOut, + std::vector& rgbaOut) const; + + GraphicsPrimitiveV3fC4f* getMatrixChartingGraphicsPrimitive(const ChartTwoMatrixTriangularViewingModeEnum::Enum matrixViewMode, + const CiftiMappableDataFile::MatrixGridMode gridMode) const; + + int32_t getMatrixChartGraphicsPrimitiveGridColorIdentifier() const; + + bool isMatrixTriangularViewingModeSupported() const; + + // ADD_NEW_METHODS_HERE + + ChartTwoMatrixLoadingDimensionEnum::Enum getSelectedRowColumnDimension() const; + + void setSelectedRowColumnDimension(const ChartTwoMatrixLoadingDimensionEnum::Enum rowColumnDimension); + + void getValidRowColumnSelectionDimensions(std::vector& validRowColumnSelectionDimensionOut) const; + + void getSelectedRowColumnIndices(const int32_t tabIndex, + ChartTwoMatrixLoadingDimensionEnum::Enum& rowColumnDimensionOut, + std::vector& selectedRowIndicesOut, + std::vector& selectedColumnIndicesOut) const; + + void setSelectedRowColumnIndex(const int32_t tabIndex, + const int32_t rowColumnIndex); + + bool hasRowSelection() const; + + bool hasColumnSelection() const; + + void loadDefaultRowOrColumn(); + + AString getRowNumberAndName(const int32_t rowIndex) const; + + AString getColumnNumberAndName(const int32_t columnIndex) const; + + AString getRowName(const int32_t rowIndex) const; + + AString getColumnName(const int32_t columnIndex) const; + + protected: + enum class MatrixDataFileType { + INVALID, + PARCEL, + PARCEL_LABEL, + PARCEL_SCALAR, + PARCEL_SERIES, + SCALAR_DATA_SERIES + }; + + virtual void saveSubClassDataToScene(const SceneAttributes* sceneAttributes, + SceneClass* sceneClass) override; + + virtual void restoreSubClassDataFromScene(const SceneAttributes* sceneAttributes, + const SceneClass* sceneClass) override; + + private: + ChartableTwoFileMatrixChart(const ChartableTwoFileMatrixChart&); + + ChartableTwoFileMatrixChart& operator=(const ChartableTwoFileMatrixChart&); + + SceneClassAssistant* m_sceneAssistant; + + ChartTwoMatrixContentTypeEnum::Enum m_matrixContentType; + + const std::vector m_validRowColumnSelectionDimensions; + + bool m_matrixTriangularViewingModeSupportedFlag = false; + + int32_t m_numberOfRows = 0; + + int32_t m_numberOfColumns = 0; + + std::vector m_rowNames; + + std::vector m_columnNames; + + std::vector m_rowNumberAndNames; + + std::vector m_columnNumberAndNames; + + bool m_hasRowSelectionFlag = false; + + bool m_hasColumnSelectionFlag = false; + + MatrixDataFileType m_matrixDataFileType = MatrixDataFileType::INVALID; + + CiftiConnectivityMatrixParcelFile *m_parcelFile = NULL; + CiftiParcelLabelFile* m_parcelLabelFile = NULL; + CiftiParcelScalarFile* m_parcelScalarFile = NULL; + CiftiParcelSeriesFile* m_parcelSeriesFile = NULL; + CiftiScalarDataSeriesFile* m_scalarDataSeriesFile = NULL; + + int32_t m_parcelLabelFileSelectedColumn[BrainConstants::MAXIMUM_NUMBER_OF_BROWSER_TABS]; + int32_t m_parcelScalarFileSelectedColumn[BrainConstants::MAXIMUM_NUMBER_OF_BROWSER_TABS]; + int32_t m_parcelSeriesFileSelectedColumn[BrainConstants::MAXIMUM_NUMBER_OF_BROWSER_TABS]; + + static const int32_t ROW_COLUMN_INDEX_BASE_OFFSET = 1; + + // ADD_NEW_MEMBERS_HERE + + }; + +#ifdef __CHARTABLE_TWO_FILE_MATRIX_CHART_DECLARE__ + // +#endif // __CHARTABLE_TWO_FILE_MATRIX_CHART_DECLARE__ + +} // namespace +#endif //__CHARTABLE_TWO_FILE_MATRIX_CHART_H__ diff --git a/src/Files/CiftiBrainordinateDataSeriesFile.cxx b/src/Files/CiftiBrainordinateDataSeriesFile.cxx index 96dc6a4b3e325fe0b97ae3e79d79a048f54c50a2..c6c6cbf6883850f8a7113189deaf8d44f520c182 100644 --- a/src/Files/CiftiBrainordinateDataSeriesFile.cxx +++ b/src/Files/CiftiBrainordinateDataSeriesFile.cxx @@ -23,6 +23,8 @@ #include "CiftiBrainordinateDataSeriesFile.h" #undef __CIFTI_BRAINORDINATE_DATA_SERIES_FILE_DECLARE__ +#include + #include "CaretLogger.h" #include "ChartDataCartesian.h" #include "CiftiConnectivityMatrixDenseDynamicFile.h" @@ -106,7 +108,7 @@ CiftiBrainordinateDataSeriesFile::initializeDenseDynamicFile() GiftiMetaData* fileMetaData = m_ciftiFile->getCiftiXML().getFileMetaData(); const AString encodedPaletteColorMappingString = fileMetaData->get(s_paletteColorMappingNameInMetaData); if ( ! encodedPaletteColorMappingString.isEmpty()) { - if (m_lazyInitializedDenseDynamicFile-getNumberOfMaps() > 0) { + if (m_lazyInitializedDenseDynamicFile->getNumberOfMaps() > 0) { PaletteColorMapping* pcm = m_lazyInitializedDenseDynamicFile->getMapPaletteColorMapping(0); CaretAssert(pcm); pcm->decodeFromStringXML(encodedPaletteColorMappingString); @@ -171,7 +173,7 @@ CiftiBrainordinateDataSeriesFile::writeFile(const AString& ciftiMapFileName) if (m_lazyInitializedDenseDynamicFile != NULL) { GiftiMetaData* fileMetaData = m_ciftiFile->getCiftiXML().getFileMetaData(); CaretAssert(fileMetaData); - if (m_lazyInitializedDenseDynamicFile-getNumberOfMaps() > 0) { + if (m_lazyInitializedDenseDynamicFile->getNumberOfMaps() > 0) { fileMetaData->set(s_paletteColorMappingNameInMetaData, m_lazyInitializedDenseDynamicFile->getMapPaletteColorMapping(0)->encodeInXML()); } @@ -297,7 +299,7 @@ CiftiBrainordinateDataSeriesFile::loadLineSeriesChartDataForVoxelAtCoordinate(co * Chart types supported by this file. */ void -CiftiBrainordinateDataSeriesFile::getSupportedLineSeriesChartDataTypes(std::vector& chartDataTypesOut) const +CiftiBrainordinateDataSeriesFile::getSupportedLineSeriesChartDataTypes(std::vector& chartDataTypesOut) const { helpGetSupportedLineSeriesChartDataTypes(chartDataTypesOut); } @@ -430,5 +432,44 @@ CiftiBrainordinateDataSeriesFile::isModifiedPaletteColorMapping() const return false; } +/** + * @return The modified status for aall palettes in this file. + * Note that 'modified' overrides any 'modified by show scene'. + */ +PaletteModifiedStatusEnum::Enum +CiftiBrainordinateDataSeriesFile::getPaletteColorMappingModifiedStatus() const +{ + const std::array fileModStatus = { { + CiftiMappableDataFile::getPaletteColorMappingModifiedStatus(), + ((m_lazyInitializedDenseDynamicFile != NULL) + ? m_lazyInitializedDenseDynamicFile->getPaletteColorMappingModifiedStatus() + : PaletteModifiedStatusEnum::UNMODIFIED) + } }; + + PaletteModifiedStatusEnum::Enum modStatus = PaletteModifiedStatusEnum::UNMODIFIED; + for (auto status : fileModStatus) { + switch (status) { + case PaletteModifiedStatusEnum::MODIFIED: + modStatus = PaletteModifiedStatusEnum::MODIFIED; + break; + case PaletteModifiedStatusEnum::MODIFIED_BY_SHOW_SCENE: + modStatus = PaletteModifiedStatusEnum::MODIFIED_BY_SHOW_SCENE; + break; + case PaletteModifiedStatusEnum::UNMODIFIED: + break; + } + + if (modStatus == PaletteModifiedStatusEnum::MODIFIED) { + /* + * 'MODIFIED' overrides 'MODIFIED_BY_SHOW_SCENE' + * so no need to continue loop + */ + break; + } + } + + return modStatus; +} + diff --git a/src/Files/CiftiBrainordinateDataSeriesFile.h b/src/Files/CiftiBrainordinateDataSeriesFile.h index 34317aa249df5cfa527b77f636c31b294a929a94..85cccd5242b4cef3e85bd07300630e640b23fd07 100644 --- a/src/Files/CiftiBrainordinateDataSeriesFile.h +++ b/src/Files/CiftiBrainordinateDataSeriesFile.h @@ -27,7 +27,6 @@ namespace caret { class CiftiConnectivityMatrixDenseDynamicFile; - class PaletteFile; class CiftiBrainordinateDataSeriesFile : public CiftiMappableDataFile, @@ -54,7 +53,7 @@ namespace caret { virtual ChartDataCartesian* loadLineSeriesChartDataForVoxelAtCoordinate(const float xyz[3]); - virtual void getSupportedLineSeriesChartDataTypes(std::vector& chartDataTypesOut) const; + virtual void getSupportedLineSeriesChartDataTypes(std::vector& chartDataTypesOut) const; CiftiConnectivityMatrixDenseDynamicFile* getConnectivityMatrixDenseDynamicFile(); @@ -66,7 +65,9 @@ namespace caret { virtual void writeFile(const AString& filename); - virtual bool isModifiedPaletteColorMapping() const; + virtual bool isModifiedPaletteColorMapping() const override; + + virtual PaletteModifiedStatusEnum::Enum getPaletteColorMappingModifiedStatus() const override; private: CiftiBrainordinateDataSeriesFile(const CiftiBrainordinateDataSeriesFile&); diff --git a/src/Files/CiftiBrainordinateScalarFile.cxx b/src/Files/CiftiBrainordinateScalarFile.cxx index 9d69de418b37c9fce2c36c939c62704afad64ed5..2e843e7c71b96d014f4592e1e5b3846769f817ad 100644 --- a/src/Files/CiftiBrainordinateScalarFile.cxx +++ b/src/Files/CiftiBrainordinateScalarFile.cxx @@ -196,8 +196,10 @@ CiftiBrainordinateScalarFile::newInstanceFromRowInCiftiConnectivityMatrixFile(co CaretAssert(scalarPalette); const PaletteColorMapping* densePalette = sourceCiftiMatrixFile->getMapPaletteColorMapping(0); CaretAssert(densePalette); - scalarPalette->copy(*densePalette); + scalarPalette->copy(*densePalette, + true); + scalarFile->updateAfterFileDataChanges(); scalarFile->setModified(); return scalarFile; @@ -262,7 +264,7 @@ CiftiBrainordinateScalarFile::setLineSeriesChartingEnabled(const int32_t tabInde * Chart types supported by this file. */ void -CiftiBrainordinateScalarFile::getSupportedLineSeriesChartDataTypes(std::vector& chartDataTypesOut) const +CiftiBrainordinateScalarFile::getSupportedLineSeriesChartDataTypes(std::vector& chartDataTypesOut) const { helpGetSupportedLineSeriesChartDataTypes(chartDataTypesOut); } @@ -296,7 +298,7 @@ CiftiBrainordinateScalarFile::loadLineSeriesChartDataForSurfaceNode(const Struct // data)) { // const int64_t numData = static_cast(data.size()); // -// chartData = new ChartDataCartesian(ChartDataTypeEnum::CHART_DATA_TYPE_LINE_DATA_SERIES, +// chartData = new ChartDataCartesian(ChartOneDataTypeEnum::CHART_DATA_TYPE_LINE_DATA_SERIES, // ChartAxisUnitsEnum::CHART_AXIS_UNITS_NONE, // ChartAxisUnitsEnum::CHART_AXIS_UNITS_NONE); // for (int64_t i = 0; i < numData; i++) { diff --git a/src/Files/CiftiBrainordinateScalarFile.h b/src/Files/CiftiBrainordinateScalarFile.h index 75ca3e1eee0b2ca04583b41f8f1027ef35604524..21d4aaa98e5a0a060b03c36b1606565fbc815aa5 100644 --- a/src/Files/CiftiBrainordinateScalarFile.h +++ b/src/Files/CiftiBrainordinateScalarFile.h @@ -59,7 +59,7 @@ namespace caret { virtual ChartDataCartesian* loadLineSeriesChartDataForVoxelAtCoordinate(const float xyz[3]); - virtual void getSupportedLineSeriesChartDataTypes(std::vector& chartDataTypesOut) const; + virtual void getSupportedLineSeriesChartDataTypes(std::vector& chartDataTypesOut) const; private: CiftiBrainordinateScalarFile(const CiftiBrainordinateScalarFile&); diff --git a/src/Files/CiftiConnectivityMatrixDenseDynamicFile.cxx b/src/Files/CiftiConnectivityMatrixDenseDynamicFile.cxx index fd0e221cd1a6774c1020b153d4d69cb1175c012a..7a18b2cf1e51ca4c5688b6e76cdff1bf0b434765 100644 --- a/src/Files/CiftiConnectivityMatrixDenseDynamicFile.cxx +++ b/src/Files/CiftiConnectivityMatrixDenseDynamicFile.cxx @@ -489,12 +489,12 @@ CiftiConnectivityMatrixDenseDynamicFile::correlation(const std::vector& d const RowData& otherData = m_rowData[otherRowIndex]; if (m_cacheDataFlag) { - xySum = sddot(&data[0], &otherData.m_data[0], numberOfPoints); + xySum = dsdot(&data[0], &otherData.m_data[0], numberOfPoints); } else { std::vector otherDataVector(m_numberOfTimePoints); m_parentDataSeriesCiftiFile->getRow(&otherDataVector[0], otherRowIndex); - xySum = sddot(&data[0], &otherDataVector[0], numberOfPoints); + xySum = dsdot(&data[0], &otherDataVector[0], numberOfPoints); } const double ssxy = xySum - (numFloat * mean * otherData.m_mean); diff --git a/src/Files/CiftiConnectivityMatrixParcelFile.cxx b/src/Files/CiftiConnectivityMatrixParcelFile.cxx index 0615f552e9ddb9f9836c12808a3fd2070dc0dfab..63ae2544437ace8ca5bc83a68b29a3bfc6e22500 100644 --- a/src/Files/CiftiConnectivityMatrixParcelFile.cxx +++ b/src/Files/CiftiConnectivityMatrixParcelFile.cxx @@ -35,7 +35,6 @@ #include "NodeAndVoxelColoring.h" #include "Palette.h" #include "PaletteColorMapping.h" -#include "PaletteFile.h" #include "SceneAttributes.h" #include "SceneClass.h" #include "SceneClassArray.h" @@ -58,7 +57,8 @@ using namespace caret; * Constructor. */ CiftiConnectivityMatrixParcelFile::CiftiConnectivityMatrixParcelFile() -: CiftiMappableConnectivityMatrixDataFile(DataFileTypeEnum::CONNECTIVITY_PARCEL) +: CiftiMappableConnectivityMatrixDataFile(DataFileTypeEnum::CONNECTIVITY_PARCEL), +ChartableMatrixParcelInterface() { for (int32_t i = 0; i < BrainConstants::MAXIMUM_NUMBER_OF_BROWSER_TABS; i++) { m_chartingEnabledForTab[i] = false; @@ -168,6 +168,8 @@ CiftiConnectivityMatrixParcelFile::receiveEvent(Event* event) } } } + + CiftiMappableConnectivityMatrixDataFile::receiveEvent(event); } /** @@ -325,10 +327,10 @@ CiftiConnectivityMatrixParcelFile::setMatrixChartingEnabled(const int32_t tabInd * Chart types supported by this file. */ void -CiftiConnectivityMatrixParcelFile::getSupportedMatrixChartDataTypes(std::vector& chartDataTypesOut) const +CiftiConnectivityMatrixParcelFile::getSupportedMatrixChartDataTypes(std::vector& chartDataTypesOut) const { chartDataTypesOut.clear(); - chartDataTypesOut.push_back(ChartDataTypeEnum::CHART_DATA_TYPE_MATRIX_LAYER); + chartDataTypesOut.push_back(ChartOneDataTypeEnum::CHART_DATA_TYPE_MATRIX_LAYER); } /** diff --git a/src/Files/CiftiConnectivityMatrixParcelFile.h b/src/Files/CiftiConnectivityMatrixParcelFile.h index eece5eb4a1b498837f985edda055e310c34c8d29..75ccbe807b685c28ebde147bb68aa32b09c4477c 100644 --- a/src/Files/CiftiConnectivityMatrixParcelFile.h +++ b/src/Files/CiftiConnectivityMatrixParcelFile.h @@ -24,18 +24,15 @@ #include "BrainConstants.h" #include "CiftiMappableConnectivityMatrixDataFile.h" #include "ChartableMatrixParcelInterface.h" -#include "EventListenerInterface.h" namespace caret { class ChartMatrixDisplayProperties; class CiftiParcelReorderingModel; - class PaletteFile; class CiftiConnectivityMatrixParcelFile : public CiftiMappableConnectivityMatrixDataFile, - public ChartableMatrixParcelInterface, - public EventListenerInterface { + public ChartableMatrixParcelInterface { public: CiftiConnectivityMatrixParcelFile(); @@ -70,7 +67,7 @@ namespace caret { virtual void setMatrixChartingEnabled(const int32_t tabIndex, const bool enabled); - virtual void getSupportedMatrixChartDataTypes(std::vector& chartDataTypesOut) const; + virtual void getSupportedMatrixChartDataTypes(std::vector& chartDataTypesOut) const; const ChartMatrixDisplayProperties* getChartMatrixDisplayProperties(const int32_t tabIndex) const; diff --git a/src/Files/CiftiFiberTrajectoryFile.cxx b/src/Files/CiftiFiberTrajectoryFile.cxx index c89dc1af81c5524ad9d4444bd3fb65be6f208d3c..a96f963154409551b0ef9db42a7655c2cf1abd81 100644 --- a/src/Files/CiftiFiberTrajectoryFile.cxx +++ b/src/Files/CiftiFiberTrajectoryFile.cxx @@ -731,7 +731,7 @@ CiftiFiberTrajectoryFile::getMapLabelTable(const int32_t /*mapIndex*/) const * vector is assumed to be the default mode. */ void -CiftiFiberTrajectoryFile::getPaletteNormalizationModesSupported(std::vector& modesSupportedOut) +CiftiFiberTrajectoryFile::getPaletteNormalizationModesSupported(std::vector& modesSupportedOut) const { modesSupportedOut.clear(); } @@ -749,8 +749,7 @@ CiftiFiberTrajectoryFile::getPaletteNormalizationModesSupported(std::vectorgetCiftiXML(); + } + return CiftiXML(); +} + +/** + * Get data from the file as requested in the given map file data selector. + * + * @param mapFileDataSelector + * Specifies selection of data. + * @param dataOut + * Output with data. Will be empty if data does not support the map file data selector. + */ +void +CiftiFiberTrajectoryFile::getDataForSelector(const MapFileDataSelector& /*mapFileDataSelector*/, + std::vector& dataOut) const +{ + dataOut.clear(); +} + +/** + * Are all brainordinates in this file also in the given file? + * That is, the brainordinates are equal to or a subset of the brainordinates + * in the given file. + * + * @param mapFile + * The given map file. + * @return + * True if brainordinates in this file are subset or equal to the given file, else false. + */ +CaretMappableDataFile::BrainordinateMappingMatch +CiftiFiberTrajectoryFile::getBrainordinateMappingMatch(const CaretMappableDataFile* /*mapFile*/) const +{ + return BrainordinateMappingMatch::NO; +} + + diff --git a/src/Files/CiftiFiberTrajectoryFile.h b/src/Files/CiftiFiberTrajectoryFile.h index fad1558326032b120d3e7604833eaff1abfef9c1..66dc4e93f530792d78b8535ef213f0fbaa4cc735 100644 --- a/src/Files/CiftiFiberTrajectoryFile.h +++ b/src/Files/CiftiFiberTrajectoryFile.h @@ -108,10 +108,10 @@ namespace caret { virtual const GiftiLabelTable* getMapLabelTable(const int32_t mapIndex) const; - virtual void getPaletteNormalizationModesSupported(std::vector& modesSupportedOut); + virtual void getPaletteNormalizationModesSupported(std::vector& modesSupportedOut) const; + + virtual void updateScalarColoringForMap(const int32_t mapIndex) override; - virtual void updateScalarColoringForMap(const int32_t mapIndex, - const PaletteFile* paletteFile); virtual void readFile(const AString& filename); virtual void writeFile(const AString& filename); @@ -171,6 +171,15 @@ namespace caret { void addToDataFileContentInformation(DataFileContentInformation& dataFileInformation); + bool hasCiftiXML() const; + + const CiftiXML getCiftiXML() const; + + virtual void getDataForSelector(const MapFileDataSelector& mapFileDataSelector, + std::vector& dataOut) const override; + + virtual BrainordinateMappingMatch getBrainordinateMappingMatch(const CaretMappableDataFile* mapFile) const override; + // ADD_NEW_METHODS_HERE private: diff --git a/src/Files/CiftiMappableConnectivityMatrixDataFile.cxx b/src/Files/CiftiMappableConnectivityMatrixDataFile.cxx index c88434743edefd755cf460e465bb89a9e66d0933..fee8cbc5f50f969daa294bd32bc1b8427dffe7c1 100644 --- a/src/Files/CiftiMappableConnectivityMatrixDataFile.cxx +++ b/src/Files/CiftiMappableConnectivityMatrixDataFile.cxx @@ -664,17 +664,17 @@ CiftiMappableConnectivityMatrixDataFile::loadDataForRowIndex(const int64_t rowIn if ((rowIndex >= 0) && (rowIndex < m_ciftiFile->getNumberOfRows())) { m_rowLoadedTextForMapName = ("Row: " - + AString::number(rowIndex)); + + AString::number(rowIndex + CIFTI_FILE_ROW_COLUMN_INDEX_BASE_FOR_GUI)); m_rowLoadedText = ("Row_" - + AString::number(rowIndex)); + + AString::number(rowIndex + CIFTI_FILE_ROW_COLUMN_INDEX_BASE_FOR_GUI)); CaretAssert((rowIndex >= 0) && (rowIndex < m_ciftiFile->getNumberOfRows())); m_loadedRowData.resize(dataCount); getProcessedDataForRow(&m_loadedRowData[0], rowIndex); - CaretLogFine("Read row " + AString::number(rowIndex)); + CaretLogFine("Read row " + AString::number(rowIndex + CIFTI_FILE_ROW_COLUMN_INDEX_BASE_FOR_GUI)); m_connectivityDataLoaded->setRowColumnLoading(rowIndex, -1); } @@ -705,17 +705,17 @@ CiftiMappableConnectivityMatrixDataFile::loadDataForColumnIndex(const int64_t co if ((columnIndex >= 0) && (columnIndex < m_ciftiFile->getNumberOfColumns())) { m_rowLoadedTextForMapName = ("Column: " - + AString::number(columnIndex)); + + AString::number(columnIndex + CIFTI_FILE_ROW_COLUMN_INDEX_BASE_FOR_GUI)); m_rowLoadedText = ("Column_" - + AString::number(columnIndex)); + + AString::number(columnIndex + CIFTI_FILE_ROW_COLUMN_INDEX_BASE_FOR_GUI)); CaretAssert((columnIndex >= 0) && (columnIndex < m_ciftiFile->getNumberOfColumns())); m_loadedRowData.resize(dataCount); getProcessedDataForColumn(&m_loadedRowData[0], columnIndex); - CaretLogFine("Read column " + AString::number(columnIndex)); + CaretLogFine("Read column " + AString::number(columnIndex + CIFTI_FILE_ROW_COLUMN_INDEX_BASE_FOR_GUI)); m_connectivityDataLoaded->setRowColumnLoading(-1, columnIndex); } @@ -806,15 +806,15 @@ CiftiMappableConnectivityMatrixDataFile::loadMapDataForSurfaceNode(const int32_t if (dataCount > 0) { m_rowLoadedTextForMapName = ("Row: " - + AString::number(rowIndex) - + ", Node Index: " + + AString::number(rowIndex + CIFTI_FILE_ROW_COLUMN_INDEX_BASE_FOR_GUI) + + ", Vertex Index: " + AString::number(nodeIndex) + ", Structure: " + StructureEnum::toName(structure)); m_rowLoadedText = ("Row_" - + AString::number(rowIndex) - + "_Node_Index_" + + AString::number(rowIndex + CIFTI_FILE_ROW_COLUMN_INDEX_BASE_FOR_GUI) + + "_Vertex_Index_" + AString::number(nodeIndex) + "_Structure_" + StructureEnum::toGuiName(structure)); @@ -823,7 +823,7 @@ CiftiMappableConnectivityMatrixDataFile::loadMapDataForSurfaceNode(const int32_t getProcessedDataForRow(&m_loadedRowData[0], rowIndex); - CaretLogFine("Read row for node " + AString::number(nodeIndex)); + CaretLogFine("Read row for vertex " + AString::number(nodeIndex)); m_connectivityDataLoaded->setSurfaceNodeLoading(structure, surfaceNumberOfNodes, @@ -839,15 +839,15 @@ CiftiMappableConnectivityMatrixDataFile::loadMapDataForSurfaceNode(const int32_t const int64_t dataCount = m_ciftiFile->getNumberOfRows(); if (dataCount > 0) { m_rowLoadedTextForMapName = ("Column: " - + AString::number(columnIndex) - + ", Node Index: " + + AString::number(columnIndex + CIFTI_FILE_ROW_COLUMN_INDEX_BASE_FOR_GUI) + + ", Vertex Index: " + AString::number(nodeIndex) + ", Structure: " + StructureEnum::toName(structure)); m_rowLoadedText = ("Column_" - + AString::number(columnIndex) - + "_Node_Index_" + + AString::number(columnIndex + CIFTI_FILE_ROW_COLUMN_INDEX_BASE_FOR_GUI) + + "_Vertex_Index_" + AString::number(nodeIndex) + "_Structure_" + StructureEnum::toGuiName(structure)); @@ -856,7 +856,7 @@ CiftiMappableConnectivityMatrixDataFile::loadMapDataForSurfaceNode(const int32_t getProcessedDataForColumn(&m_loadedRowData[0], columnIndex); - CaretLogFine("Read column for node " + AString::number(nodeIndex)); + CaretLogFine("Read column for vertex " + AString::number(nodeIndex)); m_connectivityDataLoaded->setSurfaceNodeLoading(structure, surfaceNumberOfNodes, @@ -869,7 +869,7 @@ CiftiMappableConnectivityMatrixDataFile::loadMapDataForSurfaceNode(const int32_t } if (dataWasLoaded == false) { - CaretLogFine("FAILED to read data for node " + AString::number(nodeIndex)); + CaretLogFine("FAILED to read data for vertex " + AString::number(nodeIndex)); m_connectivityDataLoaded->reset(); } } @@ -880,12 +880,12 @@ CiftiMappableConnectivityMatrixDataFile::loadMapDataForSurfaceNode(const int32_t updateForChangeInMapDataWithMapIndex(0); - AString msg = ("Time load data for surface node in " + AString msg = ("Time load data for surface vertex in " + getFileNameNoPath() + " was " + AString::number(timer.getElapsedTimeSeconds()) + " seconds."); - CaretLogInfo(msg); + CaretLogFine(msg); } @@ -979,7 +979,7 @@ CiftiMappableConnectivityMatrixDataFile::loadMapAverageDataForSurfaceNodes(const } if (dataWasLoaded == false) { - CaretLogFine("FAILED to read data for node average" + AString::fromNumbers(nodeIndices, ",")); + CaretLogFine("FAILED to read data for vertex average" + AString::fromNumbers(nodeIndices, ",")); } updateForChangeInMapDataWithMapIndex(0); @@ -1064,13 +1064,13 @@ CiftiMappableConnectivityMatrixDataFile::loadMapDataForVoxelAtCoordinate(const i rowIndex); m_rowLoadedTextForMapName = ("Row: " - + AString::number(rowIndex) + + AString::number(rowIndex + CIFTI_FILE_ROW_COLUMN_INDEX_BASE_FOR_GUI) + ", Voxel XYZ: (" + AString::fromNumbers(xyz, 3, ",") + ")"); m_rowLoadedText = ("Row_" - + AString::number(rowIndex) + + AString::number(rowIndex + CIFTI_FILE_ROW_COLUMN_INDEX_BASE_FOR_GUI) + "_Voxel_XYZ_" + AString::fromNumbers(xyz, 3, "_").replace('-', 'm')); @@ -1089,13 +1089,13 @@ CiftiMappableConnectivityMatrixDataFile::loadMapDataForVoxelAtCoordinate(const i columnIndex); m_rowLoadedTextForMapName = ("Column: " - + AString::number(columnIndex) + + AString::number(columnIndex + CIFTI_FILE_ROW_COLUMN_INDEX_BASE_FOR_GUI) + ", Voxel XYZ: (" + AString::fromNumbers(xyz, 3, ",") + ")"); m_rowLoadedText = ("Column_" - + AString::number(columnIndex) + + AString::number(columnIndex + CIFTI_FILE_ROW_COLUMN_INDEX_BASE_FOR_GUI) + "_Voxel_XYZ_" + AString::fromNumbers(xyz, 3, "_").replace('-', 'm')); @@ -1291,6 +1291,37 @@ CiftiMappableConnectivityMatrixDataFile::getMapName(const int32_t /*mapIndex*/) return m_rowLoadedTextForMapName; } +/** + * @return True if the number of rows and columns is identical + * and the row and column names are identical for the same index + * (row "i" name is same as column "i" name for all rows/columns). + */ +bool +CiftiMappableConnectivityMatrixDataFile::hasSymetricRowColumnNames() const +{ + const CiftiXML& xml = m_ciftiFile->getCiftiXML(); + if ((xml.getMappingType(CiftiXML::ALONG_COLUMN) == CiftiMappingType::PARCELS) + && (xml.getMappingType(CiftiXML::ALONG_ROW) == CiftiMappingType::PARCELS)) { + const std::vector& rowParcels = xml.getParcelsMap(CiftiXML::ALONG_COLUMN).getParcels(); + const std::vector& columnParcels = xml.getParcelsMap(CiftiXML::ALONG_ROW).getParcels(); + if (rowParcels.size() == columnParcels.size()) { + const int numRowsColumns = static_cast(rowParcels.size()); + if (numRowsColumns > 0) { + for (int32_t i = 0; i < numRowsColumns; i++) { + CaretAssertVectorIndex(rowParcels, i); + CaretAssertVectorIndex(columnParcels, i); + if (rowParcels[i].m_name != columnParcels[i].m_name) { + return false; + } + } + return true; + } + } + } + + return false; +} + AString CiftiMappableConnectivityMatrixDataFile::getRowName(const int32_t rowIndex) const { diff --git a/src/Files/CiftiMappableConnectivityMatrixDataFile.h b/src/Files/CiftiMappableConnectivityMatrixDataFile.h index 3bfd2db3bd2e0da24cda5c147a4c0b06cf36e3f1..a14e5fb88fcd014b814084347e58af71062b8420 100644 --- a/src/Files/CiftiMappableConnectivityMatrixDataFile.h +++ b/src/Files/CiftiMappableConnectivityMatrixDataFile.h @@ -78,6 +78,8 @@ namespace caret { virtual AString getMapName(const int32_t mapIndex) const; + bool hasSymetricRowColumnNames() const; + AString getRowName(const int32_t rowIndex) const; AString getColumnName(const int32_t rowIndex) const; diff --git a/src/Files/CiftiMappableDataFile.cxx b/src/Files/CiftiMappableDataFile.cxx index 336d25d20f9866bf124a230ff9ee6c186ddaed90..2d72c8661ce74c2a3d4b3b0d22d39535a30cb2a1 100644 --- a/src/Files/CiftiMappableDataFile.cxx +++ b/src/Files/CiftiMappableDataFile.cxx @@ -24,31 +24,41 @@ #include "CiftiMappableDataFile.h" #undef __CIFTI_MAPPABLE_DATA_FILE_DECLARE__ +#include "BackgroundAndForegroundColors.h" #include "BoundingBox.h" #include "CaretAssert.h" #include "CaretLogger.h" +#include "CaretPreferences.h" #include "ChartDataCartesian.h" #include "CiftiBrainordinateLabelFile.h" #include "CiftiBrainordinateScalarFile.h" +#include "CiftiConnectivityMatrixParcelFile.h" #include "CiftiFiberTrajectoryFile.h" #include "CiftiFile.h" #include "CiftiMappableConnectivityMatrixDataFile.h" +#include "CaretMappableDataFileAndMapSelectionModel.h" #include "CiftiParcelLabelFile.h" +#include "CiftiParcelReordering.h" +#include "CiftiParcelScalarFile.h" +#include "CiftiParcelSeriesFile.h" +#include "CiftiScalarDataSeriesFile.h" #include "CaretTemporaryFile.h" #include "CiftiXML.h" #include "DataFileContentInformation.h" #include "EventManager.h" -#include "EventPaletteGetByName.h" +#include "EventCaretPreferencesGet.h" +#include "EventSurfaceColoringInvalidate.h" #include "FastStatistics.h" #include "FileInformation.h" #include "GiftiLabel.h" #include "GiftiLabelTable.h" #include "GiftiMetaData.h" +#include "GraphicsPrimitiveV3fC4f.h" #include "GroupAndNameHierarchyModel.h" #include "Histogram.h" +#include "MapFileDataSelector.h" #include "NodeAndVoxelColoring.h" #include "PaletteColorMapping.h" -#include "PaletteFile.h" #include "SparseVolumeIndexer.h" using namespace caret; @@ -75,10 +85,19 @@ CiftiMappableDataFile::CiftiMappableDataFile(const DataFileTypeEnum::Enum dataFi m_classNameHierarchy.grabNew(NULL); m_fileDataReadingType = FILE_READ_DATA_ALL; + switch (CIFTI_FILE_ROW_COLUMN_INDEX_BASE_FOR_GUI) { + case 0: + case 1: + break; + default: + CaretAssertMessage(0, "CIFTI_FILE_ROW_COLUMN_INDEX_BASE_FOR_GUI must be zero or one!"); + break; + } + m_containsSurfaceData = false; m_containsVolumeData = false; - m_mappingTimeStart = 0.0; - m_mappingTimeStep = 0.0; + m_mappingTimeStart = 0.0f; + m_mappingTimeStep = 1.0f; m_mappingTimeUnits = NiftiTimeUnitsEnum::NIFTI_UNITS_UNKNOWN; m_dataReadingAccessMethod = DATA_ACCESS_METHOD_INVALID; @@ -239,14 +258,6 @@ CiftiMappableDataFile::CiftiMappableDataFile(const DataFileTypeEnum::Enum dataFi break; } - /* - * Note: The first palette normalization mode is assumed to - * be the default mode. The method called is in a parent class. - */ - if ( ! m_paletteNormalizationModesSupported.empty()) { - setPaletteNormalizationMode(m_paletteNormalizationModesSupported[0]); - } - CaretAssert(m_dataReadingAccessMethod != DATA_ACCESS_METHOD_INVALID); CaretAssert(m_dataMappingAccessMethod != DATA_ACCESS_METHOD_INVALID); CaretAssert(m_colorMappingMethod != COLOR_MAPPING_METHOD_INVALID); @@ -258,6 +269,8 @@ CiftiMappableDataFile::CiftiMappableDataFile(const DataFileTypeEnum::Enum dataFi setupCiftiReadingMappingDirection(); m_classNameHierarchy.grabNew(new GroupAndNameHierarchyModel()); + + EventManager::get()->addEventListener(this, EventTypeEnum::EVENT_SURFACE_COLORING_INVALIDATE); } /** @@ -265,9 +278,29 @@ CiftiMappableDataFile::CiftiMappableDataFile(const DataFileTypeEnum::Enum dataFi */ CiftiMappableDataFile::~CiftiMappableDataFile() { + EventManager::get()->removeAllEventsFromListener(this); + clearPrivate(); } +/** + * Receive an event. + * + * @param event + * An event for which this instance is listening. + */ +void +CiftiMappableDataFile::receiveEvent(Event* event) +{ + if (event->getEventType() == EventTypeEnum::EVENT_SURFACE_COLORING_INVALIDATE) { + EventSurfaceColoringInvalidate* colorInvalidateEvent + = dynamic_cast(event); + CaretAssert(colorInvalidateEvent); + colorInvalidateEvent->setEventProcessed(); + + invalidateColoringInAllMaps(); + } +} /** * Create a new instance of a CIFTI file from the given CIFTI data file type @@ -432,9 +465,12 @@ CiftiMappableDataFile::clearPrivate() m_containsSurfaceData = false; m_containsVolumeData = false; - m_mappingTimeStart = 0.0; - m_mappingTimeStep = 0.0; + m_mappingTimeStart = 0.0f; + m_mappingTimeStep = 1.0f; m_mappingTimeUnits = NiftiTimeUnitsEnum::NIFTI_UNITS_UNKNOWN; + + m_brainordinateMapping.reset(); + m_brainordinateMappingCachedFlag = false; } /** @@ -663,6 +699,7 @@ CiftiMappableDataFile::readFile(const AString& ciftiMapFileName) } if (m_ciftiFile != NULL) { + setFileName(ciftiMapFileName); // need by charting delegate initializeAfterReading(ciftiMapFileName); } } @@ -676,6 +713,8 @@ CiftiMappableDataFile::readFile(const AString& ciftiMapFileName) e.whatString()); } + updateAfterFileDataChanges(); + setFileName(ciftiMapFileName); clearModified(); } @@ -956,6 +995,35 @@ CiftiMappableDataFile::initializeAfterReading(const AString& filename) } break; } + + /* + * Special case for scalar data series that does NOT map to brainordinates + */ + if (getDataFileType() == DataFileTypeEnum::CONNECTIVITY_SCALAR_DATA_SERIES) { + const CiftiSeriesMap& map = ciftiXML.getSeriesMap(CiftiXML::ALONG_ROW); + CiftiSeriesMap::Unit units = map.getUnit(); + switch (units) { + case CiftiSeriesMap::HERTZ: + m_mappingTimeUnits = NiftiTimeUnitsEnum::NIFTI_UNITS_HZ; + break; + case CiftiSeriesMap::METER: + CaretLogWarning("CIFTI Units METER not implemented"); + break; + case CiftiSeriesMap::RADIAN: + CaretLogWarning("CIFTI Units RADIAN not implemented"); + break; + case CiftiSeriesMap::SECOND: + m_mappingTimeUnits = NiftiTimeUnitsEnum::NIFTI_UNITS_SEC; + break; + } + m_mappingTimeStart = map.getStart(); + m_mappingTimeStep = map.getStep(); + } + + if (m_mappingTimeStep <= 0.0) { + m_mappingTimeStep = 1.0f; + } + /* * May not have mappings to voxels */ @@ -991,7 +1059,8 @@ CiftiMappableDataFile::initializeAfterReading(const AString& filename) * Get data for maps. */ for (int32_t i = 0; i < numberOfMaps; i++) { - MapContent* mc = new MapContent(m_ciftiFile, + MapContent* mc = new MapContent(this, + m_ciftiFile, m_fileMapDataType, m_dataReadingDirectionForCiftiXML, m_dataMappingDirectionForCiftiXML, @@ -1055,43 +1124,6 @@ CiftiMappableDataFile::writeFile(const AString& ciftiMapFileName) clearModified(); } -///** -// * @return The string name of the CIFTI index type. -// * @param ciftiIndexType -// */ -//AString -//CiftiMappableDataFile::ciftiIndexTypeToName(const IndicesMapToDataType ciftiIndexType) -//{ -// AString name = "Invalid"; -// -// switch (ciftiIndexType) { -// case CIFTI_INDEX_TYPE_BRAIN_MODELS: -// name = "CIFTI_INDEX_TYPE_BRAIN_MODELS"; -// break; -// case CIFTI_INDEX_TYPE_FIBERS: -// name = "CIFTI_INDEX_TYPE_FIBERS"; -// break; -// case CIFTI_INDEX_TYPE_INVALID: -// name = "CIFTI_INDEX_TYPE_INVALID"; -// break; -// case CIFTI_INDEX_TYPE_LABELS: -// name = "CIFTI_INDEX_TYPE_LABELS"; -// break; -// case CIFTI_INDEX_TYPE_PARCELS: -// name = "CIFTI_INDEX_TYPE_PARCELS"; -// break; -// case CIFTI_INDEX_TYPE_SCALARS: -// name = "CIFTI_INDEX_TYPE_SCALARS"; -// break; -// case CIFTI_INDEX_TYPE_TIME_POINTS: -// name = "CIFTI_INDEX_TYPE_TIME_POINTS"; -// break; -// } -// -// return name; -//} -// -// /** * @return Is the data mappable to a surface? */ @@ -1258,6 +1290,37 @@ CiftiMappableDataFile::isMappedWithPalette() const return false; } +/** + * @return True if file is mapped with a palette and one + * palette is used for all maps. + */ +bool +CiftiMappableDataFile::isOnePaletteUsedForAllMaps() const +{ + bool onePaletteFlag = false; + + switch (m_colorMappingMethod) { + case COLOR_MAPPING_METHOD_INVALID: + break; + case COLOR_MAPPING_METHOD_LABEL_TABLE: + break; + case COLOR_MAPPING_METHOD_PALETTE: + switch (m_paletteColorMappingSource) { + case PALETTE_COLOR_MAPPING_SOURCE_INVALID: + break; + case PALETTE_COLOR_MAPPING_SOURCE_FROM_FILE: + onePaletteFlag = true; + break; + case PALETTE_COLOR_MAPPING_SOURCE_FROM_MAP: + onePaletteFlag = false; + break; + } + break; + } + + return onePaletteFlag; +} + /** * Get the data for the given map index. * @@ -1363,6 +1426,14 @@ CiftiMappableDataFile::invalidateColoringInAllMaps() CaretAssertVectorIndex(m_mapContent, i); m_mapContent[i]->m_rgbaValid = false; } + + /* + * Force recreation of matrix so that it receives updates to coloring + * and in particular, matrix grid outline coloring + */ + m_matrixGraphicsPrimitive.reset(); + m_matrixGraphicsOutlinePrimitive.reset(); + invalidateHistogramChartColoring(); } /** @@ -1403,12 +1474,9 @@ CiftiMappableDataFile::getFileData(std::vector& data) const * * @param rgba * RGBA for file's matrix content. - * @param paletteFile - * File containg palettes for mapping data to RGBA. */ void -CiftiMappableDataFile::getMatrixRGBA(std::vector &rgba, - PaletteFile *paletteFile) +CiftiMappableDataFile::getMatrixRGBA(std::vector &rgba) { std::vector data; getFileData(data); @@ -1418,10 +1486,7 @@ CiftiMappableDataFile::getMatrixRGBA(std::vector &rgba, CaretAssert(paletteColorMapping); CaretPointer fastStatistics(new FastStatistics()); - const AString paletteName = paletteColorMapping->getSelectedPaletteName(); - const Palette* palette = paletteFile->getPaletteByName(paletteName); - if (( ! data.empty()) - && (palette != NULL)) { + if ( ! data.empty()) { fastStatistics->update(&data[0], data.size()); @@ -1429,8 +1494,8 @@ CiftiMappableDataFile::getMatrixRGBA(std::vector &rgba, rgba.resize(data.size() * 4); NodeAndVoxelColoring::colorScalarsWithPalette(fastStatistics, paletteColorMapping, - palette, &data[0], + paletteColorMapping, &data[0], data.size(), &rgba[0]); @@ -1442,6 +1507,421 @@ CiftiMappableDataFile::getMatrixRGBA(std::vector &rgba, } } +/** + * @return The graphics primitive containing the matrix representation of the file. + * All cells are of dimension 1.0 x 1.0 + * + * @param matrixViewMode + * The matrix visualization mode (upper/lower). + * @param gridMode + * The grid mode (filled or outline) + */ +GraphicsPrimitiveV3fC4f* +CiftiMappableDataFile::getMatrixChartingGraphicsPrimitive(const ChartTwoMatrixTriangularViewingModeEnum::Enum matrixViewMode, + const MatrixGridMode gridMode) const +{ + EventCaretPreferencesGet preferencesEvent; + EventManager::get()->sendEvent(preferencesEvent.getPointer()); + CaretPreferences* caretPreferences = preferencesEvent.getCaretPreferences(); + uint8_t gridByteRGBA[4] = { 0, 0, 0, 0 }; + + GraphicsPrimitiveV3fC4f* matrixPrimitive = NULL; + switch (gridMode) { + case MatrixGridMode::FILLED: + matrixPrimitive = m_matrixGraphicsPrimitive.get(); + break; + case MatrixGridMode::OUTLINE: + caretPreferences->getBackgroundAndForegroundColors()->getColorChartMatrixGridLines(gridByteRGBA); + matrixPrimitive = m_matrixGraphicsOutlinePrimitive.get(); + + if ((gridByteRGBA[0] != m_previousMatrixGridRGBA[0]) + || (gridByteRGBA[1] != m_previousMatrixGridRGBA[1]) + || (gridByteRGBA[2] != m_previousMatrixGridRGBA[2]) + || (gridByteRGBA[3] != m_previousMatrixGridRGBA[3])) { + matrixPrimitive = NULL; + m_previousMatrixGridRGBA[0] = gridByteRGBA[0]; + m_previousMatrixGridRGBA[1] = gridByteRGBA[1]; + m_previousMatrixGridRGBA[2] = gridByteRGBA[2]; + m_previousMatrixGridRGBA[3] = gridByteRGBA[3]; + } + break; + } + + if (matrixPrimitive == NULL) { + int32_t numberOfRows = 0; + int32_t numberOfColumns = 0; + std::vector matrixRGBA; + if (getMatrixForChartingRGBA(numberOfRows, numberOfColumns, matrixRGBA)) { + const int32_t numberOfCells = numberOfRows * numberOfColumns; + if (numberOfCells > 0) { + switch (gridMode) { + case MatrixGridMode::FILLED: + matrixPrimitive = GraphicsPrimitive::newPrimitiveV3fC4f(GraphicsPrimitive::PrimitiveType::OPENGL_TRIANGLES); + matrixPrimitive->reserveForNumberOfVertices(numberOfCells * 6); // 2 triangles per cell, 3 vertices per triangle + break; + case MatrixGridMode::OUTLINE: + /* Lines are used around each cell to simplify upper/lower triangular options */ + matrixPrimitive = GraphicsPrimitive::newPrimitiveV3fC4f(GraphicsPrimitive::PrimitiveType::OPENGL_LINES); + matrixPrimitive->reserveForNumberOfVertices(numberOfCells * 8); // 4 lines per cell, 2 vertices per line + break; + } + matrixPrimitive->setUsageTypeAll(GraphicsPrimitive::UsageType::MODIFIED_ONCE_DRAWN_MANY_TIMES); + + /* + * RGBA for grid outline + */ + float cellOutlineRGBA[4] = { 1.0, 0.0, 0.0, 1.0 }; + if (caretPreferences != NULL) { + cellOutlineRGBA[0] = static_cast(gridByteRGBA[0]) / 255.0f; + cellOutlineRGBA[1] = static_cast(gridByteRGBA[1]) / 255.0f; + cellOutlineRGBA[2] = static_cast(gridByteRGBA[2]) / 255.0f; + cellOutlineRGBA[3] = 1.0; + } + + /* + * Alpha zero for cells that are "not drawn" + */ + const float cellNotDrawRGBA[4] = { 0.0f, 0.0f, 0.0f, 0.0f }; + + /* + * NOTE: All matrix cells receive coloring, event those that are + * not displayed due to the triangular view selection. + * The reason is that it greatly simplifies identification as + * one can derive the row and column from the primitive index. + * Using triangles also simplifies identification. Lastly, since + * OpenGL buffers are used, drawing is very fast. + */ + int32_t rgbaOffset = 0; + const float cellHeight = 1.0; + const float cellWidth = 1.0; + float cellY = (numberOfRows - 1) * cellHeight; + for (int32_t rowIndex = 0; rowIndex < numberOfRows; rowIndex++) { + float cellX = 0; + for (int32_t columnIndex = 0; columnIndex < numberOfColumns; columnIndex++) { + CaretAssertVectorIndex(matrixRGBA, rgbaOffset+3); + const float* rgba = &matrixRGBA[rgbaOffset]; + rgbaOffset += 4; + + bool drawCellFlag = true; + if (matrixViewMode != ChartTwoMatrixTriangularViewingModeEnum::MATRIX_VIEW_FULL) { + if (numberOfRows == numberOfColumns) { + drawCellFlag = false; + switch (matrixViewMode) { + case ChartTwoMatrixTriangularViewingModeEnum::MATRIX_VIEW_FULL: + break; + case ChartTwoMatrixTriangularViewingModeEnum::MATRIX_VIEW_FULL_NO_DIAGONAL: + if (rowIndex != columnIndex) { + drawCellFlag = true; + } + break; + case ChartTwoMatrixTriangularViewingModeEnum::MATRIX_VIEW_LOWER_NO_DIAGONAL: + if (rowIndex > columnIndex) { + drawCellFlag = true; + } + break; + case ChartTwoMatrixTriangularViewingModeEnum::MATRIX_VIEW_UPPER_NO_DIAGONAL: + if (rowIndex < columnIndex) { + drawCellFlag = true; + } + break; + } + } + else { + drawCellFlag = true; + + /* + * Diagonals for non-square matrices not allowed + */ + const bool allowNonSquareMatrixDiagonalsFlag = false; + if (allowNonSquareMatrixDiagonalsFlag) { + drawCellFlag = false; + const float slope = static_cast(numberOfRows) / static_cast(numberOfColumns); + const int32_t diagonalRow = static_cast(slope * columnIndex); + + switch (matrixViewMode) { + case ChartTwoMatrixTriangularViewingModeEnum::MATRIX_VIEW_FULL: + drawCellFlag = true; + break; + case ChartTwoMatrixTriangularViewingModeEnum::MATRIX_VIEW_FULL_NO_DIAGONAL: + if (rowIndex != diagonalRow) { + drawCellFlag = true; + } + break; + case ChartTwoMatrixTriangularViewingModeEnum::MATRIX_VIEW_LOWER_NO_DIAGONAL: + if (rowIndex > diagonalRow) { + drawCellFlag = true; + } + break; + case ChartTwoMatrixTriangularViewingModeEnum::MATRIX_VIEW_UPPER_NO_DIAGONAL: + if (rowIndex < diagonalRow) { + drawCellFlag = true; + } + break; + } + } + } + } + + switch (gridMode) { + case MatrixGridMode::FILLED: + { + const float* cellRGBA = (drawCellFlag ? rgba : cellNotDrawRGBA); + matrixPrimitive->addVertex(cellX, cellY + cellHeight, 0.0, cellRGBA); + matrixPrimitive->addVertex(cellX, cellY, 0.0, cellRGBA); + matrixPrimitive->addVertex(cellX + cellWidth, cellY, 0.0, cellRGBA); + + matrixPrimitive->addVertex(cellX, cellY + cellHeight, 0.0, cellRGBA); + matrixPrimitive->addVertex(cellX + cellWidth, cellY, 0.0, cellRGBA); + matrixPrimitive->addVertex(cellX + cellWidth, cellY + cellHeight, 0.0, cellRGBA); + } + break; + case MatrixGridMode::OUTLINE: + { + const float* cellRGBA = (drawCellFlag ? cellOutlineRGBA : cellNotDrawRGBA); + matrixPrimitive->addVertex(cellX, cellY, 0.0, cellRGBA); + matrixPrimitive->addVertex(cellX + cellWidth, cellY, 0.0, cellRGBA); + + matrixPrimitive->addVertex(cellX + cellWidth, cellY, 0.0, cellRGBA); + matrixPrimitive->addVertex(cellX + cellWidth, cellY + cellHeight, 0.0, cellRGBA); + + matrixPrimitive->addVertex(cellX + cellWidth, cellY + cellHeight, 0.0, cellRGBA); + matrixPrimitive->addVertex(cellX, cellY + cellHeight, 0.0, cellRGBA); + + matrixPrimitive->addVertex(cellX, cellY + cellHeight, 0.0, cellRGBA); + matrixPrimitive->addVertex(cellX, cellY, 0.0, cellRGBA); + } + break; + } + + cellX += cellWidth; + } + + cellY -= cellHeight; + } + } + } + } + + switch (gridMode) { + case MatrixGridMode::FILLED: + if (matrixPrimitive != m_matrixGraphicsPrimitive.get()) { + m_matrixGraphicsPrimitive.reset(matrixPrimitive); + } + break; + case MatrixGridMode::OUTLINE: + if (matrixPrimitive != m_matrixGraphicsOutlinePrimitive.get()) { + m_matrixGraphicsOutlinePrimitive.reset(matrixPrimitive); + } + break; + } + + return matrixPrimitive; +} + + +/** + * Get the matrix RGBA coloring for this matrix data creator. + * + * @param numberOfRowsOut + * Number of rows in the coloring matrix. + * @param numberOfColumnsOut + * Number of rows in the coloring matrix. + * @param rgbaOut + * RGBA coloring output with number of elements + * (numberOfRowsOut * numberOfColumnsOut * 4). + * @return + * True if data output data is valid, else false. + */ +bool +CiftiMappableDataFile::getMatrixForChartingRGBA(int32_t& numberOfRowsOut, + int32_t& numberOfColumnsOut, + std::vector& rgbaOut) const +{ + bool useMapFileHelperFlag = false; + bool useMatrixFileHelperFlag = false; + + std::vector parcelReorderedRowIndices; + + switch (getDataFileType()) { + case DataFileTypeEnum::CONNECTIVITY_DENSE: + break; + case DataFileTypeEnum::CONNECTIVITY_DENSE_DYNAMIC: + break; + case DataFileTypeEnum::CONNECTIVITY_DENSE_LABEL: + break; + case DataFileTypeEnum::CONNECTIVITY_DENSE_PARCEL: + break; + case DataFileTypeEnum::CONNECTIVITY_DENSE_SCALAR: + break; + case DataFileTypeEnum::CONNECTIVITY_DENSE_TIME_SERIES: + break; + case DataFileTypeEnum::CONNECTIVITY_PARCEL: + { + useMatrixFileHelperFlag = true; + + const CiftiConnectivityMatrixParcelFile* parcelConnFile = dynamic_cast(this); + CaretAssert(parcelConnFile); + if (parcelConnFile != NULL) { + CiftiParcelLabelFile* parcelLabelReorderingFile = NULL; + int32_t parcelLabelFileMapIndex = -1; + bool reorderingEnabledFlag = false; + + std::vector parcelLabelFiles; + parcelConnFile->getSelectedParcelLabelFileAndMapForReordering(parcelLabelFiles, + parcelLabelReorderingFile, + parcelLabelFileMapIndex, + reorderingEnabledFlag); + + if (reorderingEnabledFlag) { + const CiftiParcelReordering* parcelReordering = parcelConnFile->getParcelReordering(parcelLabelReorderingFile, + parcelLabelFileMapIndex); + if (parcelReordering != NULL) { + parcelReorderedRowIndices = parcelReordering->getReorderedParcelIndices(); + } + } + } + } + break; + case DataFileTypeEnum::CONNECTIVITY_PARCEL_DENSE: + break; + case DataFileTypeEnum::CONNECTIVITY_PARCEL_LABEL: + { + useMapFileHelperFlag = true; + + const CiftiParcelLabelFile* parcelLabelFile = dynamic_cast(this); + CaretAssert(parcelLabelFile); + if (parcelLabelFile != NULL) { + CiftiParcelLabelFile* parcelLabelReorderingFile = NULL; + int32_t parcelLabelFileMapIndex = -1; + bool reorderingEnabledFlag = false; + + std::vector parcelLabelFiles; + parcelLabelFile->getSelectedParcelLabelFileAndMapForReordering(parcelLabelFiles, + parcelLabelReorderingFile, + parcelLabelFileMapIndex, + reorderingEnabledFlag); + + if (reorderingEnabledFlag) { + const CiftiParcelReordering* parcelReordering = parcelLabelFile->getParcelReordering(parcelLabelReorderingFile, + parcelLabelFileMapIndex); + if (parcelReordering != NULL) { + parcelReorderedRowIndices = parcelReordering->getReorderedParcelIndices(); + } + } + } + } + break; + case DataFileTypeEnum::CONNECTIVITY_PARCEL_SCALAR: + { + useMapFileHelperFlag = true; + + const CiftiParcelScalarFile* parcelScalarFile = dynamic_cast(this); + CaretAssert(parcelScalarFile); + if (parcelScalarFile != NULL) { + CiftiParcelLabelFile* parcelLabelReorderingFile = NULL; + int32_t parcelLabelFileMapIndex = -1; + bool reorderingEnabledFlag = false; + + std::vector parcelLabelFiles; + parcelScalarFile->getSelectedParcelLabelFileAndMapForReordering(parcelLabelFiles, + parcelLabelReorderingFile, + parcelLabelFileMapIndex, + reorderingEnabledFlag); + + if (reorderingEnabledFlag) { + const CiftiParcelReordering* parcelReordering = parcelScalarFile->getParcelReordering(parcelLabelReorderingFile, + parcelLabelFileMapIndex); + if (parcelReordering != NULL) { + parcelReorderedRowIndices = parcelReordering->getReorderedParcelIndices(); + } + } + } + } + break; + case DataFileTypeEnum::CONNECTIVITY_PARCEL_SERIES: + { + useMapFileHelperFlag = true; + + const CiftiParcelSeriesFile* parcelSeriesFile = dynamic_cast(this); + CaretAssert(parcelSeriesFile); + if (parcelSeriesFile != NULL) { + CiftiParcelLabelFile* parcelLabelReorderingFile = NULL; + int32_t parcelLabelFileMapIndex = -1; + bool reorderingEnabledFlag = false; + std::vector parcelLabelFiles; + parcelSeriesFile->getSelectedParcelLabelFileAndMapForReordering(parcelLabelFiles, + parcelLabelReorderingFile, + parcelLabelFileMapIndex, + reorderingEnabledFlag); + + if (reorderingEnabledFlag) { + const CiftiParcelReordering* parcelReordering = parcelSeriesFile->getParcelReordering(parcelLabelReorderingFile, + parcelLabelFileMapIndex); + if (parcelReordering != NULL) { + parcelReorderedRowIndices = parcelReordering->getReorderedParcelIndices(); + } + } + } + } + break; + case DataFileTypeEnum::CONNECTIVITY_SCALAR_DATA_SERIES: + useMatrixFileHelperFlag = true; + break; + case DataFileTypeEnum::ANNOTATION: + break; + case DataFileTypeEnum::BORDER: + break; + case DataFileTypeEnum::CONNECTIVITY_FIBER_ORIENTATIONS_TEMPORARY: + break; + case DataFileTypeEnum::CONNECTIVITY_FIBER_TRAJECTORY_TEMPORARY: + break; + case DataFileTypeEnum::FOCI: + break; + case DataFileTypeEnum::IMAGE: + break; + case DataFileTypeEnum::LABEL: + break; + case DataFileTypeEnum::METRIC: + break; + case DataFileTypeEnum::PALETTE: + break; + case DataFileTypeEnum::RGBA: + break; + case DataFileTypeEnum::SCENE: + break; + case DataFileTypeEnum::SPECIFICATION: + break; + case DataFileTypeEnum::SURFACE: + break; + case DataFileTypeEnum::UNKNOWN: + break; + case DataFileTypeEnum::VOLUME: + break; + } + + if (( ! useMapFileHelperFlag) + && ( ! useMatrixFileHelperFlag)) { + CaretAssertMessage(0, "Trying to get matrix from a file that does not support matrix display"); + return false; + } + + bool validDataFlag = false; + if (useMapFileHelperFlag) { + validDataFlag = helpMapFileLoadChartDataMatrixRGBA(numberOfRowsOut, + numberOfColumnsOut, + parcelReorderedRowIndices, + rgbaOut); + } + else if (useMatrixFileHelperFlag) { + validDataFlag = helpMatrixFileLoadChartDataMatrixRGBA(numberOfRowsOut, + numberOfColumnsOut, + parcelReorderedRowIndices, + rgbaOut); + } + + return validDataFlag; +} + /** * Get the dimensions of the file (rows and columns). * @@ -1508,12 +1988,22 @@ CiftiMappableDataFile::getMapHistogram(const int32_t mapIndex) if (isMappedWithPalette()) { CaretAssertVectorIndex(m_mapContent, mapIndex); + int32_t numberOfBuckets = 0; + switch (getPaletteNormalizationMode()) { + case PaletteNormalizationModeEnum::NORMALIZATION_ALL_MAP_DATA: + numberOfBuckets = getFileHistogramNumberOfBuckets(); + break; + case PaletteNormalizationModeEnum::NORMALIZATION_SELECTED_MAP_DATA: + numberOfBuckets = getMapPaletteColorMapping(mapIndex)->getHistogramNumberOfBuckets(); + break; + } - if ( ! m_mapContent[mapIndex]->isHistogramValid()) { + if ( ! m_mapContent[mapIndex]->isHistogramValid(numberOfBuckets)) { std::vector data; getMapData(mapIndex, data); - m_mapContent[mapIndex]->updateHistogram(data); + m_mapContent[mapIndex]->updateHistogram(numberOfBuckets, + data); } histogramOut = m_mapContent[mapIndex]->m_histogram; @@ -1556,7 +2046,17 @@ CiftiMappableDataFile::getMapHistogram(const int32_t mapIndex, if (isMappedWithPalette()) { CaretAssertVectorIndex(m_mapContent, mapIndex); - if ( ! m_mapContent[mapIndex]->isHistogramLimitedValuesValid(mostPositiveValueInclusive, + int32_t numberOfBuckets = 0; + switch (getPaletteNormalizationMode()) { + case PaletteNormalizationModeEnum::NORMALIZATION_ALL_MAP_DATA: + numberOfBuckets = getFileHistogramNumberOfBuckets(); + break; + case PaletteNormalizationModeEnum::NORMALIZATION_SELECTED_MAP_DATA: + numberOfBuckets = getMapPaletteColorMapping(mapIndex)->getHistogramNumberOfBuckets(); + break; + } + if ( ! m_mapContent[mapIndex]->isHistogramLimitedValuesValid(numberOfBuckets, + mostPositiveValueInclusive, leastPositiveValueInclusive, leastNegativeValueInclusive, mostNegativeValueInclusive, @@ -1564,7 +2064,8 @@ CiftiMappableDataFile::getMapHistogram(const int32_t mapIndex, std::vector data; getMapData(mapIndex, data); - m_mapContent[mapIndex]->updateHistogramLimitedValues(data, + m_mapContent[mapIndex]->updateHistogramLimitedValues(numberOfBuckets, + data, mostPositiveValueInclusive, leastPositiveValueInclusive, leastNegativeValueInclusive, @@ -1633,13 +2134,28 @@ CiftiMappableDataFile::getFileFastStatistics() const Histogram* CiftiMappableDataFile::getFileHistogram() { - if (m_fileHistogram == NULL) { + bool updateHistogramFlag = false; + const int32_t numberOfBuckets = getFileHistogramNumberOfBuckets(); + if (m_fileHistogram != NULL) { + if (numberOfBuckets != m_fileHistogramNumberOfBuckets) { + updateHistogramFlag = true; + } + } + else { + updateHistogramFlag = true; + } + if (updateHistogramFlag) { std::vector fileData; getFileData(fileData); + if ( ! fileData.empty()) { - m_fileHistogram.grabNew(new Histogram()); - m_fileHistogram->update(&fileData[0], + if (m_fileHistogram == NULL) { + m_fileHistogram.grabNew(new Histogram(numberOfBuckets)); + } + m_fileHistogram->update(numberOfBuckets, + &fileData[0], fileData.size()); + m_fileHistogramNumberOfBuckets = numberOfBuckets; } } return m_fileHistogram; @@ -1672,8 +2188,10 @@ CiftiMappableDataFile::getFileHistogram(const float mostPositiveValueInclusive, const bool includeZeroValues) { bool updateHistogramFlag = false; + const int32_t numberOfBuckets = getFileHistogramNumberOfBuckets(); if (m_fileHistorgramLimitedValues != NULL) { - if ((mostPositiveValueInclusive != m_fileHistogramLimitedValuesMostPositiveValueInclusive) + if ((numberOfBuckets != m_fileHistogramLimitedValuesNumberOfBuckets) + ||(mostPositiveValueInclusive != m_fileHistogramLimitedValuesMostPositiveValueInclusive) || (leastPositiveValueInclusive != m_fileHistogramLimitedValuesLeastPositiveValueInclusive) || (leastNegativeValueInclusive != m_fileHistogramLimitedValuesLeastNegativeValueInclusive) || (mostNegativeValueInclusive != m_fileHistogramLimitedValuesMostNegativeValueInclusive) @@ -1692,7 +2210,8 @@ CiftiMappableDataFile::getFileHistogram(const float mostPositiveValueInclusive, if (m_fileHistorgramLimitedValues == NULL) { m_fileHistorgramLimitedValues.grabNew(new Histogram()); } - m_fileHistorgramLimitedValues->update(&fileData[0], + m_fileHistorgramLimitedValues->update(numberOfBuckets, + &fileData[0], fileData.size(), mostPositiveValueInclusive, leastPositiveValueInclusive, @@ -1700,6 +2219,7 @@ CiftiMappableDataFile::getFileHistogram(const float mostPositiveValueInclusive, mostNegativeValueInclusive, includeZeroValues); + m_fileHistogramLimitedValuesNumberOfBuckets = numberOfBuckets; m_fileHistogramLimitedValuesMostPositiveValueInclusive = mostPositiveValueInclusive; m_fileHistogramLimitedValuesLeastPositiveValueInclusive = leastPositiveValueInclusive; m_fileHistogramLimitedValuesLeastNegativeValueInclusive = leastNegativeValueInclusive; @@ -1941,7 +2461,7 @@ CiftiMappableDataFile::getMapLabelTable(const int32_t mapIndex) const * vector is assumed to be the default mode. */ void -CiftiMappableDataFile::getPaletteNormalizationModesSupported(std::vector& modesSupportedOut) +CiftiMappableDataFile::getPaletteNormalizationModesSupported(std::vector& modesSupportedOut) const { modesSupportedOut = m_paletteNormalizationModesSupported; } @@ -1952,12 +2472,9 @@ CiftiMappableDataFile::getPaletteNormalizationModesSupported(std::vectorupdateColoring(data, - paletteFile, statistics); } else if (isMappedWithLabelTable()) { m_mapContent[mapIndex]->updateColoring(data, - paletteFile, NULL); } else { CaretAssert(0); } + + /* + * Force recreation of matrix so that it receives updates to coloring. + */ + + invalidateHistogramChartColoring(); + m_matrixGraphicsPrimitive.reset(); + m_matrixGraphicsOutlinePrimitive.reset(); } /** @@ -2373,8 +2893,6 @@ CiftiMappableDataFile::getVoxelSpaceBoundingBox(BoundingBox& boundingBoxOut) con /** * Get the voxel colors for a slice in the map. * - * @param paletteFile - * The palette file. * @param mapIndex * Index of the map. * @param slicePlane @@ -2392,8 +2910,7 @@ CiftiMappableDataFile::getVoxelSpaceBoundingBox(BoundingBox& boundingBoxOut) con * Number of voxels with alpha greater than zero */ int64_t -CiftiMappableDataFile::getVoxelColorsForSliceInMap(const PaletteFile* paletteFile, - const int32_t mapIndex, +CiftiMappableDataFile::getVoxelColorsForSliceInMap(const int32_t mapIndex, const VolumeSliceViewPlaneEnum::Enum slicePlane, const int64_t sliceIndex, const DisplayGroupEnum::Enum displayGroup, @@ -2410,8 +2927,7 @@ CiftiMappableDataFile::getVoxelColorsForSliceInMap(const PaletteFile* paletteFil if (isMapColoringValid(mapIndex) == false) { CiftiMappableDataFile* nonConstThis = const_cast(this); - nonConstThis->updateScalarColoringForMap(mapIndex, - paletteFile); + nonConstThis->updateScalarColoringForMap(mapIndex); } int64_t dimI, dimJ, dimK, dimTime, dimComp; @@ -2689,24 +3205,138 @@ CiftiMappableDataFile::getVoxelColorsForSliceInMap(const PaletteFile* paletteFil * Number of voxels with alpha greater than zero */ int64_t -CiftiMappableDataFile::getVoxelColorsForSliceInMap(const int32_t /*mapIndex*/, - const int64_t[] /*firstVoxelIJK[3]*/, - const int64_t[] /*rowStepIJK[3]*/, - const int64_t[] /*columnStepIJK[3]*/, - const int64_t /*numberOfRows*/, - const int64_t /*numberOfColumns*/, - const DisplayGroupEnum::Enum /*displayGroup*/, - const int32_t /*tabIndex*/, - uint8_t* /*rgbaOut*/) const +CiftiMappableDataFile::getVoxelColorsForSliceInMap(const int32_t mapIndex, + const int64_t firstVoxelIJK[3], + const int64_t rowStepIJK[3], + const int64_t columnStepIJK[3], + const int64_t numberOfRows, + const int64_t numberOfColumns, + const DisplayGroupEnum::Enum displayGroup, + const int32_t tabIndex, + uint8_t* rgbaOut) const { - return 0; + CaretAssertVectorIndex(m_mapContent, + mapIndex); + + if (isMapColoringValid(mapIndex) == false) { + CiftiMappableDataFile* nonConstThis = const_cast(this); + nonConstThis->updateScalarColoringForMap(mapIndex); + } + + const int64_t mapRgbaCount = m_mapContent[mapIndex]->m_rgba.size(); + + + /* + * RGBA size will be zero if no data has been loaded for a CIFTI + * matrix type file (user clicking brainordinate). + */ + if (mapRgbaCount <= 0) { + return 0; + } + + const uint8_t* mapRGBA = &m_mapContent[mapIndex]->m_rgba[0]; + + CaretAssert(m_voxelIndicesToOffset); + + /* + * Data values are only needed when a label volume + * is being drawn so that we can determine if the + * label is displayed. + */ + std::vector dataValues; + const GiftiLabelTable* labelTable = (isMappedWithLabelTable() + ? getMapLabelTable(mapIndex) + : NULL); + if (isMappedWithLabelTable()) { + CaretAssert(labelTable); + getMapData(mapIndex, + dataValues); + } + + int64_t rowIJK[3] = { firstVoxelIJK[0], firstVoxelIJK[1], firstVoxelIJK[2] }; + uint8_t rgba[4] = { 0, 0, 0, 0 }; + int64_t rgbaOutIndex4 = 0; + + int64_t validVoxelCount = 0; + + for (int32_t iRow = 0; iRow < numberOfRows; iRow++) { + int64_t ijk[3] = { rowIJK[0], rowIJK[1], rowIJK[2] }; + + for (int32_t iCol = 0; iCol < numberOfColumns; iCol++) { + rgba[3] = 0; + + const int64_t dataOffset = m_voxelIndicesToOffset->getOffsetForIndices(ijk[0], ijk[1], ijk[2]); + if (dataOffset >= 0) { + const int64_t dataOffset4 = dataOffset * 4; + CaretAssert(dataOffset4 < mapRgbaCount); + + rgba[0] = mapRGBA[dataOffset4]; + rgba[1] = mapRGBA[dataOffset4+1]; + rgba[2] = mapRGBA[dataOffset4+2]; + + /* + * A negative value for alpha indicates "do not draw". + * Since unsigned bytes do not have negative values, + * change the value to zero (which indicates "transparent"). + */ + float alpha = mapRGBA[dataOffset4+3]; + if (alpha < 0.0) { + alpha = 0.0; + } + + if (alpha > 0.0) { + if (labelTable != NULL) { + /* + * For label data, verify that the label is displayed. + * If NOT displayed, zero out the alpha value to + * prevent display of the data. + */ + CaretAssertVectorIndex(dataValues, dataOffset); + const int32_t dataValue = dataValues[dataOffset]; + const GiftiLabel* label = labelTable->getLabel(dataValue); + if (label != NULL) { + const GroupAndNameHierarchyItem* item = label->getGroupNameSelectionItem(); + CaretAssert(item); + if (item->isSelected(displayGroup, tabIndex) == false) { + alpha = 0.0; + } + } + } + + } + + if (alpha > 0.0) { + ++validVoxelCount; + } + rgba[3] = (alpha * 255.0); + } + + rgbaOut[rgbaOutIndex4] = rgba[0]; + rgbaOut[rgbaOutIndex4+1] = rgba[1]; + rgbaOut[rgbaOutIndex4+2] = rgba[2]; + rgbaOut[rgbaOutIndex4+3] = rgba[3]; + rgbaOutIndex4 += 4; + + if (rgba[3] > 0) { + validVoxelCount++; + } + + ijk[0] += columnStepIJK[0]; + ijk[1] += columnStepIJK[1]; + ijk[2] += columnStepIJK[2]; + } + + rowIJK[0] += rowStepIJK[0]; + rowIJK[1] += rowStepIJK[1]; + rowIJK[2] += rowStepIJK[2]; + } + + return validVoxelCount; } /** * Get the voxel colors for a sub slice in the map. * - * @param paletteFile - * The palette file. * @param mapIndex * Index of the map. * @param slicePlane @@ -2730,8 +3360,7 @@ CiftiMappableDataFile::getVoxelColorsForSliceInMap(const int32_t /*mapIndex*/, * Number of voxels with alpha greater than zero */ int64_t -CiftiMappableDataFile::getVoxelColorsForSubSliceInMap(const PaletteFile* paletteFile, - const int32_t mapIndex, +CiftiMappableDataFile::getVoxelColorsForSubSliceInMap(const int32_t mapIndex, const VolumeSliceViewPlaneEnum::Enum slicePlane, const int64_t sliceIndex, const int64_t firstCornerVoxelIndex[3], @@ -2751,8 +3380,7 @@ CiftiMappableDataFile::getVoxelColorsForSubSliceInMap(const PaletteFile* palette if (isMapColoringValid(mapIndex) == false) { CiftiMappableDataFile* nonConstThis = const_cast(this); - nonConstThis->updateScalarColoringForMap(mapIndex, - paletteFile); + nonConstThis->updateScalarColoringForMap(mapIndex); } const int64_t iStart = firstCornerVoxelIndex[0]; @@ -3163,8 +3791,6 @@ CiftiMappableDataFile::getVoxelColorsForSubSliceInMap(const PaletteFile* palette * * This will work for non-label data. * - * @param paletteFile - * The palette file. * @param dataForMap * Data for the map. * @param indexIn1 @@ -3183,8 +3809,7 @@ CiftiMappableDataFile::getVoxelColorsForSubSliceInMap(const PaletteFile* palette * Output containing RGBA values for voxel at the given indices. */ void -CiftiMappableDataFile::getVoxelColorInMapForLabelData(const PaletteFile* paletteFile, - const std::vector& dataForMap, +CiftiMappableDataFile::getVoxelColorInMapForLabelData(const std::vector& dataForMap, const int64_t indexIn1, const int64_t indexIn2, const int64_t indexIn3, @@ -3193,8 +3818,7 @@ CiftiMappableDataFile::getVoxelColorInMapForLabelData(const PaletteFile* palette const int32_t tabIndex, uint8_t rgbaOut[4]) const { - getVoxelColorInMap(paletteFile, - indexIn1, + getVoxelColorInMap(indexIn1, indexIn2, indexIn3, mapIndex, @@ -3238,8 +3862,6 @@ CiftiMappableDataFile::getVoxelColorInMapForLabelData(const PaletteFile* palette * * @see getVoxelColorInMapForLabelData * - * @param paletteFile - * The palette file. * @param indexIn1 * First dimension (i). * @param indexIn2 @@ -3256,14 +3878,13 @@ CiftiMappableDataFile::getVoxelColorInMapForLabelData(const PaletteFile* palette * Output containing RGBA values for voxel at the given indices. */ void -CiftiMappableDataFile::getVoxelColorInMap(const PaletteFile* paletteFile, - const int64_t indexIn1, - const int64_t indexIn2, - const int64_t indexIn3, - const int64_t mapIndex, +CiftiMappableDataFile::getVoxelColorInMap(const int64_t indexIn1, + const int64_t indexIn2, + const int64_t indexIn3, + const int64_t mapIndex, const DisplayGroupEnum::Enum /*displayGroup*/, const int32_t /*tabIndex*/, - uint8_t rgbaOut[4]) const + uint8_t rgbaOut[4]) const { rgbaOut[0] = 0; rgbaOut[1] = 0; @@ -3272,8 +3893,7 @@ CiftiMappableDataFile::getVoxelColorInMap(const PaletteFile* paletteFile, if ( ! isMapColoringValid(mapIndex)) { CiftiMappableDataFile* nonConstThis = const_cast(this); - nonConstThis->updateScalarColoringForMap(mapIndex, - paletteFile); + nonConstThis->updateScalarColoringForMap(mapIndex); } CaretAssert(m_voxelIndicesToOffset); @@ -3644,11 +4264,6 @@ CiftiMappableDataFile::getMapSurfaceNodeValue(const int32_t mapIndex, std::vector mapData; getMapData(mapIndex, mapData); - -// if (parcelIndex < static_cast(mapData.size())) { -// textValueOut += (" " -// + AString::number(mapData[parcelIndex])); -// } } } @@ -4110,24 +4725,6 @@ CiftiMappableDataFile::getSurfaceNodeIdentificationForMaps(const std::vectorm_rgbaValid) { - updateScalarColoringForMap(mapIndex, - paletteFile); + updateScalarColoringForMap(mapIndex); } std::vector dataIndicesForNodes; @@ -4615,14 +5212,6 @@ CiftiMappableDataFile::getMapVolumeVoxelValue(const int32_t mapIndex, CaretAssertVectorIndex(parcels, parcelMapIndex); textValueOut = parcels[parcelMapIndex].m_name; - -// std::vector mapData; -// getMapData(mapIndex, mapData); -// -// if (parcelMapIndex < static_cast(mapData.size())) { -// textValueOut += (" " -// + AString::number(mapData[parcelMapIndex])); -// } } if (parcelMapIndex >= 0) { @@ -4781,14 +5370,7 @@ CiftiMappableDataFile::getMapVolumeVoxelValues(const std::vector mapInd textValueOut += (getMapName(*mapIter) + " "); } -// const AString networkName = getMapName(mapIndex); -// -// textValueOut = ("Area: " -// + areaName -// + ", Network: " -// + networkName); return true; - } else { switch (ciftiXML.getMappingType(m_dataMappingDirectionForCiftiXML)) { @@ -4855,14 +5437,6 @@ CiftiMappableDataFile::getMapVolumeVoxelValues(const std::vector mapInd CaretAssertVectorIndex(parcels, parcelMapIndex); textValueOut = parcels[parcelMapIndex].m_name; - - // std::vector mapData; - // getMapData(mapIndex, mapData); - // - // if (parcelMapIndex < static_cast(mapData.size())) { - // textValueOut += (" " - // + AString::number(mapData[parcelMapIndex])); - // } } for (std::vector::const_iterator mapIter = mapIndices.begin(); @@ -5117,23 +5691,6 @@ CiftiMappableDataFile::getVolumeVoxelIdentificationForMaps(const std::vectorgetCiftiXML(); - const AString badMapTypeMessage("Matrix charts supports only maps in columns at this time for LABEL and SCALAR data"); + const AString badMapTypeMessage("Matrix charts supports only maps in columns at this time for LABEL, SCALAR, and SERIES data"); if (useLabelTableFlag) { if (ciftiXML.getMappingType(CiftiXML::ALONG_ROW) != CiftiMappingType::LABELS) { CaretAssertMessage(0, badMapTypeMessage); @@ -6025,7 +6582,13 @@ CiftiMappableDataFile::helpMapFileLoadChartDataMatrixRGBA(int32_t& numberOfRowsO } if (usePaletteFlag) { - if (ciftiXML.getMappingType(CiftiXML::ALONG_ROW) != CiftiMappingType::SCALARS) { + if (ciftiXML.getMappingType(CiftiXML::ALONG_ROW) == CiftiMappingType::SCALARS) { + /* ok */ + } + else if (ciftiXML.getMappingType(CiftiXML::ALONG_ROW) == CiftiMappingType::SERIES) { + /* ok */ + } + else { CaretAssertMessage(0, badMapTypeMessage); CaretLogSevere(badMapTypeMessage); return false; @@ -6081,25 +6644,11 @@ CiftiMappableDataFile::helpMapFileLoadChartDataMatrixRGBA(int32_t& numberOfRowsO else if (usePaletteFlag) { const PaletteColorMapping* pcm = getMapPaletteColorMapping(iCol); CaretAssert(pcm); - const AString paletteName = pcm->getSelectedPaletteName(); - if (paletteName.isEmpty()) { - CaretLogSevere("No palette name for coloring matrix chart data."); - return false; - } - EventPaletteGetByName eventPaletteGetName(paletteName); - EventManager::get()->sendEvent(eventPaletteGetName.getPointer()); - const Palette* palette = eventPaletteGetName.getPalette(); - if (palette == NULL) { - CaretLogSevere("No palette named " - + paletteName - + " found for coloring matrix chart data."); - return false; - } NodeAndVoxelColoring::colorScalarsWithPalette(nonConstMapFile->getFileFastStatistics(), pcm, - palette, &columnData[0], + pcm, &columnData[0], numberOfRowsOut, &columnRGBA[0]); @@ -6195,20 +6744,6 @@ CiftiMappableDataFile::helpMatrixFileLoadChartDataMatrixRGBA(int32_t& numberOfRo const CiftiXML& ciftiXML = m_ciftiFile->getCiftiXML(); const PaletteColorMapping* pcm = ciftiXML.getFilePalette(); CaretAssert(pcm); - const AString paletteName = pcm->getSelectedPaletteName(); - if (paletteName.isEmpty()) { - CaretLogSevere("No palette name for coloring matrix chart data."); - return false; - } - EventPaletteGetByName eventPaletteGetName(paletteName); - EventManager::get()->sendEvent(eventPaletteGetName.getPointer()); - const Palette* palette = eventPaletteGetName.getPalette(); - if (palette == NULL) { - CaretLogSevere("No palette named " - + paletteName - + " found for coloring matrix chart data."); - return false; - } /* * Set up Fast Stats for files that use all data for @@ -6225,8 +6760,8 @@ CiftiMappableDataFile::helpMatrixFileLoadChartDataMatrixRGBA(int32_t& numberOfRo rgbaOut.resize(numRGBA); NodeAndVoxelColoring::colorScalarsWithPalette(fileFastStats, pcm, - palette, &data[0], + pcm, &data[0], numberOfData, &rgbaOut[0]); @@ -6240,12 +6775,274 @@ CiftiMappableDataFile::helpMatrixFileLoadChartDataMatrixRGBA(int32_t& numberOfRo return false; } +/** + * Get data from the file as requested in the given map file data selector. + * + * @param mapFileDataSelector + * Specifies selection of data. + * @param dataOut + * Output with data. Will be empty if data does not support the map file data selector. + */ +void +CiftiMappableDataFile::getDataForSelector(const MapFileDataSelector& mapFileDataSelector, + std::vector& dataOut) const +{ + dataOut.clear(); + + switch (mapFileDataSelector.getDataSelectionType()) { + case MapFileDataSelector::DataSelectionType::INVALID: + break; + case MapFileDataSelector::DataSelectionType::COLUMN_DATA: + { + const bool loadColumnDataFlag = false; + if (loadColumnDataFlag) { + const DataFileTypeEnum::Enum dataFileType = getDataFileType(); + if (dataFileType == DataFileTypeEnum::UNKNOWN) { + CaretMappableDataFile* mapFile = NULL; + AString mapFileName; + int32_t columnIndex = -1; + mapFileDataSelector.getColumnIndex(mapFile, + mapFileName, + columnIndex); + bool loadDataFlag = false; + if (mapFile != NULL) { + if (mapFile == dynamic_cast(this)) { + loadDataFlag = true; + } + } + else if (mapFileName.endsWith(getFileNameNoPath())) { + loadDataFlag = true; + } + if (loadDataFlag) { + if ((columnIndex >= 0) + && (columnIndex < m_ciftiFile->getNumberOfColumns())) { + const int32_t numberOfElementsInColumn = m_ciftiFile->getNumberOfRows(); + if (numberOfElementsInColumn > 0) { + dataOut.resize(numberOfElementsInColumn); + m_ciftiFile->getColumn(&dataOut[0], + columnIndex); + } + } + } + } + } + } + break; + case MapFileDataSelector::DataSelectionType::ROW_DATA: + { + const DataFileTypeEnum::Enum dataFileType = getDataFileType(); + if (dataFileType == DataFileTypeEnum::CONNECTIVITY_SCALAR_DATA_SERIES) { + CaretMappableDataFile* mapFile = NULL; + AString mapFileName; + int32_t rowIndex = -1; + mapFileDataSelector.getRowIndex(mapFile, + mapFileName, + rowIndex); + + bool loadDataFlag = false; + if (mapFile != NULL) { + if (mapFile == dynamic_cast(this)) { + loadDataFlag = true; + } + } + else if (mapFileName.endsWith(getFileNameNoPath())) { + loadDataFlag = true; + } + if (loadDataFlag) { + if ((rowIndex >= 0) + && (rowIndex < m_ciftiFile->getNumberOfRows())) { + const int32_t numberOfElementsInRow = m_ciftiFile->getNumberOfColumns(); + if (numberOfElementsInRow > 0) { + dataOut.resize(numberOfElementsInRow); + m_ciftiFile->getRow(&dataOut[0], + rowIndex); + } + } + } + } + } + break; + case MapFileDataSelector::DataSelectionType::SURFACE_VERTEX: + try { + StructureEnum::Enum structure = StructureEnum::INVALID; + int32_t numberOfVertices = -1; + int32_t vertexIndex = -1; + mapFileDataSelector.getSurfaceVertex(structure, + numberOfVertices, + vertexIndex); + + if (getMappingSurfaceNumberOfNodes(structure) == numberOfVertices) { + if ( ! getSeriesDataForSurfaceNode(structure, + vertexIndex, + dataOut)) { + dataOut.clear(); + } + } + } + catch (const DataFileException& dfe) { + CaretLogWarning("Exeception: " + + dfe.whatString()); + dataOut.clear(); + } + break; + case MapFileDataSelector::DataSelectionType::SURFACE_VERTICES_AVERAGE: + { + try { + StructureEnum::Enum structure = StructureEnum::INVALID; + int32_t surfaceNumberOfVertices = -1; + std::vector vertexIndices; + mapFileDataSelector.getSurfaceVertexAverage(structure, + surfaceNumberOfVertices, + vertexIndices); + + if (getMappingSurfaceNumberOfNodes(structure) == surfaceNumberOfVertices) { + const int32_t numberOfVertices = static_cast(vertexIndices.size()); + if (numberOfVertices > 0) { + std::vector dataSum; + int32_t dataSumSize = 0; + int32_t dataAverageCount = 0; + + std::vector data; + bool firstNodeFlag = true; + + for (int32_t i = 0; i < numberOfVertices; i++) { + if (getSeriesDataForSurfaceNode(structure, + vertexIndices[i], + data)) { + if (firstNodeFlag) { + firstNodeFlag = false; + + dataSumSize = static_cast(data.size()); + if (dataSumSize > 0) { + dataSum.resize(dataSumSize, + 0.0); + } + } + + CaretAssert(dataSumSize == static_cast(data.size())); + + for (int32_t j = 0; j < dataSumSize; j++) { + dataSum[j] += data[j]; + } + dataAverageCount++; + } + } + + if ((dataAverageCount > 0) + && (dataSumSize > 0)) { + dataOut.resize(dataSumSize); + for (int32_t k = 0; k < dataSumSize; k++) { + dataOut[k] = dataSum[k] / dataAverageCount; + } + } + } + } + } + catch (const DataFileException& dfe) { + CaretLogWarning("Exeception: " + + dfe.whatString()); + dataOut.clear(); + } + } + break; + case MapFileDataSelector::DataSelectionType::VOLUME_XYZ: + { + float xyz[3]; + mapFileDataSelector.getVolumeVoxelXYZ(xyz); + getSeriesDataForVoxelAtCoordinate(xyz, + dataOut); + } + break; + } +} + +/** + * @return Pointer to mapping of data to brainordinates. + * Will be NULL if data does not map to brainordinates. + */ +const CiftiBrainModelsMap* +CiftiMappableDataFile::getBrainordinateMapping() const +{ + if ( ! m_brainordinateMappingCachedFlag) { + m_brainordinateMappingCachedFlag = true; + + switch (m_dataMappingAccessMethod) { + case DATA_ACCESS_METHOD_INVALID: + CaretAssert(0); + break; + case DATA_ACCESS_NONE: + break; + case DATA_ACCESS_FILE_ROWS_OR_XML_ALONG_COLUMN: + case DATA_ACCESS_FILE_COLUMNS_OR_XML_ALONG_ROW: + { + const CiftiXML& myXML = m_ciftiFile->getCiftiXML(); + if (myXML.getMappingType(m_dataMappingDirectionForCiftiXML) == CiftiMappingType::BRAIN_MODELS) { + /* + * Cache a COPY of the CiftiBrainModelsMap to avoid calling CiftiFile::getCiftiXML() many times + */ + m_brainordinateMapping.reset(new CiftiBrainModelsMap(myXML.getBrainModelsMap(m_dataMappingDirectionForCiftiXML))); + } + } + break; + } + } + + return m_brainordinateMapping.get(); +} + +/** + * Are all brainordinates in this file also in the given file? + * That is, the brainordinates are equal to or a subset of the brainordinates + * in the given file. + * + * @param mapFile + * The given map file. + * @return + * Match status. + */ +CaretMappableDataFile::BrainordinateMappingMatch +CiftiMappableDataFile::getBrainordinateMappingMatch(const CaretMappableDataFile* mapFile) const +{ + CaretAssert(mapFile); + if (this == mapFile) { + return BrainordinateMappingMatch::EQUAL; + } + + const CiftiMappableDataFile* otherCiftiFile = dynamic_cast(mapFile); + if (otherCiftiFile == NULL) { + return BrainordinateMappingMatch::NO; + } + + const CiftiBrainModelsMap* myBrainMap = getBrainordinateMapping(); + if (myBrainMap != NULL) { + const CiftiBrainModelsMap* otherBrainMap = otherCiftiFile->getBrainordinateMapping(); + if (otherBrainMap != NULL) { + switch (myBrainMap->testMatch(*otherBrainMap)) { + case CiftiBrainModelsMap::MatchResult::EQUAL: + return BrainordinateMappingMatch::EQUAL; + break; + case CiftiBrainModelsMap::MatchResult::NO: + return BrainordinateMappingMatch::NO; + break; + case CiftiBrainModelsMap::MatchResult::SUBSET: + return BrainordinateMappingMatch::SUBSET; + break; + } + } + } + + return BrainordinateMappingMatch::NO; +} + + ///* ========================================================================== */ /** * Constructor. * + * @param ciftiMappableDataFile + * CIFTI file containing this map * @param ciftiFile * The CIFTI data file * @param fileMapDataType @@ -6257,12 +7054,14 @@ CiftiMappableDataFile::helpMatrixFileLoadChartDataMatrixRGBA(int32_t& numberOfRo * @param mapIndex * Index of this map. */ -CiftiMappableDataFile::MapContent::MapContent(CiftiFile* ciftiFile, +CiftiMappableDataFile::MapContent::MapContent(CiftiMappableDataFile* ciftiMappableDataFile, + CiftiFile* ciftiFile, const FileMapDataType fileMapDataType, const int32_t readingDirectionForCiftiXML, const int32_t mappingDirectionForCiftiXML, const int32_t mapIndex) : CaretObjectTracksModification(), +m_ciftiMappableDataFile(ciftiMappableDataFile), m_ciftiFile(ciftiFile), m_fileMapDataType(fileMapDataType), m_readingDirectionForCiftiXML(readingDirectionForCiftiXML), @@ -6309,7 +7108,6 @@ m_mapIndex(mapIndex) switch (ciftiXML.getMappingType(m_readingDirectionForCiftiXML)) { case CiftiMappingType::BRAIN_MODELS: { - //const CiftiBrainModelsMap& map = ciftiXML.getBrainModelsMap(m_readingDirectionForCiftiXML); CaretAssert(0); } break; @@ -6327,7 +7125,6 @@ m_mapIndex(mapIndex) break; case CiftiMappingType::PARCELS: { - //const CiftiParcelsMap& map = ciftiXML.getParcelsMap(m_readingDirectionForCiftiXML); CaretAssert(0); } break; @@ -6482,7 +7279,6 @@ CiftiMappableDataFile::MapContent::setName(const AString& name) switch (ciftiXML.getMappingType(m_readingDirectionForCiftiXML)) { case CiftiMappingType::BRAIN_MODELS: { - //const CiftiBrainModelsMap& map = ciftiXML.getBrainModelsMap(m_readingDirectionForCiftiXML); CaretAssert(0); } break; @@ -6496,8 +7292,6 @@ CiftiMappableDataFile::MapContent::setName(const AString& name) break; case CiftiMappingType::PARCELS: { - //const CiftiParcelsMap& map = ciftiXML.getParcelsMap(m_readingDirectionForCiftiXML); - // mapName = map.getMapName(m_mapIndex); CaretAssert(0); } break; @@ -6562,32 +7356,41 @@ CiftiMappableDataFile::MapContent::updateFastStatistics(const std::vector } /** + * @param numberOfBuckets, + * Number of buckets in the histogram. * @return True if histogram is valid, else false. */ bool -CiftiMappableDataFile::MapContent::isHistogramValid() const +CiftiMappableDataFile::MapContent::isHistogramValid(const int32_t numberOfBuckets) const { - return (m_histogram != NULL); + return ((m_histogram != NULL) + && (m_histogramNumberOfBuckets == numberOfBuckets)); } /** * Update the Histogram but only when needed. * + * @param numberOfBuckets, + * Number of buckets in the histogram. * @param data * Data for histogram. */ void -CiftiMappableDataFile::MapContent::updateHistogram(const std::vector& data) +CiftiMappableDataFile::MapContent::updateHistogram(const int32_t numberOfBuckets, + const std::vector& data) { if (data.empty()) { m_histogram.grabNew(NULL); } else { + CaretAssert(m_paletteColorMapping); if (m_histogram == NULL) { - m_histogram.grabNew(new Histogram()); - m_histogram->update(&data[0], - data.size()); + m_histogram.grabNew(new Histogram(numberOfBuckets)); } + m_histogram->update(numberOfBuckets, + &data[0], + data.size()); + m_histogramNumberOfBuckets = numberOfBuckets; } } @@ -6595,6 +7398,8 @@ CiftiMappableDataFile::MapContent::updateHistogram(const std::vector& dat * Is limited values histogram valid? Is is valid when it exists and * the limited value parameters have not changed. * + * @param numberOfBuckets, + * Number of buckets in the histogram. * @param mostPositiveValueInclusive * Values more positive than this value are excluded. * @param leastPositiveValueInclusive @@ -6609,7 +7414,8 @@ CiftiMappableDataFile::MapContent::updateHistogram(const std::vector& dat * @return true if valid, else false. */ bool -CiftiMappableDataFile::MapContent::isHistogramLimitedValuesValid(const float mostPositiveValueInclusive, +CiftiMappableDataFile::MapContent::isHistogramLimitedValuesValid(const int32_t numberOfBuckets, + const float mostPositiveValueInclusive, const float leastPositiveValueInclusive, const float leastNegativeValueInclusive, const float mostNegativeValueInclusive, @@ -6618,7 +7424,8 @@ CiftiMappableDataFile::MapContent::isHistogramLimitedValuesValid(const float mos if (m_histogramLimitedValues == NULL) { return false; } - else if ((mostPositiveValueInclusive != m_histogramLimitedValuesMostPositiveValueInclusive) + else if ((numberOfBuckets != m_histogramLimitedValuesNumberOfBuckets) + || (mostPositiveValueInclusive != m_histogramLimitedValuesMostPositiveValueInclusive) || (leastPositiveValueInclusive != m_histogramLimitedValuesLeastPositiveValueInclusive) || (leastNegativeValueInclusive != m_histogramLimitedValuesLeastNegativeValueInclusive) || (mostNegativeValueInclusive != m_histogramLimitedValuesMostNegativeValueInclusive) @@ -6646,7 +7453,8 @@ CiftiMappableDataFile::MapContent::isHistogramLimitedValuesValid(const float mos * If true zero values (very near zero) are included. */ void -CiftiMappableDataFile::MapContent::updateHistogramLimitedValues(const std::vector& data, +CiftiMappableDataFile::MapContent::updateHistogramLimitedValues(const int32_t numberOfBuckets, + const std::vector& data, const float mostPositiveValueInclusive, const float leastPositiveValueInclusive, const float leastNegativeValueInclusive, @@ -6660,14 +7468,15 @@ CiftiMappableDataFile::MapContent::updateHistogramLimitedValues(const std::vecto if (m_histogramLimitedValues == NULL) { m_histogramLimitedValues.grabNew(new Histogram()); } - m_histogramLimitedValues->update(&data[0], + m_histogramLimitedValues->update(numberOfBuckets, + &data[0], data.size(), mostPositiveValueInclusive, leastPositiveValueInclusive, leastNegativeValueInclusive, mostNegativeValueInclusive, includeZeroValues); - + m_histogramLimitedValuesNumberOfBuckets = numberOfBuckets; m_histogramLimitedValuesMostPositiveValueInclusive = mostPositiveValueInclusive; m_histogramLimitedValuesLeastPositiveValueInclusive = leastPositiveValueInclusive; m_histogramLimitedValuesLeastNegativeValueInclusive = leastNegativeValueInclusive; @@ -6676,26 +7485,91 @@ CiftiMappableDataFile::MapContent::updateHistogramLimitedValues(const std::vecto } } +/** + * Get the thresholding data from the given thresholding file in a vector that + * has the same brainordinate mapping as this file. + * + * @param threshMapFile + * The thresholding file. + * @param threshMapIndex + * The map index in the thresholding file. + * @param thresholdDataOut + * Output containing data for thresholding a map in this file. + */ +bool +CiftiMappableDataFile::MapContent::getThresholdData(const CaretMappableDataFile* threshMapFile, + const int32_t threshMapIndex, + std::vector& thresholdDataOut) +{ + CaretAssert(threshMapFile); + CaretAssert(threshMapIndex >= 0); + const CiftiMappableDataFile* thresholdCiftiMapFile = dynamic_cast(threshMapFile); + CaretAssert(thresholdCiftiMapFile); + + thresholdDataOut.resize(m_dataCount); + + switch (m_ciftiMappableDataFile->getBrainordinateMappingMatch(threshMapFile)) { + case BrainordinateMappingMatch::EQUAL: + thresholdCiftiMapFile->getMapData(threshMapIndex, + thresholdDataOut); + break; + case BrainordinateMappingMatch::NO: + CaretAssert(0); /* should never happen */ + break; + case BrainordinateMappingMatch::SUBSET: + { + /* + * Since this file is a "subset" of the other file, will need to + * data using the structures in each file. + */ + std::vector thresholdingFileMapData; + thresholdCiftiMapFile->getMapData(threshMapIndex, thresholdingFileMapData); + + const CiftiBrainModelsMap* threshBrainMap = thresholdCiftiMapFile->getBrainordinateMapping(); + CaretAssert(threshBrainMap); + const CiftiBrainModelsMap* dataBrainMap = m_ciftiMappableDataFile->getBrainordinateMapping(); + CaretAssert(dataBrainMap); + + const std::vector& dataModelsMap = dataBrainMap->getModelInfo(); + const std::vector& threshModelsMap = threshBrainMap->getModelInfo(); + for (const auto& dataModelsInfo : dataModelsMap) { + const StructureEnum::Enum structure = dataModelsInfo.m_structure; + for (const auto& threshModelsInfo : threshModelsMap) { + if (structure == threshModelsInfo.m_structure) { + CaretAssert(dataModelsInfo.m_indexCount == threshModelsInfo.m_indexCount); + CaretAssertVectorIndex(thresholdingFileMapData, (threshModelsInfo.m_indexStart + threshModelsInfo.m_indexCount) - 1); + CaretAssertVectorIndex(thresholdDataOut, (dataModelsInfo.m_indexStart + dataModelsInfo.m_indexCount) - 1); + std::copy_n(&thresholdingFileMapData[threshModelsInfo.m_indexStart], + threshModelsInfo.m_indexCount, + &thresholdDataOut[dataModelsInfo.m_indexStart]); + break; + } + } + } + } + break; + } + + return true; +} + /** * Update coloring for this map. If the paletteFile is NOT NULL, * color using a palette; otherwise, color with label table. * * @param data * Data contained in the map. - * @param paletteFile - * File containing the palettes. * @param fastStatistics * Fast statistics used for palette coloring. While map content contains * a fast statistics member, it is calculated on the data within the map. * However, some files need the statistics calculated on the entire file - * so that a particular data value from one map is colored exactly the + * so that a particular data value from one map is colored exactly the * same as the particular data value in another map (the user may have * a min/max coloring selected and the two maps may a have different * min/max values). */ void CiftiMappableDataFile::MapContent::updateColoring(const std::vector& data, - const PaletteFile* paletteFile, const FastStatistics* fastStatistics) { if (data.empty()) { @@ -6709,7 +7583,6 @@ CiftiMappableDataFile::MapContent::updateColoring(const std::vector& data if (m_dataCount != static_cast(data.size())) { m_dataCount = static_cast(data.size()); } -// CaretAssert(m_dataCount == static_cast(data.size())); const uint64_t rgbaCount = m_dataCount * 4; if (m_rgba.size() != rgbaCount) { m_rgba.resize(rgbaCount, 0); @@ -6722,19 +7595,67 @@ CiftiMappableDataFile::MapContent::updateColoring(const std::vector& data data.size(), &m_rgba[0]); } - else if (paletteFile != NULL) { + else { CaretAssert(m_paletteColorMapping); - const AString paletteName = m_paletteColorMapping->getSelectedPaletteName(); - const Palette* palette = paletteFile->getPaletteByName(paletteName); - if ((palette != NULL) - && (fastStatistics != NULL)) { - NodeAndVoxelColoring::colorScalarsWithPalette(fastStatistics, - m_paletteColorMapping, - palette, - &data[0], - &data[0], - m_dataCount, - &m_rgba[0]); + if (fastStatistics != NULL) { + bool useThreshMapFileFlag = false; + switch (m_paletteColorMapping->getThresholdType()) { + case PaletteThresholdTypeEnum::THRESHOLD_TYPE_FILE: + useThreshMapFileFlag = true; + break; + case PaletteThresholdTypeEnum::THRESHOLD_TYPE_MAPPED: + break; + case PaletteThresholdTypeEnum::THRESHOLD_TYPE_MAPPED_AVERAGE_AREA: + break; + case PaletteThresholdTypeEnum::THRESHOLD_TYPE_NORMAL: + break; + case PaletteThresholdTypeEnum::THRESHOLD_TYPE_OFF: + break; + } + + if (useThreshMapFileFlag) { + CaretAssert(m_ciftiMappableDataFile); + const CaretMappableDataFileAndMapSelectionModel* threshFileModel = m_ciftiMappableDataFile->getMapThresholdFileSelectionModel(m_mapIndex); + CaretAssert(threshFileModel); + const CaretMappableDataFile* threshMapFile = threshFileModel->getSelectedFile(); + if (threshMapFile != NULL) { + const int32_t threshMapIndex = threshFileModel->getSelectedMapIndex(); + std::vector thresholdData; + getThresholdData(threshMapFile, + threshMapIndex, + thresholdData); + CaretAssert(threshMapFile); + CaretAssert(threshMapIndex >= 0); + PaletteColorMapping* thresholdPaletteColorMapping = const_cast(threshMapFile->getMapPaletteColorMapping(threshMapIndex)); + CaretAssert(thresholdPaletteColorMapping); + CaretAssert(data.size() == thresholdData.size()); + NodeAndVoxelColoring::colorScalarsWithPalette(fastStatistics, + m_paletteColorMapping, + &data[0], + thresholdPaletteColorMapping, + &thresholdData[0], + m_dataCount, + &m_rgba[0]); + } + else { + NodeAndVoxelColoring::colorScalarsWithPalette(fastStatistics, + m_paletteColorMapping, + &data[0], + m_paletteColorMapping, + &data[0], + m_dataCount, + &m_rgba[0]); + } + } + else { + NodeAndVoxelColoring::colorScalarsWithPalette(fastStatistics, + m_paletteColorMapping, + &data[0], + m_paletteColorMapping, + &data[0], + m_dataCount, + &m_rgba[0]); + } } else { std::fill(m_rgba.begin(), @@ -6742,12 +7663,20 @@ CiftiMappableDataFile::MapContent::updateColoring(const std::vector& data 0); } } - else { - const AString msg("NULL palette for coloring scalar data."); - CaretAssertMessage(0, msg); - CaretLogSevere(msg); - } m_rgbaValid = true; } +bool CiftiMappableDataFile::hasCiftiXML() const +{ + return true; +} + +const CiftiXML CiftiMappableDataFile::getCiftiXML() const +{ + if (m_ciftiFile != NULL) + { + return m_ciftiFile->getCiftiXML(); + } + return CiftiXML();//this is why the function doesn't return a reference: must return something even when it doesn't have a CiftiXML allocated - could make it a pointer and return NULL +} diff --git a/src/Files/CiftiMappableDataFile.h b/src/Files/CiftiMappableDataFile.h index 1177db8d9972995f0d7f51528f6dff77ae138aff..e75f13a0daddbab5144765b3b4defa8ed1c046ee 100644 --- a/src/Files/CiftiMappableDataFile.h +++ b/src/Files/CiftiMappableDataFile.h @@ -24,11 +24,14 @@ #include "CaretMappableDataFile.h" #include "CaretPointer.h" #include "CaretObjectTracksModification.h" +#include "ChartTwoMatrixTriangularViewingModeEnum.h" #include "CiftiMappingType.h" #include "CiftiXMLElements.h" #include "DisplayGroupEnum.h" +#include "EventListenerInterface.h" #include "VolumeMappableInterface.h" +#include #include namespace caret { @@ -39,6 +42,7 @@ namespace caret { class CiftiParcelsMap; class CiftiXML; class FastStatistics; + class GraphicsPrimitiveV3fC4f; class GroupAndNameHierarchyModel; class Histogram; class SparseVolumeIndexer; @@ -46,6 +50,7 @@ namespace caret { class CiftiMappableDataFile : public CaretMappableDataFile, + public EventListenerInterface, public VolumeMappableInterface { protected: @@ -148,6 +153,8 @@ namespace caret { const int32_t numberOfNodes, AString& errorMessageOut); + virtual void receiveEvent(Event* event); + virtual void addToDataFileContentInformation(DataFileContentInformation& dataFileInformation); static void addCiftiXmlToDataFileContentInformation(DataFileContentInformation& dataFileInformation, @@ -197,6 +204,8 @@ namespace caret { virtual bool isMappedWithPalette() const; + virtual bool isOnePaletteUsedForAllMaps() const; + virtual const FastStatistics* getMapFastStatistics(const int32_t mapIndex); virtual const Histogram* getMapHistogram(const int32_t mapIndex); @@ -208,7 +217,7 @@ namespace caret { const float mostNegativeValueInclusive, const bool includeZeroValues); - virtual void getPaletteNormalizationModesSupported(std::vector& modesSupportedOut); + virtual void getPaletteNormalizationModesSupported(std::vector& modesSupportedOut) const; virtual int64_t getDataSizeUncompressedInBytes() const; @@ -238,10 +247,9 @@ namespace caret { virtual const GiftiLabelTable* getMapLabelTable(const int32_t mapIndex) const; - virtual void updateScalarColoringForAllMaps(const PaletteFile* paletteFile); + virtual void updateScalarColoringForAllMaps() override; - virtual void updateScalarColoringForMap(const int32_t mapIndex, - const PaletteFile* paletteFile); + virtual void updateScalarColoringForMap(const int32_t mapIndex) override; virtual bool isMapColoringValid(const int32_t mapIndex) const; @@ -249,53 +257,52 @@ namespace caret { int64_t& dimOut2, int64_t& dimOut3, int64_t& dimTimeOut, - int64_t& numComponents) const; + int64_t& numComponents) const override; - virtual void getDimensions(std::vector& dimsOut) const; + virtual void getDimensions(std::vector& dimsOut) const override; virtual void getMapDimensions(std::vector &dim) const; - virtual const int64_t& getNumberOfComponents() const; + virtual const int64_t& getNumberOfComponents() const override; virtual void indexToSpace(const float& indexIn1, const float& indexIn2, const float& indexIn3, float& coordOut1, float& coordOut2, - float& coordOut3) const; + float& coordOut3) const override; virtual void indexToSpace(const float& indexIn1, const float& indexIn2, const float& indexIn3, - float* coordOut) const; + float* coordOut) const override; virtual void indexToSpace(const int64_t* indexIn, - float* coordOut) const; + float* coordOut) const override; virtual void enclosingVoxel(const float& coordIn1, const float& coordIn2, const float& coordIn3, int64_t& indexOut1, int64_t& indexOut2, - int64_t& indexOut3) const; + int64_t& indexOut3) const override; virtual bool indexValid(const int64_t& indexIn1, const int64_t& indexIn2, const int64_t& indexIn3, const int64_t brickIndex = 0, - const int64_t component = 0) const; + const int64_t component = 0) const override; virtual const VolumeSpace& getVolumeSpace() const; - virtual void getVoxelSpaceBoundingBox(BoundingBox& boundingBoxOut) const; + virtual void getVoxelSpaceBoundingBox(BoundingBox& boundingBoxOut) const override; - virtual int64_t getVoxelColorsForSliceInMap(const PaletteFile* paletteFile, - const int32_t mapIndex, + virtual int64_t getVoxelColorsForSliceInMap(const int32_t mapIndex, const VolumeSliceViewPlaneEnum::Enum slicePlane, const int64_t sliceIndex, const DisplayGroupEnum::Enum displayGroup, const int32_t tabIndex, - uint8_t* rgbaOut) const; + uint8_t* rgbaOut) const override; int64_t getVoxelColorsForSliceInMap(const int32_t mapIndex, const int64_t firstVoxelIJK[3], @@ -305,10 +312,9 @@ namespace caret { const int64_t numberOfColumns, const DisplayGroupEnum::Enum displayGroup, const int32_t tabIndex, - uint8_t* rgbaOut) const; + uint8_t* rgbaOut) const override; - virtual int64_t getVoxelColorsForSubSliceInMap(const PaletteFile* paletteFile, - const int32_t mapIndex, + virtual int64_t getVoxelColorsForSubSliceInMap(const int32_t mapIndex, const VolumeSliceViewPlaneEnum::Enum slicePlane, const int64_t sliceIndex, const int64_t firstCornerVoxelIndex[3], @@ -316,19 +322,17 @@ namespace caret { const int64_t voxelCountIJK[3], const DisplayGroupEnum::Enum displayGroup, const int32_t tabIndex, - uint8_t* rgbaOut) const; + uint8_t* rgbaOut) const override; - virtual void getVoxelColorInMap(const PaletteFile* paletteFile, - const int64_t indexIn1, + virtual void getVoxelColorInMap(const int64_t indexIn1, const int64_t indexIn2, const int64_t indexIn3, const int64_t mapIndex, const DisplayGroupEnum::Enum displayGroup, const int32_t tabIndex, - uint8_t rgbaOut[4]) const; + uint8_t rgbaOut[4]) const override; - virtual void getVoxelColorInMapForLabelData(const PaletteFile* paletteFile, - const std::vector& dataForMap, + virtual void getVoxelColorInMapForLabelData(const std::vector& dataForMap, const int64_t indexIn1, const int64_t indexIn2, const int64_t indexIn3, @@ -357,14 +361,14 @@ namespace caret { virtual float getVoxelValue(const float* coordinateIn, bool* validOut = NULL, const int64_t mapIndex = 0, - const int64_t component = 0) const; + const int64_t component = 0) const override; virtual float getVoxelValue(const float coordinateX, const float coordinateY, const float coordinateZ, bool* validOut = NULL, const int64_t mapIndex = 0, - const int64_t component = 0) const; + const int64_t component = 0) const override; virtual bool getVolumeVoxelIdentificationForMaps(const std::vector& mapIndices, const float xyz[3], @@ -408,8 +412,7 @@ namespace caret { bool getSeriesDataForVoxelAtCoordinate(const float xyz[3], std::vector& seriesDataOut) const; - virtual bool getMapSurfaceNodeColoring(const PaletteFile* paletteFile, - const int32_t mapIndex, + virtual bool getMapSurfaceNodeColoring(const int32_t mapIndex, const StructureEnum::Enum structure, float* surfaceRGBAOut, float* dataValuesOut, @@ -450,6 +453,20 @@ namespace caret { int64_t voxelIJKOut[3], float voxelXYZOut[3], bool& voxelValidOut) const; + + bool hasCiftiXML() const; + + const CiftiXML getCiftiXML() const; + + /** + * @return The index base (zero or one) for displaying row and column indices in the Graphical User Interface + */ + static inline int32_t getCiftiFileRowColumnIndexBaseForGUI() { return CIFTI_FILE_ROW_COLUMN_INDEX_BASE_FOR_GUI; } + + virtual void getDataForSelector(const MapFileDataSelector& mapFileDataSelector, + std::vector& dataOut) const override; + + virtual BrainordinateMappingMatch getBrainordinateMappingMatch(const CaretMappableDataFile* mapFile) const override; private: @@ -458,6 +475,10 @@ namespace caret { CiftiMappableDataFile& operator=(const CiftiMappableDataFile&); public: + enum class MatrixGridMode { + FILLED, + OUTLINE + }; virtual void getMapData(const int32_t mapIndex, std::vector& dataOut) const; @@ -465,8 +486,18 @@ namespace caret { virtual void setMapData(const int32_t mapIndex, const std::vector& data); - virtual void getMatrixRGBA(std::vector& rgba, PaletteFile *paletteFile); + virtual void getMatrixRGBA(std::vector& rgba); + bool getMatrixForChartingRGBA(int32_t& numberOfRowsOut, + int32_t& numberOfColumnsOut, + std::vector& rgbaOut) const; + + GraphicsPrimitiveV3fC4f* getMatrixChartingGraphicsPrimitive(const ChartTwoMatrixTriangularViewingModeEnum::Enum matrixViewMode, + const MatrixGridMode gridMode) const; + + /** Identifier for the matrix primitives alternative color used for the grid coloring */ + int32_t getMatrixChartGraphicsPrimitiveGridColorIdentifier() const { return 1; } + virtual void getFileData(std::vector& data) const; const CiftiFile* getCiftiFile() const { return m_ciftiFile; } @@ -503,21 +534,13 @@ namespace caret { const std::vector& rowIndicesIn, std::vector& rgbaOut) const; -// bool helpLoadChartDataMatrixRGBA(int32_t& numberOfRowsOut, -// int32_t& numberOfColumnsOut, -// std::vector& rgbaOut) const; -// -// bool helpLoadChartDataMatrixRGBAWithRowIndicese(int32_t& numberOfRowsOut, -// int32_t& numberOfColumnsOut, -// const std::vector& rowIndices, -// std::vector& rgbaOut) const; - private: class MapContent : public CaretObjectTracksModification { public: - MapContent(CiftiFile* ciftiFile, + MapContent(CiftiMappableDataFile* ciftiMappableDataFile, + CiftiFile* ciftiFile, const FileMapDataType fileMapDataType, const int32_t readingDirectionForCiftiXML, const int32_t mappingDirectionForCiftiXML, @@ -532,24 +555,26 @@ namespace caret { void updateForChangeInMapData(); void updateColoring(const std::vector& data, - const PaletteFile* paletteFile, const FastStatistics* fastStatistics); bool isFastStatisticsValid() const; void updateFastStatistics(const std::vector& data); - bool isHistogramValid() const; + bool isHistogramValid(const int32_t numberOfBuckets) const; - void updateHistogram(const std::vector& data); + void updateHistogram(const int32_t numberOfBuckets, + const std::vector& data); - bool isHistogramLimitedValuesValid(const float mostPositiveValueInclusive, + bool isHistogramLimitedValuesValid(const int32_t numberOfBuckets, + const float mostPositiveValueInclusive, const float leastPositiveValueInclusive, const float leastNegativeValueInclusive, const float mostNegativeValueInclusive, const bool includeZeroValues) const; - void updateHistogramLimitedValues(const std::vector& data, + void updateHistogramLimitedValues(const int32_t numberOfBuckets, + const std::vector& data, const float mostPositiveValueInclusive, const float leastPositiveValueInclusive, const float leastNegativeValueInclusive, @@ -560,6 +585,13 @@ namespace caret { void setName(const AString& name); + bool getThresholdData(const CaretMappableDataFile* threshMapFile, + const int32_t threshMapIndex, + std::vector& thresholdData); + + /** CIFTI file containing the map */ + CiftiMappableDataFile* m_ciftiMappableDataFile; + /** The CIFTI file pointer */ CiftiFile *m_ciftiFile; @@ -605,9 +637,12 @@ namespace caret { /** histogram for all of map map */ CaretPointer m_histogram; + int32_t m_histogramNumberOfBuckets = 100; + /** histogram for limited values from map */ CaretPointer m_histogramLimitedValues; + int32_t m_histogramLimitedValuesNumberOfBuckets; float m_histogramLimitedValuesMostPositiveValueInclusive; float m_histogramLimitedValuesLeastPositiveValueInclusive; float m_histogramLimitedValuesLeastNegativeValueInclusive; @@ -652,6 +687,8 @@ namespace caret { static AString mappingTypeToName(const CiftiMappingType::MappingType mappingType); + const CiftiBrainModelsMap* getBrainordinateMapping() const; + /** * Point to the CIFTI file object. */ @@ -719,9 +756,20 @@ namespace caret { /** Histogram used when statistics computed on all data in file */ CaretPointer m_fileHistogram; + /** Primitive for matrix cells */ + mutable std::unique_ptr m_matrixGraphicsPrimitive; + + /** Primitive for grid outline around matrix cells */ + mutable std::unique_ptr m_matrixGraphicsOutlinePrimitive; + + mutable uint8_t m_previousMatrixGridRGBA[4] = { 0, 1, 2, 3 }; + + int32_t m_fileHistogramNumberOfBuckets = 100; + /** Histogram with limited values used when statistics computed on all data in file */ CaretPointer m_fileHistorgramLimitedValues; + int32_t m_fileHistogramLimitedValuesNumberOfBuckets = 100; float m_fileHistogramLimitedValuesMostPositiveValueInclusive; float m_fileHistogramLimitedValuesLeastPositiveValueInclusive; float m_fileHistogramLimitedValuesLeastNegativeValueInclusive; @@ -737,10 +785,24 @@ namespace caret { /** force an update of the class and name hierarchy */ mutable bool m_forceUpdateOfGroupAndNameHierarchy; - static const int32_t S_CIFTI_XML_ALONG_INVALID; -// std::vector m_ciftiDimensions; + protected: + /** + * This value is used for the "base index" of CIFTI rows/columns + * in the user-interface. + */ + static const int32_t CIFTI_FILE_ROW_COLUMN_INDEX_BASE_FOR_GUI = 1; + + private: + friend class ChartableTwoFileDelegate; + friend class ChartableTwoFileMatrixChart; + + /** Is lazily initialized and caches CiftiBrainModelsMap for comparison with other CIFTI files */ + mutable std::unique_ptr m_brainordinateMapping; + + /** Controls lazy initialization of m_brainordinateMapping */ + mutable bool m_brainordinateMappingCachedFlag = false; // ADD_NEW_MEMBERS_HERE diff --git a/src/Files/CiftiParcelLabelFile.cxx b/src/Files/CiftiParcelLabelFile.cxx index 803b308a3a010182e805a97a5a9dae1553b6bef1..413cf7caa589d18856f49bbb3935d57a0cd09dac 100644 --- a/src/Files/CiftiParcelLabelFile.cxx +++ b/src/Files/CiftiParcelLabelFile.cxx @@ -297,10 +297,10 @@ CiftiParcelLabelFile::setMatrixChartingEnabled(const int32_t tabIndex, * Chart types supported by this file. */ void -CiftiParcelLabelFile::getSupportedMatrixChartDataTypes(std::vector& chartDataTypesOut) const +CiftiParcelLabelFile::getSupportedMatrixChartDataTypes(std::vector& chartDataTypesOut) const { chartDataTypesOut.clear(); - chartDataTypesOut.push_back(ChartDataTypeEnum::CHART_DATA_TYPE_MATRIX_LAYER); + chartDataTypesOut.push_back(ChartOneDataTypeEnum::CHART_DATA_TYPE_MATRIX_LAYER); } /** diff --git a/src/Files/CiftiParcelLabelFile.h b/src/Files/CiftiParcelLabelFile.h index 339dfcbd2890873f0f3589a6ad7d3c323999b21e..8f78fdf9b3f0f4ee42daea54be6721f2e33256c8 100644 --- a/src/Files/CiftiParcelLabelFile.h +++ b/src/Files/CiftiParcelLabelFile.h @@ -58,7 +58,7 @@ namespace caret { virtual void setMatrixChartingEnabled(const int32_t tabIndex, const bool enabled); - virtual void getSupportedMatrixChartDataTypes(std::vector& chartDataTypesOut) const; + virtual void getSupportedMatrixChartDataTypes(std::vector& chartDataTypesOut) const; const ChartMatrixDisplayProperties* getChartMatrixDisplayProperties(const int32_t tabIndex) const; diff --git a/src/Files/CiftiParcelReorderingModel.cxx b/src/Files/CiftiParcelReorderingModel.cxx index ebbe5349b4ed198fdb58a7dc205b17d802afdb25..e904e4d5ea7dc3b5cfa49c3f1fd96b2a684753e5 100644 --- a/src/Files/CiftiParcelReorderingModel.cxx +++ b/src/Files/CiftiParcelReorderingModel.cxx @@ -56,8 +56,9 @@ m_parentCiftiMappableDataFile(parentCiftiMappableDataFile) switch (parentCiftiMappableDataFile->getDataFileType()) { case DataFileTypeEnum::CONNECTIVITY_PARCEL: - case DataFileTypeEnum::CONNECTIVITY_PARCEL_SCALAR: case DataFileTypeEnum::CONNECTIVITY_PARCEL_LABEL: + case DataFileTypeEnum::CONNECTIVITY_PARCEL_SCALAR: + case DataFileTypeEnum::CONNECTIVITY_PARCEL_SERIES: break; default: CaretAssert(0); @@ -81,6 +82,15 @@ CiftiParcelReorderingModel::~CiftiParcelReorderingModel() { delete m_sceneAssistant; + clearCiftiParcelReordering(); +} + +/** + * Clear all parcel reordering. + */ +void +CiftiParcelReorderingModel::clearCiftiParcelReordering() +{ for (std::vector::iterator iter = m_parcelReordering.begin(); iter != m_parcelReordering.end(); iter++) { @@ -89,6 +99,7 @@ CiftiParcelReorderingModel::~CiftiParcelReorderingModel() m_parcelReordering.clear(); } + /** * Copy constructor. * @param obj @@ -191,6 +202,7 @@ CiftiParcelReorderingModel::getParcelLabelFiles() const testAlongRow = true; break; case DataFileTypeEnum::CONNECTIVITY_PARCEL_SCALAR: + case DataFileTypeEnum::CONNECTIVITY_PARCEL_SERIES: testAlongColumn = true; break; case DataFileTypeEnum::CONNECTIVITY_PARCEL_LABEL: @@ -513,7 +525,7 @@ CiftiParcelReorderingModel::restoreFromScene(const SceneAttributes* sceneAttribu sceneClass); m_selectedParcelLabelFile = NULL; - m_parcelReordering.clear(); + clearCiftiParcelReordering(); const AString parcelLabelFileName = sceneClass->getPathNameValue("m_selectedParcelLabelFile"); if ( ! parcelLabelFileName.isEmpty()) { diff --git a/src/Files/CiftiParcelReorderingModel.h b/src/Files/CiftiParcelReorderingModel.h index 7b6ffaa9d1ca002f3426ebf8c007b740ccb0e35f..198e90b13225d7254134abd0fd4fa16cf980a380 100644 --- a/src/Files/CiftiParcelReorderingModel.h +++ b/src/Files/CiftiParcelReorderingModel.h @@ -97,6 +97,8 @@ namespace caret { std::vector getParcelLabelFiles() const; + void clearCiftiParcelReordering(); + mutable std::map m_parcelLabelFileCompatibilityStatus; const CiftiMappableDataFile* m_parentCiftiMappableDataFile; diff --git a/src/Files/CiftiParcelScalarFile.cxx b/src/Files/CiftiParcelScalarFile.cxx index 6e48c5e3be8c11ede58035e522c285f345c0699f..aaba8f0e892eb4f13994641ef50fe7550862a719 100644 --- a/src/Files/CiftiParcelScalarFile.cxx +++ b/src/Files/CiftiParcelScalarFile.cxx @@ -34,7 +34,6 @@ #include "NodeAndVoxelColoring.h" #include "Palette.h" #include "PaletteColorMapping.h" -#include "PaletteFile.h" #include "SceneClass.h" #include "SceneClassArray.h" #include "SceneClassAssistant.h" @@ -124,7 +123,7 @@ CiftiParcelScalarFile::isLineSeriesChartingSupported() const * Chart types supported by this file. */ void -CiftiParcelScalarFile::getSupportedLineSeriesChartDataTypes(std::vector& chartDataTypesOut) const +CiftiParcelScalarFile::getSupportedLineSeriesChartDataTypes(std::vector& chartDataTypesOut) const { helpGetSupportedLineSeriesChartDataTypes(chartDataTypesOut); } @@ -164,41 +163,6 @@ CiftiParcelScalarFile::loadLineSeriesChartDataForSurfaceNode(const StructureEnum ChartDataCartesian* chartData = helpLoadChartDataForSurfaceNode(structure, nodeIndex); return chartData; - -// ChartDataCartesian* chartData = NULL; -// -// try { -// std::vector data; -// if (getSeriesDataForSurfaceNode(structure, -// nodeIndex, -// data)) { -// const int64_t numData = static_cast(data.size()); -// -// chartData = new ChartDataCartesian(ChartDataTypeEnum::CHART_DATA_TYPE_LINE_DATA_SERIES, -// ChartAxisUnitsEnum::CHART_AXIS_UNITS_NONE, -// ChartAxisUnitsEnum::CHART_AXIS_UNITS_NONE); -// for (int64_t i = 0; i < numData; i++) { -// float xValue = i; -// chartData->addPoint(xValue, -// data[i]); -// } -// -// const AString description = (getFileNameNoPath() -// + " node " -// + AString::number(nodeIndex)); -// chartData->setDescription(description); -// } -// } -// catch (const DataFileException& dfe) { -// if (chartData != NULL) { -// delete chartData; -// chartData = NULL; -// } -// -// throw dfe; -// } -// -// return chartData; } /** @@ -393,10 +357,10 @@ CiftiParcelScalarFile::setMatrixChartingEnabled(const int32_t tabIndex, * Chart types supported by this file. */ void -CiftiParcelScalarFile::getSupportedMatrixChartDataTypes(std::vector& chartDataTypesOut) const +CiftiParcelScalarFile::getSupportedMatrixChartDataTypes(std::vector& chartDataTypesOut) const { chartDataTypesOut.clear(); - chartDataTypesOut.push_back(ChartDataTypeEnum::CHART_DATA_TYPE_MATRIX_LAYER); + chartDataTypesOut.push_back(ChartOneDataTypeEnum::CHART_DATA_TYPE_MATRIX_LAYER); } /** diff --git a/src/Files/CiftiParcelScalarFile.h b/src/Files/CiftiParcelScalarFile.h index c7a59869fda4d88c7487acc6d169b4e7ebcffb31..836b775595823bdf2b7a33d0ea1cd303418af25a 100644 --- a/src/Files/CiftiParcelScalarFile.h +++ b/src/Files/CiftiParcelScalarFile.h @@ -56,7 +56,7 @@ namespace caret { virtual ChartDataCartesian* loadLineSeriesChartDataForVoxelAtCoordinate(const float xyz[3]); - virtual void getSupportedLineSeriesChartDataTypes(std::vector& chartDataTypesOut) const; + virtual void getSupportedLineSeriesChartDataTypes(std::vector& chartDataTypesOut) const; virtual void getMatrixDimensions(int32_t& numberOfRowsOut, int32_t& numberOfColumnsOut) const; @@ -78,7 +78,7 @@ namespace caret { virtual void setMatrixChartingEnabled(const int32_t tabIndex, const bool enabled); - virtual void getSupportedMatrixChartDataTypes(std::vector& chartDataTypesOut) const; + virtual void getSupportedMatrixChartDataTypes(std::vector& chartDataTypesOut) const; const ChartMatrixDisplayProperties* getChartMatrixDisplayProperties(const int32_t tabIndex) const; diff --git a/src/Files/CiftiParcelSeriesFile.cxx b/src/Files/CiftiParcelSeriesFile.cxx index 31a45b7f169f867dbc9c576b03a8a69f3e099267..473b2fae89468c5a40778e3d3ceb98f079581a7a 100644 --- a/src/Files/CiftiParcelSeriesFile.cxx +++ b/src/Files/CiftiParcelSeriesFile.cxx @@ -25,8 +25,10 @@ #include "CaretLogger.h" #include "ChartDataCartesian.h" +#include "CiftiParcelReorderingModel.h" #include "SceneClass.h" #include "SceneClassArray.h" +#include "SceneClassAssistant.h" using namespace caret; @@ -47,6 +49,13 @@ CiftiParcelSeriesFile::CiftiParcelSeriesFile() for (int32_t i = 0; i < BrainConstants::MAXIMUM_NUMBER_OF_BROWSER_TABS; i++) { m_chartingEnabledForTab[i] = false; } + + m_parcelReorderingModel = std::unique_ptr(new CiftiParcelReorderingModel(this)); + + m_sceneAssistant = std::unique_ptr(new SceneClassAssistant()); + m_sceneAssistant->add("m_parcelReorderingModel", + "CiftiParcelReorderingModel", + m_parcelReorderingModel.get()); } /** @@ -107,7 +116,7 @@ CiftiParcelSeriesFile::setLineSeriesChartingEnabled(const int32_t tabIndex, * Chart types supported by this file. */ void -CiftiParcelSeriesFile::getSupportedLineSeriesChartDataTypes(std::vector& chartDataTypesOut) const +CiftiParcelSeriesFile::getSupportedLineSeriesChartDataTypes(std::vector& chartDataTypesOut) const { helpGetSupportedLineSeriesChartDataTypes(chartDataTypesOut); } @@ -131,97 +140,6 @@ CiftiParcelSeriesFile::loadLineSeriesChartDataForSurfaceNode(const StructureEnum ChartDataCartesian* chartData = helpLoadChartDataForSurfaceNode(structure, nodeIndex); return chartData; - -// ChartDataCartesian* chartData = NULL; -// -// try { -// std::vector data; -// if (getSeriesDataForSurfaceNode(structure, -// nodeIndex, -// data)) { -// const int64_t numData = static_cast(data.size()); -// -// bool timeSeriesFlag = false; -// bool dataSeriesFlag = false; -// float convertTimeToSeconds = 1.0; -// switch (getMapIntervalUnits()) { -// case NiftiTimeUnitsEnum::NIFTI_UNITS_HZ: -// break; -// case NiftiTimeUnitsEnum::NIFTI_UNITS_MSEC: -// timeSeriesFlag = true; -// convertTimeToSeconds = 1000.0; -// break; -// case NiftiTimeUnitsEnum::NIFTI_UNITS_PPM: -// break; -// case NiftiTimeUnitsEnum::NIFTI_UNITS_SEC: -// convertTimeToSeconds = 1.0; -// timeSeriesFlag = true; -// break; -// case NiftiTimeUnitsEnum::NIFTI_UNITS_UNKNOWN: -// dataSeriesFlag = true; -// break; -// case NiftiTimeUnitsEnum::NIFTI_UNITS_USEC: -// convertTimeToSeconds = 1000000.0; -// timeSeriesFlag = true; -// break; -// } -// -// if (dataSeriesFlag) { -// chartData = new ChartDataCartesian(ChartDataTypeEnum::CHART_DATA_TYPE_LINE_DATA_SERIES, -// ChartAxisUnitsEnum::CHART_AXIS_UNITS_NONE, -// ChartAxisUnitsEnum::CHART_AXIS_UNITS_NONE); -// } -// else if (timeSeriesFlag) { -// chartData = new ChartDataCartesian(ChartDataTypeEnum::CHART_DATA_TYPE_LINE_TIME_SERIES, -// ChartAxisUnitsEnum::CHART_AXIS_UNITS_TIME_SECONDS, -// ChartAxisUnitsEnum::CHART_AXIS_UNITS_NONE); -// } -// -// if (chartData != NULL) { -// float timeStart = 0.0; -// float timeStep = 1.0; -// if (timeSeriesFlag) { -// getMapIntervalStartAndStep(timeStart, -// timeStep); -// timeStart *= convertTimeToSeconds; -// timeStep *= convertTimeToSeconds; -// chartData->setTimeStartInSecondsAxisX(timeStart); -// chartData->setTimeStepInSecondsAxisX(timeStep); -// } -// -// for (int64_t i = 0; i < numData; i++) { -// float xValue = i; -// -// if (timeSeriesFlag) { -// xValue = timeStart + (i * timeStep); -// } -// -// chartData->addPoint(xValue, -// data[i]); -// } -// -// const AString description = (getFileNameNoPath() -// + " node " -// + AString::number(nodeIndex)); -// chartData->setDescription(description); -// } -// else { -// const AString msg = "New type of units for data series flag, needs updating for charting"; -// CaretAssertMessage(0, msg); -// throw DataFileException(msg); -// } -// } -// } -// catch (const DataFileException& dfe) { -// if (chartData != NULL) { -// delete chartData; -// chartData = NULL; -// } -// -// throw dfe; -// } -// -// return chartData; } /** @@ -282,6 +200,9 @@ CiftiParcelSeriesFile::saveFileDataToScene(const SceneAttributes* sceneAttribute CiftiMappableDataFile::saveFileDataToScene(sceneAttributes, sceneClass); + m_sceneAssistant->saveMembers(sceneAttributes, + sceneClass); + sceneClass->addBooleanArray("m_chartingEnabledForTab", m_chartingEnabledForTab, BrainConstants::MAXIMUM_NUMBER_OF_BROWSER_TABS); @@ -308,6 +229,9 @@ CiftiParcelSeriesFile::restoreFileDataFromScene(const SceneAttributes* sceneAttr CiftiMappableDataFile::restoreFileDataFromScene(sceneAttributes, sceneClass); + m_sceneAssistant->restoreMembers(sceneAttributes, + sceneClass); + const ScenePrimitiveArray* tabArray = sceneClass->getPrimitiveArray("m_chartingEnabledForTab"); if (tabArray != NULL) { sceneClass->getBooleanArrayValue("m_chartingEnabledForTab", @@ -326,4 +250,95 @@ CiftiParcelSeriesFile::restoreFileDataFromScene(const SceneAttributes* sceneAttr } } +/** + * Get the selected parcel label file used for reordering of parcels. + * + * @param compatibleParcelLabelFilesOut + * All Parcel Label files that are compatible with file implementing + * this interface. + * @param selectedParcelLabelFileOut + * The selected parcel label file used for reordering the parcels. + * May be NULL! + * @param selectedParcelLabelFileMapIndexOut + * Map index in the selected parcel label file. + * @param enabledStatusOut + * Enabled status of reordering. + */ +void +CiftiParcelSeriesFile::getSelectedParcelLabelFileAndMapForReordering(std::vector& compatibleParcelLabelFilesOut, + CiftiParcelLabelFile* &selectedParcelLabelFileOut, + int32_t& selectedParcelLabelFileMapIndexOut, + bool& enabledStatusOut) const +{ + m_parcelReorderingModel->getSelectedParcelLabelFileAndMapForReordering(compatibleParcelLabelFilesOut, + selectedParcelLabelFileOut, + selectedParcelLabelFileMapIndexOut, + enabledStatusOut); +} + +/** + * Set the selected parcel label file used for reordering of parcels. + * + * @param selectedParcelLabelFile + * The selected parcel label file used for reordering the parcels. + * May be NULL! + * @param selectedParcelLabelFileMapIndex + * Map index in the selected parcel label file. + * @param enabledStatus + * Enabled status of reordering. + */ +void +CiftiParcelSeriesFile::setSelectedParcelLabelFileAndMapForReordering(CiftiParcelLabelFile* selectedParcelLabelFile, + const int32_t selectedParcelLabelFileMapIndex, + const bool enabledStatus) +{ + m_parcelReorderingModel->setSelectedParcelLabelFileAndMapForReordering(selectedParcelLabelFile, + selectedParcelLabelFileMapIndex, + enabledStatus); +} + +/** + * Get the parcel reordering for the given map index that was created using + * the given parcel label file and its map index. + * + * @param parcelLabelFile + * The selected parcel label file used for reordering the parcels. + * @param parcelLabelFileMapIndex + * Map index in the selected parcel label file. + * @return + * Pointer to parcel reordering or NULL if not found. + */ +const CiftiParcelReordering* +CiftiParcelSeriesFile::getParcelReordering(const CiftiParcelLabelFile* parcelLabelFile, + const int32_t parcelLabelFileMapIndex) const +{ + return m_parcelReorderingModel->getParcelReordering(parcelLabelFile, + parcelLabelFileMapIndex); +} + +/** + * Create the parcel reordering for the given map index using + * the given parcel label file and its map index. + * + * @param parcelLabelFile + * The selected parcel label file used for reordering the parcels. + * @param parcelLabelFileMapIndex + * Map index in the selected parcel label file. + * @param ciftiParcelsMap + * The CIFTI parcels map that will or has been reordered. + * @param errorMessageOut + * Error message output. Will only be non-empty if NULL is returned. + * @return + * Pointer to parcel reordering or NULL if not found. + */ +bool +CiftiParcelSeriesFile::createParcelReordering(const CiftiParcelLabelFile* parcelLabelFile, + const int32_t parcelLabelFileMapIndex, + AString& errorMessageOut) +{ + return m_parcelReorderingModel->createParcelReordering(parcelLabelFile, + parcelLabelFileMapIndex, + errorMessageOut); +} + diff --git a/src/Files/CiftiParcelSeriesFile.h b/src/Files/CiftiParcelSeriesFile.h index e9c10bdb61a9772162a2e221e8910db9e3bcbb04..44c29479f815bd02140c0fdf59c308cde58ec1e3 100644 --- a/src/Files/CiftiParcelSeriesFile.h +++ b/src/Files/CiftiParcelSeriesFile.h @@ -21,12 +21,19 @@ */ /*LICENSE_END*/ +#include + #include "BrainConstants.h" #include "ChartableLineSeriesBrainordinateInterface.h" #include "CiftiMappableDataFile.h" namespace caret { + class CiftiParcelLabelFile; + class CiftiParcelReordering; + class CiftiParcelReorderingModel; + class SceneClassAssistant; + class CiftiParcelSeriesFile : public CiftiMappableDataFile, public ChartableLineSeriesBrainordinateInterface { @@ -51,8 +58,25 @@ namespace caret { virtual ChartDataCartesian* loadLineSeriesChartDataForVoxelAtCoordinate(const float xyz[3]); - virtual void getSupportedLineSeriesChartDataTypes(std::vector& chartDataTypesOut) const; + virtual void getSupportedLineSeriesChartDataTypes(std::vector& chartDataTypesOut) const; + + virtual void getSelectedParcelLabelFileAndMapForReordering(std::vector& compatibleParcelLabelFilesOut, + CiftiParcelLabelFile* &selectedParcelLabelFileOut, + int32_t& selectedParcelLabelFileMapIndexOut, + bool& enabledStatusOut) const; + + virtual void setSelectedParcelLabelFileAndMapForReordering(CiftiParcelLabelFile* selectedParcelLabelFile, + const int32_t selectedParcelLabelFileMapIndex, + const bool enabledStatus); + + virtual bool createParcelReordering(const CiftiParcelLabelFile* parcelLabelFile, + const int32_t parcelLabelFileMapIndex, + AString& errorMessageOut); + virtual const CiftiParcelReordering* getParcelReordering(const CiftiParcelLabelFile* parcelLabelFile, + const int32_t parcelLabelFileMapIndex) const; + + private: CiftiParcelSeriesFile(const CiftiParcelSeriesFile&); @@ -71,7 +95,12 @@ namespace caret { private: // ADD_NEW_MEMBERS_HERE + std::unique_ptr m_sceneAssistant; + bool m_chartingEnabledForTab[BrainConstants::MAXIMUM_NUMBER_OF_BROWSER_TABS]; + + std::unique_ptr m_parcelReorderingModel; + }; #ifdef __CIFTI_PARCEL_SERIES_FILE_DECLARE__ diff --git a/src/Files/CiftiScalarDataSeriesFile.cxx b/src/Files/CiftiScalarDataSeriesFile.cxx index 841cc71c8c2165de737ef337ae3b5b0fd0b4599d..7e86f58a97a04f1716fdda1c0d25f5ee4a0073c7 100644 --- a/src/Files/CiftiScalarDataSeriesFile.cxx +++ b/src/Files/CiftiScalarDataSeriesFile.cxx @@ -141,6 +141,8 @@ CiftiScalarDataSeriesFile::receiveEvent(Event* event) selectMapEvent->setEventProcessed(); } + + CiftiMappableDataFile::receiveEvent(event); } /** @@ -183,7 +185,7 @@ CiftiScalarDataSeriesFile::setMatrixRowColumnMapYokingGroup(const int32_t tabInd * Selected map index in the given tab. */ int32_t -CiftiScalarDataSeriesFile::getSelectedMapIndex(const int32_t tabIndex) +CiftiScalarDataSeriesFile::getSelectedMapIndex(const int32_t tabIndex) const { CaretAssertArrayIndex(m_selectedMapIndices, BrainConstants::MAXIMUM_NUMBER_OF_BROWSER_TABS, tabIndex); return m_selectedMapIndices[tabIndex]; @@ -198,11 +200,16 @@ CiftiScalarDataSeriesFile::getSelectedMapIndex(const int32_t tabIndex) * New value for selected map index. */ void -CiftiScalarDataSeriesFile::setSelectedMapIndex(const int32_t tabIndex, +CiftiScalarDataSeriesFile::setSelectedMapIndex(const int32_t /*tabIndex*/, const int32_t mapIndex) { - CaretAssertArrayIndex(m_selectedMapIndices, BrainConstants::MAXIMUM_NUMBER_OF_BROWSER_TABS, tabIndex); - m_selectedMapIndices[tabIndex] = mapIndex; + /* + * One map selection for all tabs + */ + for (int32_t i = 0; i < BrainConstants::MAXIMUM_NUMBER_OF_BROWSER_TABS; i++) { + CaretAssertArrayIndex(m_selectedMapIndices, BrainConstants::MAXIMUM_NUMBER_OF_BROWSER_TABS, i); + m_selectedMapIndices[i] = mapIndex; + } } @@ -307,11 +314,6 @@ CiftiScalarDataSeriesFile::getMatrixCellAttributes(const int32_t rowIndex, columnNameOut = (AString::number(time, 'f', 3) + " " + timeUnitsString); -// columnNameOut = (AString::number(time, 'f', 3) -// + " " -// + NiftiTimeUnitsEnum::toGuiName(getMapIntervalUnits()) -// + " Map Name: " -// + getMapName(columnIndex)); const int32_t numberOfElementsInRow = m_ciftiFile->getNumberOfColumns(); std::vector rowData(numberOfElementsInRow); @@ -372,10 +374,10 @@ CiftiScalarDataSeriesFile::setMatrixChartingEnabled(const int32_t tabIndex, * Chart types supported by this file. */ void -CiftiScalarDataSeriesFile::getSupportedMatrixChartDataTypes(std::vector& chartDataTypesOut) const +CiftiScalarDataSeriesFile::getSupportedMatrixChartDataTypes(std::vector& chartDataTypesOut) const { chartDataTypesOut.clear(); - chartDataTypesOut.push_back(ChartDataTypeEnum::CHART_DATA_TYPE_MATRIX_SERIES); + chartDataTypesOut.push_back(ChartOneDataTypeEnum::CHART_DATA_TYPE_MATRIX_SERIES); } /** @@ -442,11 +444,11 @@ CiftiScalarDataSeriesFile::loadLineSeriesChartDataForRow(const int32_t rowIndex) const CiftiXML& ciftiXML = m_ciftiFile->getCiftiXML(); const CiftiSeriesMap& seriesMap = ciftiXML.getSeriesMap(CiftiXML::ALONG_ROW); - ChartDataTypeEnum::Enum chartDataType = ChartDataTypeEnum::CHART_DATA_TYPE_INVALID; + ChartOneDataTypeEnum::Enum chartDataType = ChartOneDataTypeEnum::CHART_DATA_TYPE_INVALID; AString timeUnitsString; switch (seriesMap.getUnit()) { case CiftiSeriesMap::HERTZ: - chartDataType = ChartDataTypeEnum::CHART_DATA_TYPE_LINE_FREQUENCY_SERIES; + chartDataType = ChartOneDataTypeEnum::CHART_DATA_TYPE_LINE_FREQUENCY_SERIES; break; case CiftiSeriesMap::METER: CaretLogWarning("CIFTI Units METER not implemented"); @@ -455,11 +457,11 @@ CiftiScalarDataSeriesFile::loadLineSeriesChartDataForRow(const int32_t rowIndex) CaretLogWarning("CIFTI Units RADIAN not implemented"); break; case CiftiSeriesMap::SECOND: - chartDataType = ChartDataTypeEnum::CHART_DATA_TYPE_LINE_TIME_SERIES; + chartDataType = ChartOneDataTypeEnum::CHART_DATA_TYPE_LINE_TIME_SERIES; break; } - if (chartDataType != ChartDataTypeEnum::CHART_DATA_TYPE_INVALID) { + if (chartDataType != ChartOneDataTypeEnum::CHART_DATA_TYPE_INVALID) { chartData = new ChartDataCartesian(chartDataType, ChartAxisUnitsEnum::CHART_AXIS_UNITS_NONE, ChartAxisUnitsEnum::CHART_AXIS_UNITS_NONE); @@ -528,7 +530,7 @@ CiftiScalarDataSeriesFile::isLineSeriesChartingSupported() const * Chart types supported by this file. */ void -CiftiScalarDataSeriesFile::getSupportedLineSeriesChartDataTypes(std::vector& chartDataTypesOut) const +CiftiScalarDataSeriesFile::getSupportedLineSeriesChartDataTypes(std::vector& chartDataTypesOut) const { chartDataTypesOut.clear(); @@ -541,7 +543,7 @@ CiftiScalarDataSeriesFile::getSupportedLineSeriesChartDataTypes(std::vectorsaveMembers(sceneAttributes, sceneClass); /* @@ -630,6 +635,9 @@ void CiftiScalarDataSeriesFile::restoreFileDataFromScene(const SceneAttributes* sceneAttributes, const SceneClass* sceneClass) { + CiftiMappableDataFile::restoreFileDataFromScene(sceneAttributes, + sceneClass); + m_sceneAssistant->restoreMembers(sceneAttributes, sceneClass); diff --git a/src/Files/CiftiScalarDataSeriesFile.h b/src/Files/CiftiScalarDataSeriesFile.h index 17164d9334f8ee1127e9968b813dcfcc5527933b..d917f86192c488b97c0942feeb452d694dd523cb 100644 --- a/src/Files/CiftiScalarDataSeriesFile.h +++ b/src/Files/CiftiScalarDataSeriesFile.h @@ -35,8 +35,7 @@ namespace caret { class CiftiScalarDataSeriesFile : public CiftiMappableDataFile, public ChartableLineSeriesRowColumnInterface, - public ChartableMatrixSeriesInterface, - public EventListenerInterface { + public ChartableMatrixSeriesInterface { public: CiftiScalarDataSeriesFile(); @@ -48,7 +47,7 @@ namespace caret { virtual void setMatrixRowColumnMapYokingGroup(const int32_t tabIndex, const MapYokingGroupEnum::Enum yokingType); - virtual int32_t getSelectedMapIndex(const int32_t tabIndex); + virtual int32_t getSelectedMapIndex(const int32_t tabIndex) const override; virtual void setSelectedMapIndex(const int32_t tabIndex, const int32_t mapIndex); @@ -75,7 +74,7 @@ namespace caret { virtual void setMatrixChartingEnabled(const int32_t tabIndex, const bool enabled); - virtual void getSupportedMatrixChartDataTypes(std::vector& chartDataTypesOut) const; + virtual void getSupportedMatrixChartDataTypes(std::vector& chartDataTypesOut) const; const ChartMatrixDisplayProperties* getChartMatrixDisplayProperties(const int32_t tabIndex) const; @@ -92,7 +91,7 @@ namespace caret { virtual ChartDataCartesian* loadLineSeriesChartDataForRow(const int32_t rowIndex); - virtual void getSupportedLineSeriesChartDataTypes(std::vector& chartDataTypesOut) const; + virtual void getSupportedLineSeriesChartDataTypes(std::vector& chartDataTypesOut) const; // ADD_NEW_METHODS_HERE diff --git a/src/Files/EventCaretDataFilesGet.cxx b/src/Files/EventCaretDataFilesGet.cxx new file mode 100644 index 0000000000000000000000000000000000000000..48a5ab44188e4e7bee56445244a2cc36c198661d --- /dev/null +++ b/src/Files/EventCaretDataFilesGet.cxx @@ -0,0 +1,200 @@ + +/*LICENSE_START*/ +/* + * Copyright (C) 2018 Washington University School of Medicine + * + * 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 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ +/*LICENSE_END*/ + +#define __EVENT_CARET_DATA_FILES_GET_DECLARE__ +#include "EventCaretDataFilesGet.h" +#undef __EVENT_CARET_DATA_FILES_GET_DECLARE__ + +#include "CaretAssert.h" +#include "CaretDataFile.h" +#include "EventManager.h" +#include "EventTypeEnum.h" + +using namespace caret; + + + +/** + * \class caret::EventCaretDataFilesGet + * \brief Event to get CaretDataFiles + * \ingroup Files + */ + +/** + * Constructor. + * + * @param structure + * Structure. + * @param dataFileTypes + * Type of data files. + */ +EventCaretDataFilesGet::EventCaretDataFilesGet(const StructureEnum::Enum structure, + const std::vector& dataFileTypes) +: Event(EventTypeEnum::EVENT_CARET_DATA_FILES_GET) +, m_structure(structure), +m_dataFileTypes(dataFileTypes) +{ + +} + +/** + * Destructor. + */ +EventCaretDataFilesGet::~EventCaretDataFilesGet() +{ +} + +std::vector +EventCaretDataFilesGet::getAllCaretDataFiles() +{ + std::vector types; + + std::vector files; + run(StructureEnum::ALL, + types, + files); + + return files; +} + +/** + * @return All data files of the given data file type. + * + * @param dataFileType + * Type of data file. + */ +std::vector +EventCaretDataFilesGet::getCaretDataFilesForType(const DataFileTypeEnum::Enum dataFileType) +{ + std::vector types; + types.push_back(dataFileType); + + std::vector files; + run(StructureEnum::ALL, + types, + files); + + return files; +} + +/** + * @return All data files of the given multiple data file types. + * + * @param dataFileTypes + * Types of data files. + */ +std::vector +EventCaretDataFilesGet::getCaretDataFilesForTypes(const std::vector& dataFileTypes) +{ + std::vector files; + run(StructureEnum::ALL, + dataFileTypes, + files); + + return files; +} + +/** + * @return All data files of the given structure and multiple data file types. + * + * @param structure + * Structure. + * @param dataFileTypes + * Type of data files. + */ +std::vector +EventCaretDataFilesGet::getCaretDataFilesForStructureAndTypes(const StructureEnum::Enum structure, + const std::vector& dataFileTypes) +{ + std::vector files; + run(structure, + dataFileTypes, + files); + + return files; +} + +/** + * Add all caret data files (called by Brain). This method will filter the + * data files to meet the selection criteria. + * + * @param caretDataFiles + * All caret data files. + */ +void +EventCaretDataFilesGet::addAllCaretDataFiles(std::vector& caretDataFiles) +{ + m_caretDataFiles = caretDataFiles; +} + +/** + * Run the event to get files of the specified criteria. + * + * @param structure + * Structure. + * @param dataFileTypes + * Type of data files. + * @param caretDataFilesOut + * Output with caret data files matching selection criteria. + */ +void +EventCaretDataFilesGet::run(const StructureEnum::Enum structure, + const std::vector& dataFileTypes, + std::vector& caretDataFilesOut) +{ + caretDataFilesOut.clear(); + + EventCaretDataFilesGet instance(structure, + dataFileTypes); + EventManager::get()->sendEvent(instance.getPointer()); + + /* + * If data file types is empty it implies ALL file types + */ + const bool allDataFileTypesFlag = dataFileTypes.empty(); + + for (auto dataFile : instance.m_caretDataFiles) { + /* + * Match structure + */ + if (structure != StructureEnum::ALL) { + if (structure != dataFile->getStructure()) { + continue; + } + } + + /* + * Test data file type + */ + if (allDataFileTypesFlag) { + caretDataFilesOut.push_back(dataFile); + } + else { + if (std::find(dataFileTypes.begin(), + dataFileTypes.end(), + dataFile->getDataFileType()) != dataFileTypes.end()) { + caretDataFilesOut.push_back(dataFile); + } + } + } +} + + diff --git a/src/Files/EventCaretDataFilesGet.h b/src/Files/EventCaretDataFilesGet.h new file mode 100644 index 0000000000000000000000000000000000000000..5a9d206d08e4c13128feff84e49a4c1e8a23acf0 --- /dev/null +++ b/src/Files/EventCaretDataFilesGet.h @@ -0,0 +1,83 @@ +#ifndef __EVENT_CARET_DATA_FILES_GET_H__ +#define __EVENT_CARET_DATA_FILES_GET_H__ + +/*LICENSE_START*/ +/* + * Copyright (C) 2018 Washington University School of Medicine + * + * 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 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ +/*LICENSE_END*/ + + + +#include + +#include "DataFileTypeEnum.h" +#include "Event.h" +#include "StructureEnum.h" + + + +namespace caret { + class BorderFile; + class CaretDataFile; + + class EventCaretDataFilesGet : public Event { + + public: + virtual ~EventCaretDataFilesGet(); + + static std::vector getAllCaretDataFiles(); + + static std::vector getCaretDataFilesForType(const DataFileTypeEnum::Enum dataFileType); + + static std::vector getCaretDataFilesForTypes(const std::vector& dataFileTypes); + + static std::vector getCaretDataFilesForStructureAndTypes(const StructureEnum::Enum structure, + const std::vector& dataFileTypes); + + void addAllCaretDataFiles(std::vector& caretDataFiles); + + // ADD_NEW_METHODS_HERE + + private: + EventCaretDataFilesGet(const StructureEnum::Enum structure, + const std::vector& dataFileTypes); + + EventCaretDataFilesGet(const EventCaretDataFilesGet&); + + EventCaretDataFilesGet& operator=(const EventCaretDataFilesGet&); + + static void run(const StructureEnum::Enum structure, + const std::vector& dataFileTypes, + std::vector& caretDataFilesOut); + + const StructureEnum::Enum m_structure; + + const std::vector& m_dataFileTypes; + + std::vector m_caretDataFiles; + + // ADD_NEW_MEMBERS_HERE + + }; + +#ifdef __EVENT_CARET_DATA_FILES_GET_DECLARE__ + // +#endif // __EVENT_CARET_DATA_FILES_GET_DECLARE__ + +} // namespace +#endif //__EVENT_CARET_DATA_FILES_GET_H__ diff --git a/src/Brain/EventCaretMappableDataFileMapsViewedInOverlays.cxx b/src/Files/EventCaretMappableDataFileMapsViewedInOverlays.cxx similarity index 100% rename from src/Brain/EventCaretMappableDataFileMapsViewedInOverlays.cxx rename to src/Files/EventCaretMappableDataFileMapsViewedInOverlays.cxx diff --git a/src/Brain/EventCaretMappableDataFileMapsViewedInOverlays.h b/src/Files/EventCaretMappableDataFileMapsViewedInOverlays.h similarity index 100% rename from src/Brain/EventCaretMappableDataFileMapsViewedInOverlays.h rename to src/Files/EventCaretMappableDataFileMapsViewedInOverlays.h diff --git a/src/Files/EventMapYokingValidation.cxx b/src/Files/EventMapYokingValidation.cxx index eb3d66f3896e1d36ff7f4dbd65cda6506b04bf5f..8fa39389254c5e55b5aeff2d723ed1a8b4b9aab9 100644 --- a/src/Files/EventMapYokingValidation.cxx +++ b/src/Files/EventMapYokingValidation.cxx @@ -220,7 +220,7 @@ m_tabIndex(tabIndex) m_infoText = (AString::number(m_numberOfMaps) + " maps in tab " - + AString::number(tabIndex) + + AString::number(tabIndex + 1) + " file: " + caretMapFile->getFileNameNoPath()); } diff --git a/src/Files/GiftiTypeFile.cxx b/src/Files/GiftiTypeFile.cxx index 570b3a974b98d664a95bad89ea713d13d522af0d..eebefd3168adec1fda87ad433e1d714d9fda1bc8 100644 --- a/src/Files/GiftiTypeFile.cxx +++ b/src/Files/GiftiTypeFile.cxx @@ -150,6 +150,7 @@ GiftiTypeFile::readFile(const AString& filename) this->setFileName(filename); this->giftiFile->readFile(filename); this->validateDataArraysAfterReading(); + updateAfterFileDataChanges(); this->clearModified(); } @@ -514,8 +515,18 @@ const FastStatistics* GiftiTypeFile::getMapFastStatistics(const int32_t mapIndex const Histogram* GiftiTypeFile::getMapHistogram(const int32_t mapIndex) { + int32_t numberOfBuckets = 0; + switch (getPaletteNormalizationMode()) { + case PaletteNormalizationModeEnum::NORMALIZATION_ALL_MAP_DATA: + numberOfBuckets = getFileHistogramNumberOfBuckets(); + break; + case PaletteNormalizationModeEnum::NORMALIZATION_SELECTED_MAP_DATA: + numberOfBuckets = getMapPaletteColorMapping(mapIndex)->getHistogramNumberOfBuckets(); + break; + } + const GiftiDataArray* gda = this->giftiFile->getDataArray(mapIndex); - return gda->getHistogram(); + return gda->getHistogram(numberOfBuckets); } const Histogram* GiftiTypeFile::getMapHistogram(const int32_t mapIndex, @@ -525,9 +536,19 @@ const Histogram* GiftiTypeFile::getMapHistogram(const int32_t mapIndex, const float mostNegativeValueInclusive, const bool includeZeroValues) { + int32_t numberOfBuckets = 0; + switch (getPaletteNormalizationMode()) { + case PaletteNormalizationModeEnum::NORMALIZATION_ALL_MAP_DATA: + numberOfBuckets = getFileHistogramNumberOfBuckets(); + break; + case PaletteNormalizationModeEnum::NORMALIZATION_SELECTED_MAP_DATA: + numberOfBuckets = getMapPaletteColorMapping(mapIndex)->getHistogramNumberOfBuckets(); + break; + } const GiftiDataArray* gda = this->giftiFile->getDataArray(mapIndex); - return gda->getHistogram(mostPositiveValueInclusive, - leastPositiveValueInclusive, + return gda->getHistogram(numberOfBuckets, + mostPositiveValueInclusive, + leastPositiveValueInclusive, leastNegativeValueInclusive, mostNegativeValueInclusive, includeZeroValues); @@ -643,15 +664,32 @@ GiftiTypeFile::getFileFastStatistics() const Histogram* GiftiTypeFile::getFileHistogram() { - if (m_fileHistogram == NULL) { + const int32_t numBuckets = getFileHistogramNumberOfBuckets(); + bool updateHistogramFlag = false; + if (m_fileHistogram != NULL) { + if (numBuckets != m_histogramNumberOfBuckets) { + updateHistogramFlag = true; + } + } + else { + updateHistogramFlag = true; + + } + + if (updateHistogramFlag) { std::vector fileData; getFileDataFloat(fileData); if ( ! fileData.empty()) { - m_fileHistogram.grabNew(new Histogram()); - m_fileHistogram->update(&fileData[0], + if (m_fileHistogram == NULL) { + m_fileHistogram.grabNew(new Histogram(numBuckets)); + } + m_fileHistogram->update(numBuckets, + &fileData[0], fileData.size()); + m_histogramNumberOfBuckets = numBuckets; } } + return m_fileHistogram; } @@ -681,9 +719,11 @@ GiftiTypeFile::getFileHistogram(const float mostPositiveValueInclusive, const float mostNegativeValueInclusive, const bool includeZeroValues) { + const int32_t numberOfBuckets = getFileHistogramNumberOfBuckets(); bool updateHistogramFlag = false; if (m_fileHistorgramLimitedValues != NULL) { - if ((mostPositiveValueInclusive != m_fileHistogramLimitedValuesMostPositiveValueInclusive) + if ((numberOfBuckets != m_fileHistogramLimitedValuesNumberOfBuckets) + || (mostPositiveValueInclusive != m_fileHistogramLimitedValuesMostPositiveValueInclusive) || (leastPositiveValueInclusive != m_fileHistogramLimitedValuesLeastPositiveValueInclusive) || (leastNegativeValueInclusive != m_fileHistogramLimitedValuesLeastNegativeValueInclusive) || (mostNegativeValueInclusive != m_fileHistogramLimitedValuesMostNegativeValueInclusive) @@ -702,7 +742,8 @@ GiftiTypeFile::getFileHistogram(const float mostPositiveValueInclusive, if (m_fileHistorgramLimitedValues == NULL) { m_fileHistorgramLimitedValues.grabNew(new Histogram()); } - m_fileHistorgramLimitedValues->update(&fileData[0], + m_fileHistorgramLimitedValues->update(numberOfBuckets, + &fileData[0], fileData.size(), mostPositiveValueInclusive, leastPositiveValueInclusive, @@ -710,6 +751,7 @@ GiftiTypeFile::getFileHistogram(const float mostPositiveValueInclusive, mostNegativeValueInclusive, includeZeroValues); + m_fileHistogramLimitedValuesNumberOfBuckets = numberOfBuckets; m_fileHistogramLimitedValuesMostPositiveValueInclusive = mostPositiveValueInclusive; m_fileHistogramLimitedValuesLeastPositiveValueInclusive = leastPositiveValueInclusive; m_fileHistogramLimitedValuesLeastNegativeValueInclusive = leastNegativeValueInclusive; @@ -719,6 +761,45 @@ GiftiTypeFile::getFileHistogram(const float mostPositiveValueInclusive, } return m_fileHistorgramLimitedValues; +// bool updateHistogramFlag = false; +// if (m_fileHistorgramLimitedValues != NULL) { +// if ((mostPositiveValueInclusive != m_fileHistogramLimitedValuesMostPositiveValueInclusive) +// || (leastPositiveValueInclusive != m_fileHistogramLimitedValuesLeastPositiveValueInclusive) +// || (leastNegativeValueInclusive != m_fileHistogramLimitedValuesLeastNegativeValueInclusive) +// || (mostNegativeValueInclusive != m_fileHistogramLimitedValuesMostNegativeValueInclusive) +// || (includeZeroValues != m_fileHistogramLimitedValuesIncludeZeroValues)) { +// updateHistogramFlag = true; +// } +// } +// else { +// updateHistogramFlag = true; +// } +// +// if (updateHistogramFlag) { +// std::vector fileData; +// getFileDataFloat(fileData); +// if ( ! fileData.empty()) { +// if (m_fileHistorgramLimitedValues == NULL) { +// m_fileHistorgramLimitedValues.grabNew(new Histogram()); +// } +// m_fileHistorgramLimitedValues->update(getFileHistogramNumberOfBuckets(), +// &fileData[0], +// fileData.size(), +// mostPositiveValueInclusive, +// leastPositiveValueInclusive, +// leastNegativeValueInclusive, +// mostNegativeValueInclusive, +// includeZeroValues); +// +// m_fileHistogramLimitedValuesMostPositiveValueInclusive = mostPositiveValueInclusive; +// m_fileHistogramLimitedValuesLeastPositiveValueInclusive = leastPositiveValueInclusive; +// m_fileHistogramLimitedValuesLeastNegativeValueInclusive = leastNegativeValueInclusive; +// m_fileHistogramLimitedValuesMostNegativeValueInclusive = mostNegativeValueInclusive; +// m_fileHistogramLimitedValuesIncludeZeroValues = includeZeroValues; +// } +// } +// +// return m_fileHistorgramLimitedValues; } /** @@ -744,7 +825,7 @@ GiftiTypeFile::isMappedWithPalette() const * vector is assumed to be the default mode. */ void -GiftiTypeFile::getPaletteNormalizationModesSupported(std::vector& modesSupportedOut) +GiftiTypeFile::getPaletteNormalizationModesSupported(std::vector& modesSupportedOut) const { modesSupportedOut.clear(); @@ -882,10 +963,9 @@ GiftiTypeFile::getMapIndexFromUniqueID(const AString& uniqueID) const * Palette file containing palettes. */ void -GiftiTypeFile::updateScalarColoringForMap(const int32_t /*mapIndex*/, - const PaletteFile* /*paletteFile*/) +GiftiTypeFile::updateScalarColoringForMap(const int32_t /*mapIndex*/) { - /* no volumes in gifti */ + invalidateHistogramChartColoring(); } /** @@ -903,4 +983,107 @@ GiftiTypeFile::addToDataFileContentInformation(DataFileContentInformation& dataF getNumberOfNodes()); } +/** + * Get data from the file as requested in the given map file data selector. + * + * @param mapFileDataSelector + * Specifies selection of data. + * @param dataOut + * Output with data. Will be empty if data does not support the map file data selector. + */ +void +GiftiTypeFile::getDataForSelector(const MapFileDataSelector& /*mapFileDataSelector*/, + std::vector& dataOut) const +{ + dataOut.clear(); +} + +/** + * Are all brainordinates in this file also in the given file? + * That is, the brainordinates are equal to or a subset of the brainordinates + * in the given file. + * + * @param mapFile + * The given map file. + * @return + * Match status. + */ +CaretMappableDataFile::BrainordinateMappingMatch +GiftiTypeFile::getBrainordinateMappingMatch(const CaretMappableDataFile* mapFile) const +{ + bool giftiFlag = false; + CaretAssert(mapFile); + switch (mapFile->getDataFileType()) { + case DataFileTypeEnum::ANNOTATION: + break; + case DataFileTypeEnum::BORDER: + break; + case DataFileTypeEnum::CONNECTIVITY_DENSE: + break; + case DataFileTypeEnum::CONNECTIVITY_DENSE_DYNAMIC: + break; + case DataFileTypeEnum::CONNECTIVITY_DENSE_LABEL: + break; + case DataFileTypeEnum::CONNECTIVITY_DENSE_PARCEL: + break; + case DataFileTypeEnum::CONNECTIVITY_DENSE_SCALAR: + break; + case DataFileTypeEnum::CONNECTIVITY_DENSE_TIME_SERIES: + break; + case DataFileTypeEnum::CONNECTIVITY_FIBER_ORIENTATIONS_TEMPORARY: + break; + case DataFileTypeEnum::CONNECTIVITY_FIBER_TRAJECTORY_TEMPORARY: + break; + case DataFileTypeEnum::CONNECTIVITY_PARCEL: + break; + case DataFileTypeEnum::CONNECTIVITY_PARCEL_DENSE: + break; + case DataFileTypeEnum::CONNECTIVITY_PARCEL_LABEL: + break; + case DataFileTypeEnum::CONNECTIVITY_PARCEL_SCALAR: + break; + case DataFileTypeEnum::CONNECTIVITY_PARCEL_SERIES: + break; + case DataFileTypeEnum::CONNECTIVITY_SCALAR_DATA_SERIES: + break; + case DataFileTypeEnum::FOCI: + break; + case DataFileTypeEnum::IMAGE: + break; + case DataFileTypeEnum::LABEL: + giftiFlag = true; + break; + case DataFileTypeEnum::METRIC: + giftiFlag = true; + break; + case DataFileTypeEnum::PALETTE: + break; + case DataFileTypeEnum::RGBA: + giftiFlag = true; + break; + case DataFileTypeEnum::SCENE: + break; + case DataFileTypeEnum::SPECIFICATION: + break; + case DataFileTypeEnum::SURFACE: + giftiFlag = true; + break; + case DataFileTypeEnum::UNKNOWN: + break; + case DataFileTypeEnum::VOLUME: + break; + } + + if (giftiFlag) { + const GiftiTypeFile* gtf = dynamic_cast(mapFile); + CaretAssert(gtf); + if ((this->getNumberOfNodes() == gtf->getNumberOfNodes()) + && (this->getStructure() == gtf->getStructure())) { + return BrainordinateMappingMatch::EQUAL; + } + } + + return BrainordinateMappingMatch::NO; +} + diff --git a/src/Files/GiftiTypeFile.h b/src/Files/GiftiTypeFile.h index 854d3ca3913c365677350388369f98dabdeb8f69..aef32ce515b688b6d7e486fef1113ae1c7714cb6 100644 --- a/src/Files/GiftiTypeFile.h +++ b/src/Files/GiftiTypeFile.h @@ -146,7 +146,7 @@ namespace caret { virtual bool isMappedWithPalette() const; - virtual void getPaletteNormalizationModesSupported(std::vector& modesSupportedOut); + virtual void getPaletteNormalizationModesSupported(std::vector& modesSupportedOut) const; virtual PaletteColorMapping* getMapPaletteColorMapping(const int32_t mapIndex); @@ -163,8 +163,13 @@ namespace caret { virtual int32_t getMapIndexFromUniqueID(const AString& uniqueID) const; - virtual void updateScalarColoringForMap(const int32_t mapIndex, - const PaletteFile* paletteFile); + virtual void updateScalarColoringForMap(const int32_t mapIndex) override; + + virtual void getDataForSelector(const MapFileDataSelector& mapFileDataSelector, + std::vector& dataOut) const override; + + virtual BrainordinateMappingMatch getBrainordinateMappingMatch(const CaretMappableDataFile* mapFile) const override; + private: void copyHelperGiftiTypeFile(const GiftiTypeFile& gtf); @@ -176,9 +181,12 @@ namespace caret { /** Histogram used when statistics computed on all data in file */ CaretPointer m_fileHistogram; + int32_t m_histogramNumberOfBuckets = 100; + /** Histogram with limited values used when statistics computed on all data in file */ CaretPointer m_fileHistorgramLimitedValues; + int32_t m_fileHistogramLimitedValuesNumberOfBuckets = 100; float m_fileHistogramLimitedValuesMostPositiveValueInclusive; float m_fileHistogramLimitedValuesLeastPositiveValueInclusive; float m_fileHistogramLimitedValuesLeastNegativeValueInclusive; diff --git a/src/Files/GroupAndNameHierarchyItem.cxx b/src/Files/GroupAndNameHierarchyItem.cxx index b80f04d8fb8d7e693b1819089aef058214e8fd85..ac5154b70048de1886733ff84483dca6144143ce 100644 --- a/src/Files/GroupAndNameHierarchyItem.cxx +++ b/src/Files/GroupAndNameHierarchyItem.cxx @@ -928,6 +928,7 @@ GroupAndNameHierarchyItem::restoreFromScene(const SceneAttributes* sceneAttribut return; } + sceneClass->getStringValue("m_name"); // prevents "failed to restore" m_sceneAssistant->restoreMembers(sceneAttributes, sceneClass); diff --git a/src/Files/ImageFile.cxx b/src/Files/ImageFile.cxx index 23e117302aa659f7b850fb2611ef2ecbf2982d51..45261135afc7c91321586606893e69ffc9d0cfe5 100644 --- a/src/Files/ImageFile.cxx +++ b/src/Files/ImageFile.cxx @@ -37,7 +37,6 @@ #include "ImageFile.h" #include "Matrix4x4.h" #include "MathFunctions.h" -#include "PaletteFile.h" #include "SceneClass.h" #include "VolumeFile.h" @@ -879,7 +878,7 @@ ImageFile::writeFile(const AString& filename) format = "JPEG"; } - QImageWriter writer(filename, format.toAscii()); + QImageWriter writer(filename, format.toLatin1()); if (writer.supportsOption(QImageIOHandler::Quality)) { if (format.compare("png", Qt::CaseInsensitive) == 0) { const int quality = 1; @@ -1377,7 +1376,7 @@ ImageFile::getImageInByteArray(QByteArray& byteArrayOut, } else { successFlag = m_image->save(&buffer, - format.toAscii().data()); + format.toLatin1().data()); } if ( ! successFlag) { @@ -1396,7 +1395,7 @@ ImageFile::getImageInByteArray(QByteArray& byteArrayOut, * @param format * Format for the image (jpg, ppm, etc.) or empty if unknown. */ -void +bool ImageFile::setImageFromByteArray(const QByteArray& byteArray, const AString& format) { @@ -1406,13 +1405,15 @@ ImageFile::setImageFromByteArray(const QByteArray& byteArray, } else { successFlag = m_image->loadFromData(byteArray, - format.toAscii().data()); + format.toLatin1().data()); } if ( ! successFlag) { - throw DataFileException(getFileName(), - "Failed to create image from byte array."); + CaretLogSevere(getFileName() + + " Failed to create image from byte array."); } + + return successFlag; } /** @@ -1422,8 +1423,6 @@ ImageFile::setImageFromByteArray(const QByteArray& byteArray, * * @param colorMode * Color mode for conversion. - * @param paletteFile - * Palette file used for coloring the voxels. * @param errorMessageOut * Contains error message if conversion fails. * @return @@ -1434,7 +1433,6 @@ ImageFile::setImageFromByteArray(const QByteArray& byteArray, */ VolumeFile* ImageFile::convertToVolumeFile(const CONVERT_TO_VOLUME_COLOR_MODE colorMode, - const PaletteFile* paletteFile, AString& errorMessageOut) const { errorMessageOut.clear(); @@ -1458,7 +1456,7 @@ ImageFile::convertToVolumeFile(const CONVERT_TO_VOLUME_COLOR_MODE colorMode, transformationMatrix->multiplyPoint3(firstPixel); std::cout << "First pixel coord: " << AString::fromNumbers(firstPixel, 3, ",") << std::endl; - float lastPixel[3] = { width - 1, height - 1, 0 }; + float lastPixel[3] = { (float)(width - 1), (float)(height - 1), 0.0f }; transformationMatrix->multiplyPoint3(lastPixel); std::cout << "Last pixel coord: " << AString::fromNumbers(lastPixel, 3, ",") << std::endl; @@ -1467,13 +1465,13 @@ ImageFile::convertToVolumeFile(const CONVERT_TO_VOLUME_COLOR_MODE colorMode, transformationMatrix->multiplyPoint3(bl); ControlPoint3D bottomLeft(0, 0, 0, bl[0], bl[1], bl[2]); - float br[3] = { width - 1.0, 0.0, 0.0 }; + float br[3] = { (float)(width - 1.0), 0.0, 0.0 }; transformationMatrix->multiplyPoint3(br); ControlPoint3D bottomRight(width - 1.0, 0.0, 0.0, br[0], br[1], br[2]); - float tr[3] = { width - 1.0, height - 1.0, 0.0 }; + float tr[3] = { (float)(width - 1.0), (float)(height - 1.0), 0.0 }; transformationMatrix->multiplyPoint3(tr); - ControlPoint3D topRight(width - 1.0, height - 1.0, 0.0, tr[0], tr[1], tr[2]); + ControlPoint3D topRight((float)(width - 1.0), (float)(height - 1.0), 0.0, tr[0], tr[1], tr[2]); ControlPointFile volumeControlPointFile; volumeControlPointFile.addControlPoint(bottomLeft); @@ -1593,8 +1591,7 @@ ImageFile::convertToVolumeFile(const CONVERT_TO_VOLUME_COLOR_MODE colorMode, } volumeFile->clearVoxelColoringForMap(mapIndex); - volumeFile->updateScalarColoringForMap(mapIndex, - paletteFile); + volumeFile->updateScalarColoringForMap(mapIndex); return volumeFile; } diff --git a/src/Files/ImageFile.h b/src/Files/ImageFile.h index 817da19fd7af2fdac7bb769ea7b4e4faa0fb6b6f..fd64cf4caa40dc36b9dac5fe6518f398612a0177 100644 --- a/src/Files/ImageFile.h +++ b/src/Files/ImageFile.h @@ -29,7 +29,6 @@ class QImage; namespace caret { class ControlPointFile; class ControlPoint3D; - class PaletteFile; class VolumeFile; /// File for images @@ -184,7 +183,7 @@ public: void getImageInByteArray(QByteArray& byteArrayOut, const AString& format) const; - void setImageFromByteArray(const QByteArray& byteArray, + bool setImageFromByteArray(const QByteArray& byteArray, const AString& format); void combinePreservingAspectAndFillIfNeeded(const std::vector& imageFiles, @@ -198,7 +197,6 @@ public: AString& defaultFilter); VolumeFile* convertToVolumeFile(const CONVERT_TO_VOLUME_COLOR_MODE colorMode, - const PaletteFile* paletteFile, AString& errorMessageOut) const; ControlPointFile* getControlPointFile(); diff --git a/src/Files/LabelDrawingProperties.cxx b/src/Files/LabelDrawingProperties.cxx index fc1cd16d26b8da558f9dda5075c4c6996930109f..a0d06acbda2de82939593ee3d89f6daf66052482 100644 --- a/src/Files/LabelDrawingProperties.cxx +++ b/src/Files/LabelDrawingProperties.cxx @@ -42,11 +42,58 @@ using namespace caret; */ LabelDrawingProperties::LabelDrawingProperties() : CaretObject() +{ + initializeInstance(); +} + +/** + * Destructor. + */ +LabelDrawingProperties::~LabelDrawingProperties() +{ + delete m_sceneAssistant; +} + +/** + * Copy constructor. + * @param obj + * Object that is copied. + */ +LabelDrawingProperties::LabelDrawingProperties(const LabelDrawingProperties& obj) +: CaretObject(obj), +SceneableInterface(obj) +{ + initializeInstance(); + copyHelper(obj); +} + +/** + * Assignment operator. + * @param obj + * Data copied from obj to this. + * @return + * Reference to this object. + */ +LabelDrawingProperties& +LabelDrawingProperties::operator=(const LabelDrawingProperties& obj) +{ + if (this != &obj) { + CaretObject::operator=(obj); + this->copyHelper(obj); + } + return *this; +} + +/** + * Initialize an instance of this class. + */ +void +LabelDrawingProperties::initializeInstance() { m_drawingType = LabelDrawingTypeEnum::DRAW_FILLED; m_outlineColor = CaretColorEnum::BLACK; m_drawMedialWallFilled = true; - + m_sceneAssistant = new SceneClassAssistant(); m_sceneAssistant->add("m_drawingType", &m_drawingType); @@ -56,15 +103,20 @@ LabelDrawingProperties::LabelDrawingProperties() &m_drawMedialWallFilled); } + /** - * Destructor. + * Helps with copying an object of this type. + * @param obj + * Object that is copied. */ -LabelDrawingProperties::~LabelDrawingProperties() +void +LabelDrawingProperties::copyHelper(const LabelDrawingProperties& obj) { - delete m_sceneAssistant; + m_drawingType = obj.m_drawingType; + m_outlineColor = obj.m_outlineColor; + m_drawMedialWallFilled = obj.m_drawMedialWallFilled; } - /** * @return The drawing type. */ diff --git a/src/Files/LabelDrawingProperties.h b/src/Files/LabelDrawingProperties.h index 68c2f2b53ab565083713376e81db5b30bfaee79b..1a928e465726a0c122a77757516f8ff5e38810d0 100644 --- a/src/Files/LabelDrawingProperties.h +++ b/src/Files/LabelDrawingProperties.h @@ -39,6 +39,10 @@ namespace caret { virtual ~LabelDrawingProperties(); + LabelDrawingProperties(const LabelDrawingProperties&); + + LabelDrawingProperties& operator=(const LabelDrawingProperties&); + LabelDrawingTypeEnum::Enum getDrawingType() const; void setDrawingType(const LabelDrawingTypeEnum::Enum drawingType); @@ -75,9 +79,10 @@ namespace caret { // const SceneClass* sceneClass) = 0; private: - LabelDrawingProperties(const LabelDrawingProperties&); - - LabelDrawingProperties& operator=(const LabelDrawingProperties&); + + void initializeInstance(); + + void copyHelper(const LabelDrawingProperties& obj); SceneClassAssistant* m_sceneAssistant; diff --git a/src/Files/MetricFile.cxx b/src/Files/MetricFile.cxx index dd9f3c6784772af1b58d44872d9cf2518b66cae9..c7892b114b594c2ec3ccc0e1ce804eb5cb66e145 100644 --- a/src/Files/MetricFile.cxx +++ b/src/Files/MetricFile.cxx @@ -25,6 +25,7 @@ #include "DataFileException.h" #include "DataFileTypeEnum.h" #include "GiftiFile.h" +#include "MapFileDataSelector.h" #include "MathFunctions.h" #include "MetricFile.h" #include "NiftiEnums.h" @@ -475,7 +476,7 @@ MetricFile::setLineSeriesChartingEnabled(const int32_t tabIndex, * Chart types supported by this file. */ void -MetricFile::getSupportedLineSeriesChartDataTypes(std::vector& chartDataTypesOut) const +MetricFile::getSupportedLineSeriesChartDataTypes(std::vector& chartDataTypesOut) const { helpGetSupportedLineSeriesChartDataTypes(chartDataTypesOut); } @@ -617,6 +618,105 @@ MetricFile::loadLineSeriesChartDataForVoxelAtCoordinate(const float * /*xyz[3]*/ return chartData; } +/** + * Get data from the file as requested in the given map file data selector. + * + * @param mapFileDataSelector + * Specifies selection of data. + * @param dataOut + * Output with data. Will be empty if data does not support the map file data selector. + */ +void +MetricFile::getDataForSelector(const MapFileDataSelector& mapFileDataSelector, + std::vector& dataOut) const +{ + dataOut.clear(); + + switch (mapFileDataSelector.getDataSelectionType()) { + case MapFileDataSelector::DataSelectionType::INVALID: + break; + case MapFileDataSelector::DataSelectionType::COLUMN_DATA: + break; + case MapFileDataSelector::DataSelectionType::ROW_DATA: + break; + case MapFileDataSelector::DataSelectionType::SURFACE_VERTEX: + try { + StructureEnum::Enum structure = StructureEnum::INVALID; + int32_t surfaceNumberOfVertices = -1; + int32_t vertexIndex = -1; + mapFileDataSelector.getSurfaceVertex(structure, + surfaceNumberOfVertices, + vertexIndex); + + if ((structure == getStructure()) + && (surfaceNumberOfVertices == getNumberOfNodes())) { + const int32_t numMaps = getNumberOfMaps(); + + for (int64_t iMap = 0; iMap < numMaps; iMap++) { + dataOut.push_back(getValue(vertexIndex, + iMap)); + } + } + } + catch (const DataFileException& dfe) { + CaretLogWarning("Exeception: " + + dfe.whatString()); + dataOut.clear(); + } + break; + case MapFileDataSelector::DataSelectionType::SURFACE_VERTICES_AVERAGE: + { + try { + StructureEnum::Enum structure = StructureEnum::INVALID; + int32_t surfaceNumberOfVertices = -1; + std::vector vertexIndices; + mapFileDataSelector.getSurfaceVertexAverage(structure, + surfaceNumberOfVertices, + vertexIndices); + + if ((structure == getStructure()) + && (surfaceNumberOfVertices == getNumberOfNodes())) { + const int32_t numberOfVertexIndices = static_cast(vertexIndices.size()); + const int32_t numberOfMaps = getNumberOfMaps(); + + if ((numberOfVertexIndices > 0) + && (numberOfMaps > 0)) { + + std::vector dataSum(numberOfMaps, 0.0); + + for (int32_t iMap = 0; iMap < numberOfMaps; iMap++) { + CaretAssertVectorIndex(dataSum, iMap); + + for (int32_t iNode = 0; iNode < numberOfVertexIndices; iNode++) { + const int32_t nodeIndex = vertexIndices[iNode]; + dataSum[iMap] += getValue(nodeIndex, + iMap); + } + } + + dataOut.resize(numberOfMaps); + for (int32_t iMap = 0; iMap < numberOfMaps; iMap++) { + CaretAssertVectorIndex(dataSum, iMap); + + const float mapAverageValue = dataSum[iMap] / numberOfVertexIndices; + dataOut[iMap] = mapAverageValue; + } + } + } + } + catch (const DataFileException& dfe) { + CaretLogWarning("Exeception: " + + dfe.whatString()); + dataOut.clear(); + } + } + break; + case MapFileDataSelector::DataSelectionType::VOLUME_XYZ: + break; + } +} + + /** * Save file data from the scene. For subclasses that need to * save to a scene, this method should be overriden. sceneClass diff --git a/src/Files/MetricFile.h b/src/Files/MetricFile.h index eedd1ea2dfbbd7831af7c194de8c7aaa8a23ae3a..b22298dda1125bff1f641eba7a7e138dba5fb495 100644 --- a/src/Files/MetricFile.h +++ b/src/Files/MetricFile.h @@ -90,7 +90,11 @@ namespace caret { virtual ChartDataCartesian* loadLineSeriesChartDataForVoxelAtCoordinate(const float xyz[3]); - virtual void getSupportedLineSeriesChartDataTypes(std::vector& chartDataTypesOut) const; + virtual void getSupportedLineSeriesChartDataTypes(std::vector& chartDataTypesOut) const; + + void getDataForSelector(const MapFileDataSelector& mapFileDataSelector, + std::vector& dataOut) const override; + //override writeFile in order to check filename against type of file virtual void writeFile(const AString& filename); diff --git a/src/Files/MetricSmoothingObject.cxx b/src/Files/MetricSmoothingObject.cxx index d35f21ffcd1c5b9b40381d84f7ea4f89d652f1da..c2ea05cf5273c9a40206fec005c11ebdb71e51d8 100644 --- a/src/Files/MetricSmoothingObject.cxx +++ b/src/Files/MetricSmoothingObject.cxx @@ -277,16 +277,17 @@ void MetricSmoothingObject::smoothColumnInternal(float* scratch, const MetricFil metricOut->setValuesForColumn(whichOutColumn, scratch); } -void MetricSmoothingObject::precomputeWeightsGeoGauss(const SurfaceFile* mySurf, float myKernel) +void MetricSmoothingObject::precomputeWeightsGeoGauss(const SurfaceFile* mySurf, float myKernel, const float* nodeAreas) { int32_t numNodes = mySurf->getNumberOfNodes(); float myGeoDist = myKernel * 3.0f; float gaussianDenom = -0.5f / myKernel / myKernel; m_weightLists.resize(numNodes); + CaretPointer myGeoBase(new GeodesicHelperBase(mySurf, nodeAreas));//NOTE: if these are equal to the surface's areas, then it does some extra operations, but gets the same answer #pragma omp CARET_PAR { CaretPointer myTopoHelp = mySurf->getTopologyHelper();//don't really need one per thread here, but good practice in case we want getNeighborsToDepth - CaretPointer myGeoHelp = mySurf->getGeodesicHelper(); + CaretPointer myGeoHelp(new GeodesicHelper(myGeoBase)); vector distances; #pragma omp CARET_FOR schedule(dynamic) for (int32_t i = 0; i < numNodes; ++i) @@ -311,17 +312,18 @@ void MetricSmoothingObject::precomputeWeightsGeoGauss(const SurfaceFile* mySurf, } } -void MetricSmoothingObject::precomputeWeightsROIGeoGauss(const SurfaceFile* mySurf, float myKernel, const MetricFile* theRoi) +void MetricSmoothingObject::precomputeWeightsROIGeoGauss(const SurfaceFile* mySurf, float myKernel, const MetricFile* theRoi, const float* nodeAreas) { int32_t numNodes = mySurf->getNumberOfNodes(); float myGeoDist = myKernel * 3.0f; float gaussianDenom = -0.5f / myKernel / myKernel; m_weightLists.resize(numNodes); const float* myRoiColumn = theRoi->getValuePointerForColumn(0); + CaretPointer myGeoBase(new GeodesicHelperBase(mySurf, nodeAreas));//NOTE: if these are equal to the surface's areas, then it does some extra operations, but gets the same answer #pragma omp CARET_PAR { CaretPointer myTopoHelp = mySurf->getTopologyHelper(); - CaretPointer myGeoHelp = mySurf->getGeodesicHelper(); + CaretPointer myGeoHelp(new GeodesicHelper(myGeoBase)); vector distances; vector nodes; #pragma omp CARET_FOR schedule(dynamic) @@ -490,17 +492,18 @@ void MetricSmoothingObject::precomputeWeightsROIGeoGaussArea(const SurfaceFile* } } -void MetricSmoothingObject::precomputeWeightsGeoGaussEqual(const SurfaceFile* mySurf, float myKernel) +void MetricSmoothingObject::precomputeWeightsGeoGaussEqual(const SurfaceFile* mySurf, float myKernel, const float* nodeAreas) {//this method is normalized in two ways to provide evenly diffusing smoothing with equivalent sum of values as input - this special purpose smoothing is for things that should not be integrated across the surface int32_t numNodes = mySurf->getNumberOfNodes(); float myGeoDist = myKernel * 3.0f; float gaussianDenom = -0.5f / myKernel / myKernel; vector tempList;//this is used to compute scattering kernels because it is easier to normalize scattering kernels correctly, and then convert to gathering kernels tempList.resize(numNodes); + CaretPointer myGeoBase(new GeodesicHelperBase(mySurf, nodeAreas));//NOTE: if these are equal to the surface's areas, then it does some extra operations, but gets the same answer #pragma omp CARET_PAR { CaretPointer myTopoHelp = mySurf->getTopologyHelper();//don't really need one per thread here, but good practice in case we want getNeighborsToDepth - CaretPointer myGeoHelp = mySurf->getGeodesicHelper(); + CaretPointer myGeoHelp(new GeodesicHelper(myGeoBase)); vector distances; #pragma omp CARET_FOR schedule(dynamic) for (int32_t i = 0; i < numNodes; ++i) @@ -552,7 +555,7 @@ void MetricSmoothingObject::precomputeWeightsGeoGaussEqual(const SurfaceFile* my } } -void MetricSmoothingObject::precomputeWeightsROIGeoGaussEqual(const SurfaceFile* mySurf, float myKernel, const MetricFile* theRoi) +void MetricSmoothingObject::precomputeWeightsROIGeoGaussEqual(const SurfaceFile* mySurf, float myKernel, const MetricFile* theRoi, const float* nodeAreas) { int32_t numNodes = mySurf->getNumberOfNodes(); float myGeoDist = myKernel * 3.0f; @@ -560,10 +563,11 @@ void MetricSmoothingObject::precomputeWeightsROIGeoGaussEqual(const SurfaceFile* vector tempList;//this is used to compute scattering kernels because it is easier to normalize scattering kernels correctly, and then convert to gathering kernels tempList.resize(numNodes); const float* myRoiColumn = theRoi->getValuePointerForColumn(0); + CaretPointer myGeoBase(new GeodesicHelperBase(mySurf, nodeAreas));//NOTE: if these are equal to the surface's areas, then it does some extra operations, but gets the same answer #pragma omp CARET_PAR { CaretPointer myTopoHelp = mySurf->getTopologyHelper(); - CaretPointer myGeoHelp = mySurf->getGeodesicHelper(); + CaretPointer myGeoHelp(new GeodesicHelper(myGeoBase)); vector distances; vector nodes; #pragma omp CARET_FOR schedule(dynamic) @@ -627,17 +631,10 @@ void MetricSmoothingObject::precomputeWeights(const SurfaceFile* mySurf, float m { const float* passAreas = nodeAreas; vector areasTemp; - switch (myMethod) - { - case GEO_GAUSS_AREA://currently, only this method needs them - if (passAreas == NULL) - { - mySurf->computeNodeAreas(areasTemp); - passAreas = areasTemp.data(); - } - break; - default: - break; + if (passAreas == NULL) + {//corrected node areas need to be passed into all geodesic smoothings, to generate corrected distances + mySurf->computeNodeAreas(areasTemp); + passAreas = areasTemp.data(); } if (theRoi != NULL) { @@ -647,10 +644,10 @@ void MetricSmoothingObject::precomputeWeights(const SurfaceFile* mySurf, float m precomputeWeightsROIGeoGaussArea(mySurf, myKernel, theRoi, passAreas); break; case GEO_GAUSS_EQUAL: - precomputeWeightsROIGeoGaussEqual(mySurf, myKernel, theRoi); + precomputeWeightsROIGeoGaussEqual(mySurf, myKernel, theRoi, passAreas); break; case GEO_GAUSS: - precomputeWeightsROIGeoGauss(mySurf, myKernel, theRoi); + precomputeWeightsROIGeoGauss(mySurf, myKernel, theRoi, passAreas); break; default: throw CaretException("unknown smoothing method specified"); @@ -662,10 +659,10 @@ void MetricSmoothingObject::precomputeWeights(const SurfaceFile* mySurf, float m precomputeWeightsGeoGaussArea(mySurf, myKernel, passAreas); break; case GEO_GAUSS_EQUAL: - precomputeWeightsGeoGaussEqual(mySurf, myKernel); + precomputeWeightsGeoGaussEqual(mySurf, myKernel, passAreas); break; case GEO_GAUSS: - precomputeWeightsGeoGauss(mySurf, myKernel); + precomputeWeightsGeoGauss(mySurf, myKernel, passAreas); break; default: throw CaretException("unknown smoothing method specified"); diff --git a/src/Files/MetricSmoothingObject.h b/src/Files/MetricSmoothingObject.h index 222c86021650e5ee90eade736282723a5b38d267..fb27eeb755668711c09522840b8d26346541b51c 100644 --- a/src/Files/MetricSmoothingObject.h +++ b/src/Files/MetricSmoothingObject.h @@ -63,12 +63,12 @@ namespace caret { void smoothColumnInternal(float* scratch, const MetricFile* metricIn, const int& whichColumn, MetricFile* metricOut, const int& whichOutColumn, const bool& fixZeros) const; void smoothColumnInternal(float* scratch, const MetricFile* metricIn, const int& whichColumn, MetricFile* metricOut, const int& whichOutColumn, const MetricFile* roi, const int& whichRoiColumn, const bool& fixZeros) const; void precomputeWeights(const SurfaceFile* mySurf, float myKernel, const MetricFile* theRoi, Method myMethod, const float* nodeAreas); - void precomputeWeightsGeoGauss(const SurfaceFile* mySurf, float myKernel); - void precomputeWeightsROIGeoGauss(const SurfaceFile* mySurf, float myKernel, const MetricFile* theRoi); + void precomputeWeightsGeoGauss(const SurfaceFile* mySurf, float myKernel, const float* nodeAreas); + void precomputeWeightsROIGeoGauss(const SurfaceFile* mySurf, float myKernel, const MetricFile* theRoi, const float* nodeAreas); void precomputeWeightsGeoGaussArea(const SurfaceFile* mySurf, float myKernel, const float* nodeAreas); void precomputeWeightsROIGeoGaussArea(const SurfaceFile* mySurf, float myKernel, const MetricFile* theRoi, const float* nodeAreas); - void precomputeWeightsGeoGaussEqual(const SurfaceFile* mySurf, float myKernel); - void precomputeWeightsROIGeoGaussEqual(const SurfaceFile* mySurf, float myKernel, const MetricFile* theRoi); + void precomputeWeightsGeoGaussEqual(const SurfaceFile* mySurf, float myKernel, const float* nodeAreas); + void precomputeWeightsROIGeoGaussEqual(const SurfaceFile* mySurf, float myKernel, const MetricFile* theRoi, const float* nodeAreas); MetricSmoothingObject(); }; diff --git a/src/Files/NodeAndVoxelColoring.cxx b/src/Files/NodeAndVoxelColoring.cxx index 510f17ff9d39bb71fa240402e3a90413d22ff463..12379563e5ef31992b364e30a2cc75b9596142f0 100644 --- a/src/Files/NodeAndVoxelColoring.cxx +++ b/src/Files/NodeAndVoxelColoring.cxx @@ -38,7 +38,7 @@ #include "GroupAndNameHierarchyItem.h" #include "Palette.h" #include "PaletteColorMapping.h" -#include "CaretOMP.h" +#include "MathFunctions.h" using namespace caret; @@ -75,11 +75,11 @@ static const float negativeThresholdGreenColor[] = { * Descriptive statistics for min/max values. * @param paletteColorMapping * Specifies mapping of scalars to palette colors. - * @param palette - * Color palette used to map scalars to colors. * @param scalarValues * Scalars that are used to color the values. * Number of elements is 'numberOfScalars'. + * @param thresholdPaletteColorMapping + * Specifies thresholding for thresholding scalars. * @param thresholdValues * Thresholds for inhibiting coloring. * Number of elements is 'numberOfScalars'. @@ -95,23 +95,26 @@ static const float negativeThresholdGreenColor[] = { */ void NodeAndVoxelColoring::colorScalarsWithPalettePrivate(const FastStatistics* statistics, - const PaletteColorMapping* paletteColorMapping, - const Palette* palette, - const float* scalarValues, - const float* thresholdValues, - const int64_t numberOfScalars, - const ColorDataType colorDataType, - void* rgbaOutPointer, - const bool ignoreThresholding) + const PaletteColorMapping* paletteColorMapping, + const float* scalarValues, + const PaletteColorMapping* thresholdPaletteColorMapping, + const float* thresholdValues, + const int64_t numberOfScalars, + const ColorDataType colorDataType, + void* rgbaOutPointer, + const bool ignoreThresholding) { if (numberOfScalars <= 0) { return; } + const Palette* palette = paletteColorMapping->getPalette(); + CaretAssert(palette); + CaretAssert(statistics); CaretAssert(paletteColorMapping); - CaretAssert(palette); CaretAssert(scalarValues); + CaretAssert(thresholdPaletteColorMapping); CaretAssert(thresholdValues); CaretAssert(rgbaOutPointer); @@ -133,7 +136,7 @@ NodeAndVoxelColoring::colorScalarsWithPalettePrivate(const FastStatistics* stati * Type of threshold testing */ bool showOutsideFlag = false; - const PaletteThresholdTestEnum::Enum thresholdTest = paletteColorMapping->getThresholdTest(); + const PaletteThresholdTestEnum::Enum thresholdTest = thresholdPaletteColorMapping->getThresholdTest(); switch (thresholdTest) { case PaletteThresholdTestEnum::THRESHOLD_TEST_SHOW_OUTSIDE: showOutsideFlag = true; @@ -147,13 +150,13 @@ NodeAndVoxelColoring::colorScalarsWithPalettePrivate(const FastStatistics* stati * Range of values allowed by thresholding */ const PaletteThresholdTypeEnum::Enum thresholdType = paletteColorMapping->getThresholdType(); - const float thresholdMinimum = paletteColorMapping->getThresholdMinimum(thresholdType); - const float thresholdMaximum = paletteColorMapping->getThresholdMaximum(thresholdType); - const float thresholdMappedPositive = paletteColorMapping->getThresholdMappedMaximum(); - const float thresholdMappedPositiveAverageArea = paletteColorMapping->getThresholdMappedAverageAreaMaximum(); - const float thresholdMappedNegative = paletteColorMapping->getThresholdMappedMinimum(); - const float thresholdMappedNegativeAverageArea = paletteColorMapping->getThresholdMappedAverageAreaMinimum(); - const bool showMappedThresholdFailuresInGreen = paletteColorMapping->isShowThresholdFailureInGreen(); + const float thresholdMinimum = thresholdPaletteColorMapping->getThresholdMinimum(thresholdType); + const float thresholdMaximum = thresholdPaletteColorMapping->getThresholdMaximum(thresholdType); + const float thresholdMappedPositive = thresholdPaletteColorMapping->getThresholdMappedMaximum(); + const float thresholdMappedPositiveAverageArea = thresholdPaletteColorMapping->getThresholdMappedAverageAreaMaximum(); + const float thresholdMappedNegative = thresholdPaletteColorMapping->getThresholdMappedMinimum(); + const float thresholdMappedNegativeAverageArea = thresholdPaletteColorMapping->getThresholdMappedAverageAreaMinimum(); + const bool showMappedThresholdFailuresInGreen = thresholdPaletteColorMapping->isShowThresholdFailureInGreen(); /* * Skip threshold testing? @@ -237,11 +240,18 @@ NodeAndVoxelColoring::colorScalarsWithPalettePrivate(const FastStatistics* stati continue; } } - else { + else if (MathFunctions::isNaN(scalar)) { + continue;//TSC: never color NaN + } else { /* * May be very near zero so force to zero. + * + * TSC: that seems wrong, leave the normalized value alone + * if the data value is near zero, that doesn't mean the palette settings aren't also near zero + * therefore, normalized value may not be near zero, which is important + * */ - normalizedValues[i] = 0.0; + //normalizedValues[i] = 0.0; if (hideZeroValues) { continue; } @@ -377,11 +387,11 @@ NodeAndVoxelColoring::colorScalarsWithPalettePrivate(const FastStatistics* stati * Descriptive statistics for min/max values. * @param paletteColorMapping * Specifies mapping of scalars to palette colors. - * @param palette - * Color palette used to map scalars to colors. * @param scalarValues * Scalars that are used to color the values. * Number of elements is 'numberOfScalars'. + * @param thresholdPaletteColorMapping + * Specifies thresholding for thresholding scalars. * @param thresholdValues * Thresholds for inhibiting coloring. * Number of elements is 'numberOfScalars'. @@ -398,8 +408,8 @@ NodeAndVoxelColoring::colorScalarsWithPalettePrivate(const FastStatistics* stati void NodeAndVoxelColoring::colorScalarsWithPalette(const FastStatistics* statistics, const PaletteColorMapping* paletteColorMapping, - const Palette* palette, const float* scalarValues, + const PaletteColorMapping* thresholdPaletteColorMapping, const float* thresholdValues, const int64_t numberOfScalars, float* rgbaOut, @@ -407,8 +417,8 @@ NodeAndVoxelColoring::colorScalarsWithPalette(const FastStatistics* statistics, { colorScalarsWithPalettePrivate(statistics, paletteColorMapping, - palette, scalarValues, + thresholdPaletteColorMapping, thresholdValues, numberOfScalars, COLOR_TYPE_FLOAT, @@ -423,11 +433,11 @@ NodeAndVoxelColoring::colorScalarsWithPalette(const FastStatistics* statistics, * Descriptive statistics for min/max values. * @param paletteColorMapping * Specifies mapping of scalars to palette colors. - * @param palette - * Color palette used to map scalars to colors. * @param scalarValues * Scalars that are used to color the values. * Number of elements is 'numberOfScalars'. + * @param thresholdPaletteColorMapping + * Specifies thresholding for thresholding scalars. * @param thresholdValues * Thresholds for inhibiting coloring. * Number of elements is 'numberOfScalars'. @@ -444,8 +454,8 @@ NodeAndVoxelColoring::colorScalarsWithPalette(const FastStatistics* statistics, void NodeAndVoxelColoring::colorScalarsWithPalette(const FastStatistics* statistics, const PaletteColorMapping* paletteColorMapping, - const Palette* palette, const float* scalarValues, + const PaletteColorMapping* thresholdPaletteColorMapping, const float* thresholdValues, const int64_t numberOfScalars, uint8_t* rgbaOut, @@ -453,8 +463,8 @@ NodeAndVoxelColoring::colorScalarsWithPalette(const FastStatistics* statistics, { colorScalarsWithPalettePrivate(statistics, paletteColorMapping, - palette, scalarValues, + thresholdPaletteColorMapping, thresholdValues, numberOfScalars, COLOR_TYPE_UNSIGNED_BTYE, @@ -874,8 +884,8 @@ NodeAndVoxelColoring::convertSliceColoringToOutlineMode(uint8_t* rgbaInOut, } uint8_t outlineRGBA[4]; - CaretColorEnum::toRGBByte(labelOutlineColor, - outlineRGBA); + CaretColorEnum::toRGBAByte(labelOutlineColor, + outlineRGBA); outlineRGBA[3] = 255; /* diff --git a/src/Files/NodeAndVoxelColoring.h b/src/Files/NodeAndVoxelColoring.h index ea184a60b31c7a1c30729ba10c123173d9cec8a5..43a4ceaa3a13de49503053fb6cb8b6977a38a0a7 100644 --- a/src/Files/NodeAndVoxelColoring.h +++ b/src/Files/NodeAndVoxelColoring.h @@ -30,7 +30,6 @@ namespace caret { class FastStatistics; class GiftiLabelTable; - class Palette; class PaletteColorMapping; class NodeAndVoxelColoring { @@ -38,8 +37,8 @@ namespace caret { public: static void colorScalarsWithPalette(const FastStatistics* statistics, const PaletteColorMapping* paletteColorMapping, - const Palette* palette, const float* scalars, + const PaletteColorMapping* thresholdPaletteColorMapping, const float* scalarThresholds, const int64_t numberOfScalars, float* rgbaOut, @@ -47,8 +46,8 @@ namespace caret { static void colorScalarsWithPalette(const FastStatistics* statistics, const PaletteColorMapping* paletteColorMapping, - const Palette* palette, const float* scalars, + const PaletteColorMapping* thresholdPaletteColorMapping, const float* scalarThresholds, const int64_t numberOfScalars, uint8_t* rgbaOut, @@ -103,8 +102,8 @@ namespace caret { static void colorScalarsWithPalettePrivate(const FastStatistics* statistics, const PaletteColorMapping* paletteColorMapping, - const Palette* palette, const float* scalars, + const PaletteColorMapping* thresholdPaletteColorMapping, const float* scalarThresholds, const int64_t numberOfScalars, const ColorDataType colorDataType, diff --git a/src/Files/PaletteFile.cxx b/src/Files/PaletteFile.cxx index e6c8622cb83a37075ea98acbdd98b0cb7665722c..ae85e202570a64d0081029fe288b6f4547caf70e 100644 --- a/src/Files/PaletteFile.cxx +++ b/src/Files/PaletteFile.cxx @@ -400,64 +400,7 @@ PaletteFile::addDefaultPalettes() this->addColor("_black", 0x00, 0x00, 0x00 ); this->addColor("_orange", 0xff, 0x69, 0x00 ); - //---------------------------------------------------------------------- - // Psych palette - // - if (this->getPaletteByName("PSYCH") == NULL) { - this->addColor("_pyell-oran", 0xff, 0xcc, 0x00 ); - this->addColor("_poran-red", 0xff, 0x44, 0x00 ); - this->addColor("_pblue", 0x00, 0x44, 0xff ); - this->addColor("_pltblue1", 0x00, 0x69, 0xff ); - this->addColor("_pltblue2", 0x00, 0x99, 0xff ); - this->addColor("_pbluecyan", 0x00, 0xcc, 0xff ); - - Palette psych; - psych.setName("PSYCH"); - //psych.setPositiveOnly(false); - - psych.addScalarAndColor(1.00f, "_yellow"); - psych.addScalarAndColor(0.75f, "_pyell-oran"); - psych.addScalarAndColor(0.50f, "_orange"); - psych.addScalarAndColor(0.25f, "_poran-red"); - psych.addScalarAndColor(0.05f, "none"); - psych.addScalarAndColor(-0.05f, "_pblue"); - psych.addScalarAndColor(-0.25f, "_pltblue1"); - psych.addScalarAndColor(-0.50f, "_pltblue2"); - psych.addScalarAndColor(-0.75f, "_pbluecyan"); - - addPalette(psych); - } - //---------------------------------------------------------------------- - // Psych no-none palette - // - if (this->getPaletteByName("PSYCH-NO-NONE") == NULL) { - this->addColor("_pyell-oran", 0xff, 0xcc, 0x00 ); - this->addColor("_poran-red", 0xff, 0x44, 0x00 ); - this->addColor("_pblue", 0x00, 0x44, 0xff ); - this->addColor("_pltblue1", 0x00, 0x69, 0xff ); - this->addColor("_pltblue2", 0x00, 0x99, 0xff ); - this->addColor("_pbluecyan", 0x00, 0xcc, 0xff ); - - Palette psychNoNone; - psychNoNone.setName("PSYCH-NO-NONE"); - //psychNoNone.setPositiveOnly(false); - - psychNoNone.addScalarAndColor(1.00f, "_yellow"); - psychNoNone.addScalarAndColor(0.75f, "_pyell-oran"); - psychNoNone.addScalarAndColor(0.50f, "_orange"); - psychNoNone.addScalarAndColor(0.25f, "_poran-red"); - psychNoNone.addScalarAndColor(0.0f, "_pblue"); - psychNoNone.addScalarAndColor(-0.25f, "_pltblue1"); - psychNoNone.addScalarAndColor(-0.50f, "_pltblue2"); - psychNoNone.addScalarAndColor(-0.75f, "_pbluecyan"); - - addPalette(psychNoNone); - } - - //---------------------------------------------------------------------- - // ROY-BIG palette - // - if (this->getPaletteByName("ROY-BIG") == NULL) { + if (this->getPaletteByName(Palette::ROY_BIG_BL_PALETTE_NAME) == NULL) { this->addColor("_RGB_255_255_0", 255, 255, 0 ); //#ffff00 this->addColor("_RGB_255_200_0", 255, 200, 0 ); //#ffc800 this->addColor("_RGB_255_120_0", 255, 120, 0 ); //#ff7800 @@ -475,31 +418,6 @@ PaletteFile::addDefaultPalettes() this->addColor("_RGB_0_255_0", 0, 255, 0 ); //#00ff00 this->addColor("_RGB_0_255_255", 0, 255, 255 ); //#00ffff - Palette royBig; - royBig.setName("ROY-BIG"); - - royBig.addScalarAndColor(1.00f, "_RGB_255_255_0"); - royBig.addScalarAndColor(0.875f, "_RGB_255_200_0"); - royBig.addScalarAndColor(0.750f, "_RGB_255_120_0"); - royBig.addScalarAndColor(0.625f, "_RGB_255_0_0"); - royBig.addScalarAndColor(0.500f, "_RGB_200_0_0"); - royBig.addScalarAndColor(0.375f, "_RGB_150_0_0"); - royBig.addScalarAndColor(0.250f, "_RGB_100_0_0"); - royBig.addScalarAndColor(0.125f, "_RGB_60_0_0"); - royBig.addScalarAndColor(0.000f, "none"); - royBig.addScalarAndColor(-0.125f, "_RGB_0_0_80"); - royBig.addScalarAndColor(-0.250f, "_RGB_0_0_170"); - royBig.addScalarAndColor(-0.375f, "_RGB_75_0_125"); - royBig.addScalarAndColor(-0.500f, "_RGB_125_0_160"); - royBig.addScalarAndColor(-0.625f, "_RGB_75_125_0"); - royBig.addScalarAndColor(-0.750f, "_RGB_0_200_0"); - royBig.addScalarAndColor(-0.875f, "_RGB_0_255_0"); - royBig.addScalarAndColor(-0.990f, "_RGB_0_255_255"); - royBig.addScalarAndColor(-1.00f, "_RGB_0_255_255"); - - addPalette(royBig); - - Palette royBigBL; royBigBL.setName(Palette::ROY_BIG_BL_PALETTE_NAME); @@ -525,48 +443,6 @@ PaletteFile::addDefaultPalettes() addPalette(royBigBL); } - //---------------------------------------------------------------------- - // Orange-Yellow palette - // - if (this->getPaletteByName("Orange-Yellow") == NULL) { - this->addColor("_oy1", 0, 0, 0 ); - this->addColor("_oy2", 130, 2, 0 ); - this->addColor("_oy3", 254, 130, 2 ); - this->addColor("_oy4", 254, 254, 126 ); - this->addColor("_oy5", 254, 254, 254 ); - - Palette orangeYellow; - orangeYellow.setName("Orange-Yellow"); - orangeYellow.addScalarAndColor( 1.0f, "_oy5"); - orangeYellow.addScalarAndColor( 0.5f, "_oy4"); - orangeYellow.addScalarAndColor( 0.0f, "_oy3"); - orangeYellow.addScalarAndColor(-0.5f, "_oy2"); - orangeYellow.addScalarAndColor(-1.0f, "_oy1"); - addPalette(orangeYellow); - } - - // - // Create a palette with just white and black designed to be used - // with the interpolate option - // - if (this->getPaletteByName(Palette::GRAY_INTERP_PALETTE_NAME) == NULL) { - this->addColor("_white_gray_interp", 255, 255, 255 ); - this->addColor("_black_gray_interp", 0, 0, 0 ); - - - Palette palGrayPositiveInterp; - palGrayPositiveInterp.setName(Palette::GRAY_INTERP_POSITIVE_PALETTE_NAME); - palGrayPositiveInterp.addScalarAndColor( 1.0f, "_white_gray_interp"); - palGrayPositiveInterp.addScalarAndColor(0.0f, "_black_gray_interp"); - addPalette(palGrayPositiveInterp); - - Palette palGrayInterp; - palGrayInterp.setName(Palette::GRAY_INTERP_PALETTE_NAME); - palGrayInterp.addScalarAndColor( 1.0f, "_white_gray_interp"); - palGrayInterp.addScalarAndColor(-1.0f, "_black_gray_interp"); - addPalette(palGrayInterp); - } - //------------------------------------------------------------------------ // // Palette by David Van Essen @@ -600,31 +476,6 @@ PaletteFile::addDefaultPalettes() int blue_videen7[3] = { 0x7f, 0x7f, 0xcc }; this->addColor("_blue_videen7", blue_videen7); - if (this->getPaletteByName("clear_brain") == NULL) { - Palette clearBrain; - clearBrain.setName("clear_brain"); - clearBrain.addScalarAndColor(1.0f , "_red"); - clearBrain.addScalarAndColor(0.9f , "_orange"); - clearBrain.addScalarAndColor(0.8f , "_oran-yell"); - clearBrain.addScalarAndColor(0.7f , "_yellow"); - clearBrain.addScalarAndColor(0.6f , "_limegreen"); - clearBrain.addScalarAndColor(0.5f , "_green"); - clearBrain.addScalarAndColor(0.4f , "_blue_videen7"); - clearBrain.addScalarAndColor(0.3f , "_blue_videen9"); - clearBrain.addScalarAndColor(0.2f , "_blue_videen11"); - clearBrain.addScalarAndColor(0.1f , "_purple2"); - clearBrain.addScalarAndColor(0.0f , "none"); - clearBrain.addScalarAndColor(-0.1f , "_cyan"); - clearBrain.addScalarAndColor(-0.2f , "_green"); - clearBrain.addScalarAndColor(-0.3f , "_limegreen"); - clearBrain.addScalarAndColor(-0.4f , "_violet"); - clearBrain.addScalarAndColor(-0.5f , "_hotpink"); - clearBrain.addScalarAndColor(-0.6f , "_white"); - clearBrain.addScalarAndColor(-0.7f , "_gry-dd"); - clearBrain.addScalarAndColor(-0.8f , "_gry-bb"); - clearBrain.addScalarAndColor(-0.9f , "_black"); - addPalette(clearBrain); - } if (this->getPaletteByName("videen_style") == NULL) { Palette videenStyle; videenStyle.setName("videen_style"); @@ -651,289 +502,64 @@ PaletteFile::addDefaultPalettes() addPalette(videenStyle); } - if (this->getPaletteByName("fidl") == NULL) { - int Bright_Yellow[3] = { 0xee, 0xee, 0x55 }; - this->addColor("_Bright_Yellow", Bright_Yellow); - int Mustard[3] = { 0xdd, 0xdd, 0x66 }; - this->addColor("_Mustard", Mustard); - int Brown_Mustard[3] = { 0xdd, 0x99, 0x00 }; - this->addColor("_Brown_Mustard", Brown_Mustard); - int Bright_Red[3] = { 0xff, 0x00, 0x00 }; - this->addColor("_Bright_Red", Bright_Red); - int Fire_Engine_Red[3] = { 0xdd, 0x00, 0x00 }; - this->addColor("_Fire_Engine_Red", Fire_Engine_Red); - int Brick[3] = { 0xbb, 0x00, 0x00 }; - this->addColor("_Brick", Brick); - int Beet[3] = { 0x99, 0x00, 0x00 }; - this->addColor("_Beet", Beet); - int Beaujolais[3] = { 0x77, 0x00, 0x00 }; - this->addColor("_Beaujolais", Beaujolais); - int Burgundy[3] = { 0x55, 0x00, 0x00 }; - this->addColor("_Burgundy", Burgundy); - int Thrombin[3] = { 0x11, 0x00, 0x00 }; - this->addColor("_Thrombin", Thrombin); - int Deep_Green[3] = { 0x00, 0x11, 0x00 }; - this->addColor("_Deep_Green", Deep_Green); - int British_Racing_Green[3] = { 0x00, 0x55, 0x00 }; - this->addColor("_British_Racing_Green", British_Racing_Green); - int Kelp[3] = { 0x00, 0x77, 0x00 }; - this->addColor("_Kelp", Kelp); - int Lime[3] = { 0x00, 0x99, 0x00 }; - this->addColor("_Lime", Lime); - int Mint[3] = { 0x00, 0xbb, 0x00 }; - this->addColor("_Mint", Mint); - int Brussell_Sprout[3] = { 0x00, 0xdd, 0x00 }; - this->addColor("_Brussell_Sprout", Brussell_Sprout); - int Bright_Green[3] = { 0x00, 0xff, 0x00 }; - this->addColor("_Bright_Green", Bright_Green); - int Periwinkle[3] = { 0x66, 0x66, 0xbb }; - this->addColor("_Periwinkle", Periwinkle); - int Azure[3] = { 0x88, 0x88, 0xee }; - this->addColor("_Azure", Azure); - int Turquoise[3] = { 0x00, 0xcc, 0xcc }; - this->addColor("_Turquoise", Turquoise); + // + // Create a palette with just white and black designed to be used + // with the interpolate option + // + if (this->getPaletteByName(Palette::GRAY_INTERP_PALETTE_NAME) == NULL) { + this->addColor("_white_gray_interp", 255, 255, 255 ); + this->addColor("_black_gray_interp", 0, 0, 0 ); + - Palette fidl; - fidl.setName("fidl"); - fidl.addScalarAndColor(1.0f, "_Bright_Yellow"); - fidl.addScalarAndColor(0.9f, "_Mustard"); - fidl.addScalarAndColor(0.8f, "_Brown_Mustard"); - fidl.addScalarAndColor(0.7f, "_Bright_Red"); - fidl.addScalarAndColor(0.6f, "_Fire_Engine_Red"); - fidl.addScalarAndColor(0.5f, "_Brick"); - fidl.addScalarAndColor(0.4f, "_Beet"); - fidl.addScalarAndColor(0.3f, "_Beaujolais"); - fidl.addScalarAndColor(0.2f, "_Burgundy"); - fidl.addScalarAndColor(0.1f, "_Thrombin"); - fidl.addScalarAndColor(0.0f, "none"); - fidl.addScalarAndColor(-0.1f, "_Deep_Green"); - fidl.addScalarAndColor(-0.2f, "_British_Racing_Green"); - fidl.addScalarAndColor(-0.3f, "_Kelp"); - fidl.addScalarAndColor(-0.4f, "_Lime"); - fidl.addScalarAndColor(-0.5f, "_Mint"); - fidl.addScalarAndColor(-0.6f, "_Brussell_Sprout"); - fidl.addScalarAndColor(-0.7f, "_Bright_Green"); - fidl.addScalarAndColor(-0.8f, "_Periwinkle"); - fidl.addScalarAndColor(-0.9f, "_Azure"); - fidl.addScalarAndColor(-1.0f, "_Turquoise"); - addPalette(fidl); + Palette palGrayPositiveInterp; + palGrayPositiveInterp.setName(Palette::GRAY_INTERP_POSITIVE_PALETTE_NAME); + palGrayPositiveInterp.addScalarAndColor( 1.0f, "_white_gray_interp"); + palGrayPositiveInterp.addScalarAndColor(0.0f, "_black_gray_interp"); + addPalette(palGrayPositiveInterp); + + Palette palGrayInterp; + palGrayInterp.setName(Palette::GRAY_INTERP_PALETTE_NAME); + palGrayInterp.addScalarAndColor( 1.0f, "_white_gray_interp"); + palGrayInterp.addScalarAndColor(-1.0f, "_black_gray_interp"); + addPalette(palGrayInterp); + } + + //---------------------------------------------------------------------- + // fixed Psych palette + // + if (this->getPaletteByName("PSYCH-FIXED") == NULL) { + this->addColor("_pyell-oran", 0xff, 0xcc, 0x00 ); + this->addColor("_poran-red", 0xff, 0x44, 0x00 ); + this->addColor("_pred", 0xff, 0x00, 0x00 ); + this->addColor("_pblue", 0x00, 0x44, 0xff ); + this->addColor("_pltblue1", 0x00, 0x69, 0xff ); + this->addColor("_pltblue2", 0x00, 0x99, 0xff ); + this->addColor("_pbluecyan", 0x00, 0xcc, 0xff ); + this->addColor("_pcyan", 0x00, 0xff, 0xff ); + + Palette psychFixed; + psychFixed.setName("PSYCH-FIXED"); + //psych.setPositiveOnly(false); + + psychFixed.addScalarAndColor(1.00f, "_yellow"); + psychFixed.addScalarAndColor(0.75f, "_pyell-oran"); + psychFixed.addScalarAndColor(0.50f, "_orange"); + psychFixed.addScalarAndColor(0.25f, "_poran-red"); + psychFixed.addScalarAndColor(0.00001f, "_pred");//0.00001f is a special range reserved by data normalization for zero values, see PaletteColorMapping.cxx:1590 + psychFixed.addScalarAndColor(0.0000099f, "_black"); + psychFixed.addScalarAndColor(-0.0000099f, "_black"); + psychFixed.addScalarAndColor(-0.00001f, "_pblue"); + psychFixed.addScalarAndColor(-0.25f, "_pltblue1"); + psychFixed.addScalarAndColor(-0.50f, "_pltblue2"); + psychFixed.addScalarAndColor(-0.75f, "_pbluecyan"); + psychFixed.addScalarAndColor(-1.0f, "_pcyan"); + + addPalette(psychFixed); } //------------------------------------------------------------------------ // - // Colors by Russ H. - // - int _rbgyr20_10[3] = { 0x00, 0xff, 0x00 }; - this->addColor("_rbgyr20_10", _rbgyr20_10); - int _rbgyr20_15[3] = { 0xff, 0xff, 0x00 }; - this->addColor("_rbgyr20_15", _rbgyr20_15); - int _rbgyr20_20[3] = { 0xff, 0x00, 0x00 }; - this->addColor("_rbgyr20_20", _rbgyr20_20); - - int _rbgyr20_21[3] = { 0x9d, 0x22, 0xc1 }; - this->addColor("_rbgyr20_21", _rbgyr20_21); - int _rbgyr20_22[3] = { 0x81, 0x06, 0xa5 }; - this->addColor("_rbgyr20_22", _rbgyr20_22); - int _rbgyr20_23[3] = { 0xff, 0xec, 0x00 }; - this->addColor("_rbgyr20_23", _rbgyr20_23); - int _rbgyr20_24[3] = { 0xff, 0xd6, 0x00 }; - this->addColor("_rbgyr20_24", _rbgyr20_24); - int _rbgyr20_25[3] = { 0xff, 0xbc, 0x00 }; - this->addColor("_rbgyr20_25", _rbgyr20_25); - int _rbgyr20_26[3] = { 0xff, 0x9c, 0x00 }; - this->addColor("_rbgyr20_26", _rbgyr20_26); - int _rbgyr20_27[3] = { 0xff, 0x7c, 0x00 }; - this->addColor("_rbgyr20_27", _rbgyr20_27); - int _rbgyr20_28[3] = { 0xff, 0x5c, 0x00 }; - this->addColor("_rbgyr20_28", _rbgyr20_28); - int _rbgyr20_29[3] = { 0xff, 0x3d, 0x00 }; - this->addColor("_rbgyr20_29", _rbgyr20_29); - int _rbgyr20_30[3] = { 0xff, 0x23, 0x00 }; - this->addColor("_rbgyr20_30", _rbgyr20_30); - int _rbgyr20_31[3] = { 0x00, 0xed, 0x12 }; - this->addColor("_rbgyr20_31", _rbgyr20_31); - int _rbgyr20_32[3] = { 0x00, 0xd5, 0x2a }; - this->addColor("_rbgyr20_32", _rbgyr20_32); - int _rbgyr20_33[3] = { 0x00, 0xb9, 0x46 }; - this->addColor("_rbgyr20_33", _rbgyr20_33); - int _rbgyr20_34[3] = { 0x00, 0x9b, 0x64 }; - this->addColor("_rbgyr20_34", _rbgyr20_34); - int _rbgyr20_35[3] = { 0x00, 0x7b, 0x84 }; - this->addColor("_rbgyr20_35", _rbgyr20_35); - int _rbgyr20_36[3] = { 0x00, 0x5b, 0xa4 }; - this->addColor("_rbgyr20_36", _rbgyr20_36); - int _rbgyr20_37[3] = { 0x00, 0x44, 0xbb }; - this->addColor("_rbgyr20_37", _rbgyr20_37); - int _rbgyr20_38[3] = { 0x00, 0x24, 0xdb }; - this->addColor("_rbgyr20_38", _rbgyr20_38); - int _rbgyr20_39[3] = { 0x00, 0x00, 0xff }; - this->addColor("_rbgyr20_39", _rbgyr20_39); - - int _rbgyr20_40[3] = { 0xff, 0xf1, 0x00 }; - this->addColor("_rbgyr20_40", _rbgyr20_40); - int _rbgyr20_41[3] = { 0xff, 0xdc, 0x00 }; - this->addColor("_rbgyr20_41", _rbgyr20_41); - int _rbgyr20_42[3] = { 0xff, 0xcb, 0x00 }; - this->addColor("_rbgyr20_42", _rbgyr20_42); - int _rbgyr20_43[3] = { 0xff, 0xc2, 0x00 }; - this->addColor("_rbgyr20_43", _rbgyr20_43); - int _rbgyr20_44[3] = { 0xff, 0xae, 0x00 }; - this->addColor("_rbgyr20_44", _rbgyr20_44); - int _rbgyr20_45[3] = { 0xff, 0x9f, 0x00 }; - this->addColor("_rbgyr20_45", _rbgyr20_45); - int _rbgyr20_46[3] = { 0xff, 0x86, 0x00 }; - this->addColor("_rbgyr20_46", _rbgyr20_46); - int _rbgyr20_47[3] = { 0xff, 0x59, 0x00 }; - this->addColor("_rbgyr20_47", _rbgyr20_47); - int _rbgyr20_48[3] = { 0x00, 0xff, 0x2d }; - this->addColor("_rbgyr20_48", _rbgyr20_48); - int _rbgyr20_49[3] = { 0x00, 0xff, 0x65 }; - this->addColor("_rbgyr20_49", _rbgyr20_49); - int _rbgyr20_50[3] = { 0x00, 0xff, 0xa5 }; - this->addColor("_rbgyr20_50", _rbgyr20_50); - int _rbgyr20_51[3] = { 0x00, 0xff, 0xdd }; - this->addColor("_rbgyr20_51", _rbgyr20_51); - int _rbgyr20_52[3] = { 0x00, 0xff, 0xff }; - this->addColor("_rbgyr20_52", _rbgyr20_52); - int _rbgyr20_53[3] = { 0x00, 0xe9, 0xff }; - this->addColor("_rbgyr20_53", _rbgyr20_53); - int _rbgyr20_54[3] = { 0x00, 0xad, 0xff }; - this->addColor("_rbgyr20_54", _rbgyr20_54); - int _rbgyr20_55[3] = { 0x00, 0x69, 0xff }; - this->addColor("_rbgyr20_55", _rbgyr20_55); - int _rbgyr20_56[3] = { 0xff, 0x00, 0xb9 }; - this->addColor("_rbgyr20_56", _rbgyr20_56); - int _rbgyr20_57[3] = { 0xff, 0x00, 0x63 }; - this->addColor("_rbgyr20_57", _rbgyr20_57); - int _rbgyr20_58[3] = { 0xff, 0x05, 0x00 }; - this->addColor("_rbgyr20_58", _rbgyr20_58); - int _rbgyr20_59[3] = { 0xff, 0x32, 0x00 }; - this->addColor("_rbgyr20_59", _rbgyr20_59); - int _rbgyr20_60[3] = { 0xff, 0x70, 0x00 }; - this->addColor("_rbgyr20_60", _rbgyr20_60); - int _rbgyr20_61[3] = { 0xff, 0xa4, 0x00 }; - this->addColor("_rbgyr20_61", _rbgyr20_61); - int _rbgyr20_62[3] = { 0xff, 0xba, 0x00 }; - this->addColor("_rbgyr20_62", _rbgyr20_62); - int _rbgyr20_63[3] = { 0xff, 0xd3, 0x00 }; - this->addColor("_rbgyr20_63", _rbgyr20_63); - int _rbgyr20_64[3] = { 0x42, 0x21, 0xdb }; - this->addColor("_rbgyr20_64", _rbgyr20_64); - int _rbgyr20_65[3] = { 0x10, 0x08, 0xf6 }; - this->addColor("_rbgyr20_65", _rbgyr20_65); - int _rbgyr20_66[3] = { 0x00, 0x13, 0xff }; - this->addColor("_rbgyr20_66", _rbgyr20_66); - int _rbgyr20_67[3] = { 0x00, 0x5b, 0xff }; - this->addColor("_rbgyr20_67", _rbgyr20_67); - int _rbgyr20_68[3] = { 0x00, 0xb3, 0xff }; - this->addColor("_rbgyr20_68", _rbgyr20_68); - int _rbgyr20_69[3] = { 0x00, 0xfc, 0xff }; - this->addColor("_rbgyr20_69", _rbgyr20_69); - int _rbgyr20_70[3] = { 0x00, 0xff, 0xcd }; - this->addColor("_rbgyr20_70", _rbgyr20_70); - int _rbgyr20_71[3] = { 0x00, 0xff, 0x74 }; - this->addColor("_rbgyr20_71", _rbgyr20_71); - int _rbgyr20_72[3] = { 0xff, 0x00, 0xf9 }; - this->addColor("_rbgyr20_72", _rbgyr20_72); - int _rbgyr20_73[3] = { 0x62, 0x31, 0xc9 }; - this->addColor("_rbgyr20_73", _rbgyr20_73); - - //------------------------------------------------------------------------ - // - // Palette by Russ H. - // - if (this->getPaletteByName("raich4_clrmid") == NULL) { - Palette r4; - r4.setName("raich4_clrmid"); - r4.addScalarAndColor(1.000000f, "_rbgyr20_20"); - r4.addScalarAndColor(0.900000f, "_rbgyr20_30"); - r4.addScalarAndColor(0.800000f, "_rbgyr20_29"); - r4.addScalarAndColor(0.700000f, "_rbgyr20_28"); - r4.addScalarAndColor(0.600000f, "_rbgyr20_27"); - r4.addScalarAndColor(0.500000f, "_rbgyr20_26"); - r4.addScalarAndColor(0.400000f, "_rbgyr20_25"); - r4.addScalarAndColor(0.300000f, "_rbgyr20_24"); - r4.addScalarAndColor(0.200000f, "_rbgyr20_23"); - r4.addScalarAndColor(0.100000f, "_rbgyr20_15"); - r4.addScalarAndColor(0.000000f, "none"); - r4.addScalarAndColor(-0.100000f, "_rbgyr20_10"); - r4.addScalarAndColor(-0.200000f, "_rbgyr20_31"); - r4.addScalarAndColor(-0.300000f, "_rbgyr20_32"); - r4.addScalarAndColor(-0.400000f, "_rbgyr20_33"); - r4.addScalarAndColor(-0.500000f, "_rbgyr20_34"); - r4.addScalarAndColor(-0.600000f, "_rbgyr20_35"); - r4.addScalarAndColor(-0.700000f, "_rbgyr20_36"); - r4.addScalarAndColor(-0.800000f, "_rbgyr20_37"); - r4.addScalarAndColor(-0.900000f, "_rbgyr20_38"); - r4.addScalarAndColor(-1.000000f, "_rbgyr20_39"); - addPalette(r4); - } - - //------------------------------------------------------------------------ - // - // Palette by Russ H. - // - if (this->getPaletteByName("raich6_clrmid") == NULL) { - Palette r6; - r6.setName("raich6_clrmid"); - r6.addScalarAndColor(1.000000f, "_rbgyr20_20"); - r6.addScalarAndColor(0.900000f, "_rbgyr20_47"); - r6.addScalarAndColor(0.800000f, "_rbgyr20_46"); - r6.addScalarAndColor(0.700000f, "_rbgyr20_45"); - r6.addScalarAndColor(0.600000f, "_rbgyr20_44"); - r6.addScalarAndColor(0.500000f, "_rbgyr20_43"); - r6.addScalarAndColor(0.400000f, "_rbgyr20_42"); - r6.addScalarAndColor(0.300000f, "_rbgyr20_41"); - r6.addScalarAndColor(0.200000f, "_rbgyr20_40"); - r6.addScalarAndColor(0.100000f, "_rbgyr20_15"); - r6.addScalarAndColor(0.000000f, "none"); - r6.addScalarAndColor(-0.100000f, "_rbgyr20_10"); - r6.addScalarAndColor(-0.200000f, "_rbgyr20_48"); - r6.addScalarAndColor(-0.300000f, "_rbgyr20_49"); - r6.addScalarAndColor(-0.400000f, "_rbgyr20_50"); - r6.addScalarAndColor(-0.500000f, "_rbgyr20_51"); - r6.addScalarAndColor(-0.600000f, "_rbgyr20_52"); - r6.addScalarAndColor(-0.700000f, "_rbgyr20_53"); - r6.addScalarAndColor(-0.800000f, "_rbgyr20_54"); - r6.addScalarAndColor(-0.900000f, "_rbgyr20_55"); - r6.addScalarAndColor(-1.000000f, "_rbgyr20_39"); - addPalette(r6); - } - - //------------------------------------------------------------------------ - // - // Palette by Russ H. - // - if (this->getPaletteByName("HSB8_clrmid") == NULL) { - Palette hsb8; - hsb8.setName("HSB8_clrmid"); - hsb8.addScalarAndColor(1.000000f, "_rbgyr20_15"); - hsb8.addScalarAndColor(0.900000f, "_rbgyr20_63"); - hsb8.addScalarAndColor(0.800000f, "_rbgyr20_62"); - hsb8.addScalarAndColor(0.700000f, "_rbgyr20_61"); - hsb8.addScalarAndColor(0.600000f, "_rbgyr20_60"); - hsb8.addScalarAndColor(0.500000f, "_rbgyr20_59"); - hsb8.addScalarAndColor(0.400000f, "_rbgyr20_58"); - hsb8.addScalarAndColor(0.300000f, "_rbgyr20_57"); - hsb8.addScalarAndColor(0.200000f, "_rbgyr20_56"); - hsb8.addScalarAndColor(0.100000f, "_rbgyr20_72"); - hsb8.addScalarAndColor(0.000000f, "none"); - hsb8.addScalarAndColor(-0.100000f, "_rbgyr20_73"); - hsb8.addScalarAndColor(-0.200000f, "_rbgyr20_64"); - hsb8.addScalarAndColor(-0.300000f, "_rbgyr20_65"); - hsb8.addScalarAndColor(-0.400000f, "_rbgyr20_66"); - hsb8.addScalarAndColor(-0.500000f, "_rbgyr20_67"); - hsb8.addScalarAndColor(-0.600000f, "_rbgyr20_68"); - hsb8.addScalarAndColor(-0.700000f, "_rbgyr20_69"); - hsb8.addScalarAndColor(-0.800000f, "_rbgyr20_70"); - hsb8.addScalarAndColor(-0.900000f, "_rbgyr20_71"); - hsb8.addScalarAndColor(-1.000000f, "_rbgyr20_10"); - addPalette(hsb8); - } - - //------------------------------------------------------------------------ - // - // Palette by Jon Wieser @ mcw + // Palette by Jon Wieser @ mcw // int rbgyr20_01[3] = { 0xCC, 0x10, 0x33 }; this->addColor("_rbgyr20_01", rbgyr20_01); @@ -1026,21 +652,85 @@ PaletteFile::addDefaultPalettes() pal3.addScalarAndColor(0.0f, "none"); addPalette(pal3); } + + if (this->getPaletteByName("RYGBR4_positive") == NULL) { + this->addColor("rygbr4_0", 255, 0, 0); + this->addColor("rygbr4_1", 255, 255, 0); + this->addColor("rygbr4_2", 0, 180, 0); + this->addColor("rygbr4_3", 0, 0, 255); + + Palette rygbr4_pos; + rygbr4_pos.setName("RYGBR4_positive"); + rygbr4_pos.addScalarAndColor(1.0f, "rygbr4_0"); + rygbr4_pos.addScalarAndColor(0.75f, "rygbr4_3"); + rygbr4_pos.addScalarAndColor(0.5f, "rygbr4_2"); + rygbr4_pos.addScalarAndColor(0.25f, "rygbr4_1"); + rygbr4_pos.addScalarAndColor(0.0f, "rygbr4_0"); + rygbr4_pos.addScalarAndColor(-1.0f, "rygbr4_0");//negatives red, I guess + addPalette(rygbr4_pos); + } + + if (this->getPaletteByName("RGRBR_mirror90_pos") == NULL) { + this->addColor("rgrbr_m9_p_red", 220, 0, 0); + this->addColor("rgrbr_m9_p_yellow", 255, 255, 0); + this->addColor("rgrbr_m9_p_green", 0, 180, 0); + this->addColor("rgrbr_m9_p_blue", 0, 0, 255); + this->addColor("rgrbr_m9_p_purple", 255, 100, 255); + + Palette rgbgr_m9_p; + rgbgr_m9_p.setName("RGRBR_mirror90_pos"); + rgbgr_m9_p.addScalarAndColor(1.0f, "rgrbr_m9_p_red"); + rgbgr_m9_p.addScalarAndColor(0.875f, "rgrbr_m9_p_purple"); + rgbgr_m9_p.addScalarAndColor(0.75f, "rgrbr_m9_p_blue"); + rgbgr_m9_p.addScalarAndColor(0.625f, "rgrbr_m9_p_purple"); + rgbgr_m9_p.addScalarAndColor(0.5f, "rgrbr_m9_p_red"); + rgbgr_m9_p.addScalarAndColor(0.375f, "rgrbr_m9_p_yellow"); + rgbgr_m9_p.addScalarAndColor(0.25f, "rgrbr_m9_p_green"); + rgbgr_m9_p.addScalarAndColor(0.125f, "rgrbr_m9_p_yellow"); + rgbgr_m9_p.addScalarAndColor(0.0f, "rgrbr_m9_p_red"); + rgbgr_m9_p.addScalarAndColor(-1.0f, "rgrbr_m9_p_red");//negatives red, I guess + addPalette(rgbgr_m9_p); + + } + //---------------------------------------------------------------------- - // Positive/Negative palette + // Orange-Yellow palette // - if (this->getPaletteByName("POS_NEG") == NULL) { + if (this->getPaletteByName("Orange-Yellow") == NULL) { + this->addColor("_oy1", 0, 0, 0 ); + this->addColor("_oy2", 130, 2, 0 ); + this->addColor("_oy3", 254, 130, 2 ); + this->addColor("_oy4", 254, 254, 126 ); + this->addColor("_oy5", 254, 254, 254 ); + + Palette orangeYellow; + orangeYellow.setName("Orange-Yellow"); + orangeYellow.addScalarAndColor( 1.0f, "_oy5"); + orangeYellow.addScalarAndColor( 0.5f, "_oy4"); + orangeYellow.addScalarAndColor( 0.0f, "_oy3"); + orangeYellow.addScalarAndColor(-0.5f, "_oy2"); + orangeYellow.addScalarAndColor(-1.0f, "_oy1"); + addPalette(orangeYellow); + } + + //---------------------------------------------------------------------- + // Positive/Negative/Zero palette + // + if (this->getPaletteByName("POS_NEG_ZERO") == NULL) { this->addColor("pos_neg_blue", 0x00, 0x00, 0xff ); this->addColor("pos_neg_red", 0xff, 0x00, 0x00 ); - Palette posNeg; - posNeg.setName("POS_NEG"); + Palette posNegZero; + posNegZero.setName("POS_NEG_ZERO"); - posNeg.addScalarAndColor(1.00f, "pos_neg_red"); - posNeg.addScalarAndColor(0.0001f, "none"); - posNeg.addScalarAndColor(-0.0001f, "pos_neg_blue"); + posNegZero.addScalarAndColor(1.0f, "pos_neg_red"); + posNegZero.addScalarAndColor(0.00001f, "pos_neg_red"); + posNegZero.addScalarAndColor(0.0000099f, "_black"); + posNegZero.addScalarAndColor(-0.0000099f, "_black"); + posNegZero.addScalarAndColor(-0.00001f, "pos_neg_blue"); + posNegZero.addScalarAndColor(-1.0f, "pos_neg_blue"); - addPalette(posNeg); + addPalette(posNegZero); } if (this->getPaletteByName("red-yellow") == NULL) { @@ -1167,6 +857,226 @@ PaletteFile::addDefaultPalettes() addPalette(fslYellow); } + // + // Create a palette with red (positive), white (zero), + // and blue (negative) for Alan A. + // + if (this->getPaletteByName("RedWhiteBlue") == NULL) { + Palette redWhiteBlue; + redWhiteBlue.setName("RedWhiteBlue"); + this->addColor("rwbBlue", 0, 0, 255); + this->addColor("rwbBlueMiddle", 127, 127, 255); + this->addColor("rwbWhite", 255, 255, 255); + this->addColor("rwbRedMiddle", 255, 127, 127); + this->addColor("rwbRed", 255, 0, 0); + redWhiteBlue.addScalarAndColor( 1.0f, "rwbRed"); + redWhiteBlue.addScalarAndColor( 0.5f, "rwbRedMiddle"); + redWhiteBlue.addScalarAndColor( 0.0f, "rwbWhite"); + redWhiteBlue.addScalarAndColor(-0.5f, "rwbBlueMiddle"); + redWhiteBlue.addScalarAndColor(-1.0f, "rwbBlue"); + addPalette(redWhiteBlue); + } + + //coolwarm, http://www.kennethmoreland.com/color-maps/ + //"Diverging Color Maps for Scientific Visualization." Kenneth Moreland. In Proceedings of the 5th International Symposium on Visual Computing, December 2009. DOI 10.1007/978-3-642-10520-3_9. + //this palette was interpolated in CIELAB space, and then translated to sRGB, so we need to keep a substantial number of points + //use the 33 csv file + if (this->getPaletteByName("cool-warm") == NULL) { + Palette coolwarm; + coolwarm.setName("cool-warm"); + this->addColor("cool-warm-0", 59,76,192); + this->addColor("cool-warm-1", 68,90,204); + this->addColor("cool-warm-2", 77,104,215); + this->addColor("cool-warm-3", 87,117,225); + this->addColor("cool-warm-4", 98,130,234); + this->addColor("cool-warm-5", 108,142,241); + this->addColor("cool-warm-6", 119,154,247); + this->addColor("cool-warm-7", 130,165,251); + this->addColor("cool-warm-8", 141,176,254); + this->addColor("cool-warm-9", 152,185,255); + this->addColor("cool-warm-10", 163,194,255); + this->addColor("cool-warm-11", 174,201,253); + this->addColor("cool-warm-12", 184,208,249); + this->addColor("cool-warm-13", 194,213,244); + this->addColor("cool-warm-14", 204,217,238); + this->addColor("cool-warm-15", 213,219,230); + this->addColor("cool-warm-16", 221,221,221); + this->addColor("cool-warm-17", 229,216,209); + this->addColor("cool-warm-18", 236,211,197); + this->addColor("cool-warm-19", 241,204,185); + this->addColor("cool-warm-20", 245,196,173); + this->addColor("cool-warm-21", 247,187,160); + this->addColor("cool-warm-22", 247,177,148); + this->addColor("cool-warm-23", 247,166,135); + this->addColor("cool-warm-24", 244,154,123); + this->addColor("cool-warm-25", 241,141,111); + this->addColor("cool-warm-26", 236,127,99); + this->addColor("cool-warm-27", 229,112,88); + this->addColor("cool-warm-28", 222,96,77); + this->addColor("cool-warm-29", 213,80,66); + this->addColor("cool-warm-30", 203,62,56); + this->addColor("cool-warm-31", 192,40,47); + this->addColor("cool-warm-32", 180,4,38); + coolwarm.addScalarAndColor(16.0f / 16.0f, "cool-warm-32"); + coolwarm.addScalarAndColor(15.0f / 16.0f, "cool-warm-31"); + coolwarm.addScalarAndColor(14.0f / 16.0f, "cool-warm-30"); + coolwarm.addScalarAndColor(13.0f / 16.0f, "cool-warm-29"); + coolwarm.addScalarAndColor(12.0f / 16.0f, "cool-warm-28"); + coolwarm.addScalarAndColor(11.0f / 16.0f, "cool-warm-27"); + coolwarm.addScalarAndColor(10.0f / 16.0f, "cool-warm-26"); + coolwarm.addScalarAndColor(9.0f / 16.0f, "cool-warm-25"); + coolwarm.addScalarAndColor(8.0f / 16.0f, "cool-warm-24"); + coolwarm.addScalarAndColor(7.0f / 16.0f, "cool-warm-23"); + coolwarm.addScalarAndColor(6.0f / 16.0f, "cool-warm-22"); + coolwarm.addScalarAndColor(5.0f / 16.0f, "cool-warm-21"); + coolwarm.addScalarAndColor(4.0f / 16.0f, "cool-warm-20"); + coolwarm.addScalarAndColor(3.0f / 16.0f, "cool-warm-19"); + coolwarm.addScalarAndColor(2.0f / 16.0f, "cool-warm-18"); + coolwarm.addScalarAndColor(1.0f / 16.0f, "cool-warm-17"); + coolwarm.addScalarAndColor(0.0f / 16.0f, "cool-warm-16"); + coolwarm.addScalarAndColor(-1.0f / 16.0f, "cool-warm-15"); + coolwarm.addScalarAndColor(-2.0f / 16.0f, "cool-warm-14"); + coolwarm.addScalarAndColor(-3.0f / 16.0f, "cool-warm-13"); + coolwarm.addScalarAndColor(-4.0f / 16.0f, "cool-warm-12"); + coolwarm.addScalarAndColor(-5.0f / 16.0f, "cool-warm-11"); + coolwarm.addScalarAndColor(-6.0f / 16.0f, "cool-warm-10"); + coolwarm.addScalarAndColor(-7.0f / 16.0f, "cool-warm-9"); + coolwarm.addScalarAndColor(-8.0f / 16.0f, "cool-warm-8"); + coolwarm.addScalarAndColor(-9.0f / 16.0f, "cool-warm-7"); + coolwarm.addScalarAndColor(-10.0f / 16.0f, "cool-warm-6"); + coolwarm.addScalarAndColor(-11.0f / 16.0f, "cool-warm-5"); + coolwarm.addScalarAndColor(-12.0f / 16.0f, "cool-warm-4"); + coolwarm.addScalarAndColor(-13.0f / 16.0f, "cool-warm-3"); + coolwarm.addScalarAndColor(-14.0f / 16.0f, "cool-warm-2"); + coolwarm.addScalarAndColor(-15.0f / 16.0f, "cool-warm-1"); + coolwarm.addScalarAndColor(-16.0f / 16.0f, "cool-warm-0"); + addPalette(coolwarm); + } + + //matplotlib's spectral is from colorbrewer + //Colors from www.ColorBrewer.org by Cynthia A. Brewer, Geography, Pennsylvania State University. + //Copyright (c) 2002 Cynthia Brewer, Mark Harrower, and The Pennsylvania State University. + if (this->getPaletteByName("spectral") == NULL) { + Palette spectral; + spectral.setName("spectral"); + this->addColor("spectral-0", 158,1,66); + this->addColor("spectral-1", 213,62,79); + this->addColor("spectral-2", 244,109,67); + this->addColor("spectral-3", 253,174,97); + this->addColor("spectral-4", 254,224,139); + this->addColor("spectral-5", 255,255,191); + this->addColor("spectral-6", 230,245,152); + this->addColor("spectral-7", 171,221,164); + this->addColor("spectral-8", 102,194,165); + this->addColor("spectral-9", 50,136,189); + this->addColor("spectral-10", 94,79,162); + spectral.addScalarAndColor(5.0f / 5.0f, "spectral-0"); + spectral.addScalarAndColor(4.0f / 5.0f, "spectral-1"); + spectral.addScalarAndColor(3.0f / 5.0f, "spectral-2"); + spectral.addScalarAndColor(2.0f / 5.0f, "spectral-3"); + spectral.addScalarAndColor(1.0f / 5.0f, "spectral-4"); + spectral.addScalarAndColor(0.0f / 5.0f, "spectral-5"); + spectral.addScalarAndColor(-1.0f / 5.0f, "spectral-6"); + spectral.addScalarAndColor(-2.0f / 5.0f, "spectral-7"); + spectral.addScalarAndColor(-3.0f / 5.0f, "spectral-8"); + spectral.addScalarAndColor(-4.0f / 5.0f, "spectral-9"); + spectral.addScalarAndColor(-5.0f / 5.0f, "spectral-10"); + addPalette(spectral); + } + + //palette from Alan Anticevic + if (this->getPaletteByName("RY-BC-BL") == NULL) { + Palette RY_BC_BL; + RY_BC_BL.setName("RY-BC-BL");//named like "ROY-BIG-BL" + this->addColor("rybcbl-y", 255, 255, 0); + this->addColor("rybcbl-r", 255, 0, 0); + this->addColor("rybcbl-bl", 0, 0, 0); + this->addColor("rybcbl-b", 0, 0, 255); + this->addColor("rybcbl-c", 0, 255, 255); + RY_BC_BL.addScalarAndColor(1.0f, "rybcbl-y"); + RY_BC_BL.addScalarAndColor(0.5f, "rybcbl-r"); + RY_BC_BL.addScalarAndColor(0.0f, "rybcbl-bl"); + RY_BC_BL.addScalarAndColor(-0.5f, "rybcbl-b"); + RY_BC_BL.addScalarAndColor(-1.0f, "rybcbl-c"); + addPalette(RY_BC_BL); + } + + //matplotlib's magma, approximated by reducing their massive 256-entry lookup table to 32 entries + //Copyright (c) 2012-2015 Matplotlib Development Team; All Rights Reserved + //see https://github.com/matplotlib/matplotlib/blob/master/LICENSE/LICENSE or debian/copyright for the matplotlib license, "All Rights Reserved" is required language by the license, but it is actually open source + if (this->getPaletteByName("magma") == NULL) { + Palette magma; + magma.setName("magma"); + this->addColor("magma-black", 0, 0, 0);//negative and zero + this->addColor("magma-1", 3, 3, 15); + this->addColor("magma-2", 9, 7, 32); + this->addColor("magma-3", 18, 13, 49); + this->addColor("magma-4", 28, 16, 68); + this->addColor("magma-5", 39, 18, 88); + this->addColor("magma-6", 52, 16, 105); + this->addColor("magma-7", 66, 15, 117); + this->addColor("magma-8", 79, 18, 123); + this->addColor("magma-9", 92, 22, 127); + this->addColor("magma-10", 104, 28, 129); + this->addColor("magma-11", 117, 33, 129); + this->addColor("magma-12", 129, 37, 129); + this->addColor("magma-13", 142, 42, 129); + this->addColor("magma-14", 155, 46, 127); + this->addColor("magma-15", 168, 50, 125); + this->addColor("magma-16", 181, 54, 122); + this->addColor("magma-17", 194, 59, 117); + this->addColor("magma-18", 207, 64, 112); + this->addColor("magma-19", 219, 71, 106); + this->addColor("magma-20", 229, 80, 100); + this->addColor("magma-21", 238, 91, 94); + this->addColor("magma-22", 244, 105, 92); + this->addColor("magma-23", 249, 120, 93); + this->addColor("magma-24", 251, 135, 97); + this->addColor("magma-25", 253, 150, 104); + this->addColor("magma-26", 254, 165, 113); + this->addColor("magma-27", 254, 180, 123); + this->addColor("magma-28", 254, 194, 135); + this->addColor("magma-29", 254, 209, 148); + this->addColor("magma-30", 253, 224, 161); + this->addColor("magma-31", 252, 238, 176); + this->addColor("magma-32", 252, 253, 191); + magma.addScalarAndColor(32.0f / 32.0f, "magma-32"); + magma.addScalarAndColor(31.0f / 32.0f, "magma-31"); + magma.addScalarAndColor(30.0f / 32.0f, "magma-30"); + magma.addScalarAndColor(29.0f / 32.0f, "magma-29"); + magma.addScalarAndColor(28.0f / 32.0f, "magma-28"); + magma.addScalarAndColor(27.0f / 32.0f, "magma-27"); + magma.addScalarAndColor(26.0f / 32.0f, "magma-26"); + magma.addScalarAndColor(25.0f / 32.0f, "magma-25"); + magma.addScalarAndColor(24.0f / 32.0f, "magma-24"); + magma.addScalarAndColor(23.0f / 32.0f, "magma-23"); + magma.addScalarAndColor(22.0f / 32.0f, "magma-22"); + magma.addScalarAndColor(21.0f / 32.0f, "magma-21"); + magma.addScalarAndColor(20.0f / 32.0f, "magma-20"); + magma.addScalarAndColor(19.0f / 32.0f, "magma-19"); + magma.addScalarAndColor(18.0f / 32.0f, "magma-18"); + magma.addScalarAndColor(17.0f / 32.0f, "magma-17"); + magma.addScalarAndColor(16.0f / 32.0f, "magma-16"); + magma.addScalarAndColor(15.0f / 32.0f, "magma-15"); + magma.addScalarAndColor(14.0f / 32.0f, "magma-14"); + magma.addScalarAndColor(13.0f / 32.0f, "magma-13"); + magma.addScalarAndColor(12.0f / 32.0f, "magma-12"); + magma.addScalarAndColor(11.0f / 32.0f, "magma-11"); + magma.addScalarAndColor(10.0f / 32.0f, "magma-10"); + magma.addScalarAndColor(9.0f / 32.0f, "magma-9"); + magma.addScalarAndColor(8.0f / 32.0f, "magma-8"); + magma.addScalarAndColor(7.0f / 32.0f, "magma-7"); + magma.addScalarAndColor(6.0f / 32.0f, "magma-6"); + magma.addScalarAndColor(5.0f / 32.0f, "magma-5"); + magma.addScalarAndColor(4.0f / 32.0f, "magma-4"); + magma.addScalarAndColor(3.0f / 32.0f, "magma-3"); + magma.addScalarAndColor(2.0f / 32.0f, "magma-2"); + magma.addScalarAndColor(1.0f / 32.0f, "magma-1"); + magma.addScalarAndColor(0.0f, "magma-black");//technically magma doesn't quite reach black at the minimum end (0 0 4), or ~(0 0 2) if extended by one, as it is a 256 element lookup, but it is close enough + magma.addScalarAndColor(-1.0f, "magma-black"); + addPalette(magma); + } + if (this->getPaletteByName("JET256") == NULL) { Palette JET256; JET256.setName("JET256"); @@ -1206,8 +1116,437 @@ PaletteFile::addDefaultPalettes() addPalette(JET256); } - if (modifiedStatus == false) { - this->clearModified(); + + //TSC: palettes that use "none" or are broken (psych no none) start here - exception: rbgyr20p, to keep it close to rbgyr20 + //---------------------------------------------------------------------- + // Psych palette + // + if (this->getPaletteByName("PSYCH") == NULL) { + this->addColor("_pyell-oran", 0xff, 0xcc, 0x00 ); + this->addColor("_poran-red", 0xff, 0x44, 0x00 ); + this->addColor("_pblue", 0x00, 0x44, 0xff ); + this->addColor("_pltblue1", 0x00, 0x69, 0xff ); + this->addColor("_pltblue2", 0x00, 0x99, 0xff ); + this->addColor("_pbluecyan", 0x00, 0xcc, 0xff ); + + Palette psych; + psych.setName("PSYCH"); + //psych.setPositiveOnly(false); + + psych.addScalarAndColor(1.00f, "_yellow"); + psych.addScalarAndColor(0.75f, "_pyell-oran"); + psych.addScalarAndColor(0.50f, "_orange"); + psych.addScalarAndColor(0.25f, "_poran-red"); + psych.addScalarAndColor(0.05f, "none"); + psych.addScalarAndColor(-0.05f, "_pblue"); + psych.addScalarAndColor(-0.25f, "_pltblue1"); + psych.addScalarAndColor(-0.50f, "_pltblue2"); + psych.addScalarAndColor(-0.75f, "_pbluecyan"); + + addPalette(psych); + } + //---------------------------------------------------------------------- + // Psych no-none palette + // + if (this->getPaletteByName("PSYCH-NO-NONE") == NULL) { + this->addColor("_pyell-oran", 0xff, 0xcc, 0x00 ); + this->addColor("_poran-red", 0xff, 0x44, 0x00 ); + this->addColor("_pblue", 0x00, 0x44, 0xff ); + this->addColor("_pltblue1", 0x00, 0x69, 0xff ); + this->addColor("_pltblue2", 0x00, 0x99, 0xff ); + this->addColor("_pbluecyan", 0x00, 0xcc, 0xff ); + + Palette psychNoNone; + psychNoNone.setName("PSYCH-NO-NONE"); + //psychNoNone.setPositiveOnly(false); + + psychNoNone.addScalarAndColor(1.00f, "_yellow"); + psychNoNone.addScalarAndColor(0.75f, "_pyell-oran"); + psychNoNone.addScalarAndColor(0.50f, "_orange"); + psychNoNone.addScalarAndColor(0.25f, "_poran-red"); + psychNoNone.addScalarAndColor(0.0f, "_pblue"); + psychNoNone.addScalarAndColor(-0.25f, "_pltblue1"); + psychNoNone.addScalarAndColor(-0.50f, "_pltblue2"); + psychNoNone.addScalarAndColor(-0.75f, "_pbluecyan"); + + addPalette(psychNoNone); + } + + //---------------------------------------------------------------------- + // ROY-BIG palette + // + if (this->getPaletteByName("ROY-BIG") == NULL) { + Palette royBig; + royBig.setName("ROY-BIG"); + + royBig.addScalarAndColor(1.00f, "_RGB_255_255_0"); + royBig.addScalarAndColor(0.875f, "_RGB_255_200_0"); + royBig.addScalarAndColor(0.750f, "_RGB_255_120_0"); + royBig.addScalarAndColor(0.625f, "_RGB_255_0_0"); + royBig.addScalarAndColor(0.500f, "_RGB_200_0_0"); + royBig.addScalarAndColor(0.375f, "_RGB_150_0_0"); + royBig.addScalarAndColor(0.250f, "_RGB_100_0_0"); + royBig.addScalarAndColor(0.125f, "_RGB_60_0_0"); + royBig.addScalarAndColor(0.000f, "none"); + royBig.addScalarAndColor(-0.125f, "_RGB_0_0_80"); + royBig.addScalarAndColor(-0.250f, "_RGB_0_0_170"); + royBig.addScalarAndColor(-0.375f, "_RGB_75_0_125"); + royBig.addScalarAndColor(-0.500f, "_RGB_125_0_160"); + royBig.addScalarAndColor(-0.625f, "_RGB_75_125_0"); + royBig.addScalarAndColor(-0.750f, "_RGB_0_200_0"); + royBig.addScalarAndColor(-0.875f, "_RGB_0_255_0"); + royBig.addScalarAndColor(-0.990f, "_RGB_0_255_255"); + royBig.addScalarAndColor(-1.00f, "_RGB_0_255_255"); + + addPalette(royBig); + } + + if (this->getPaletteByName("clear_brain") == NULL) { + Palette clearBrain; + clearBrain.setName("clear_brain"); + clearBrain.addScalarAndColor(1.0f , "_red"); + clearBrain.addScalarAndColor(0.9f , "_orange"); + clearBrain.addScalarAndColor(0.8f , "_oran-yell"); + clearBrain.addScalarAndColor(0.7f , "_yellow"); + clearBrain.addScalarAndColor(0.6f , "_limegreen"); + clearBrain.addScalarAndColor(0.5f , "_green"); + clearBrain.addScalarAndColor(0.4f , "_blue_videen7"); + clearBrain.addScalarAndColor(0.3f , "_blue_videen9"); + clearBrain.addScalarAndColor(0.2f , "_blue_videen11"); + clearBrain.addScalarAndColor(0.1f , "_purple2"); + clearBrain.addScalarAndColor(0.0f , "none"); + clearBrain.addScalarAndColor(-0.1f , "_cyan"); + clearBrain.addScalarAndColor(-0.2f , "_green"); + clearBrain.addScalarAndColor(-0.3f , "_limegreen"); + clearBrain.addScalarAndColor(-0.4f , "_violet"); + clearBrain.addScalarAndColor(-0.5f , "_hotpink"); + clearBrain.addScalarAndColor(-0.6f , "_white"); + clearBrain.addScalarAndColor(-0.7f , "_gry-dd"); + clearBrain.addScalarAndColor(-0.8f , "_gry-bb"); + clearBrain.addScalarAndColor(-0.9f , "_black"); + addPalette(clearBrain); + } + + if (this->getPaletteByName("fidl") == NULL) { + int Bright_Yellow[3] = { 0xee, 0xee, 0x55 }; + this->addColor("_Bright_Yellow", Bright_Yellow); + int Mustard[3] = { 0xdd, 0xdd, 0x66 }; + this->addColor("_Mustard", Mustard); + int Brown_Mustard[3] = { 0xdd, 0x99, 0x00 }; + this->addColor("_Brown_Mustard", Brown_Mustard); + int Bright_Red[3] = { 0xff, 0x00, 0x00 }; + this->addColor("_Bright_Red", Bright_Red); + int Fire_Engine_Red[3] = { 0xdd, 0x00, 0x00 }; + this->addColor("_Fire_Engine_Red", Fire_Engine_Red); + int Brick[3] = { 0xbb, 0x00, 0x00 }; + this->addColor("_Brick", Brick); + int Beet[3] = { 0x99, 0x00, 0x00 }; + this->addColor("_Beet", Beet); + int Beaujolais[3] = { 0x77, 0x00, 0x00 }; + this->addColor("_Beaujolais", Beaujolais); + int Burgundy[3] = { 0x55, 0x00, 0x00 }; + this->addColor("_Burgundy", Burgundy); + int Thrombin[3] = { 0x11, 0x00, 0x00 }; + this->addColor("_Thrombin", Thrombin); + int Deep_Green[3] = { 0x00, 0x11, 0x00 }; + this->addColor("_Deep_Green", Deep_Green); + int British_Racing_Green[3] = { 0x00, 0x55, 0x00 }; + this->addColor("_British_Racing_Green", British_Racing_Green); + int Kelp[3] = { 0x00, 0x77, 0x00 }; + this->addColor("_Kelp", Kelp); + int Lime[3] = { 0x00, 0x99, 0x00 }; + this->addColor("_Lime", Lime); + int Mint[3] = { 0x00, 0xbb, 0x00 }; + this->addColor("_Mint", Mint); + int Brussell_Sprout[3] = { 0x00, 0xdd, 0x00 }; + this->addColor("_Brussell_Sprout", Brussell_Sprout); + int Bright_Green[3] = { 0x00, 0xff, 0x00 }; + this->addColor("_Bright_Green", Bright_Green); + int Periwinkle[3] = { 0x66, 0x66, 0xbb }; + this->addColor("_Periwinkle", Periwinkle); + int Azure[3] = { 0x88, 0x88, 0xee }; + this->addColor("_Azure", Azure); + int Turquoise[3] = { 0x00, 0xcc, 0xcc }; + this->addColor("_Turquoise", Turquoise); + + Palette fidl; + fidl.setName("fidl"); + fidl.addScalarAndColor(1.0f, "_Bright_Yellow"); + fidl.addScalarAndColor(0.9f, "_Mustard"); + fidl.addScalarAndColor(0.8f, "_Brown_Mustard"); + fidl.addScalarAndColor(0.7f, "_Bright_Red"); + fidl.addScalarAndColor(0.6f, "_Fire_Engine_Red"); + fidl.addScalarAndColor(0.5f, "_Brick"); + fidl.addScalarAndColor(0.4f, "_Beet"); + fidl.addScalarAndColor(0.3f, "_Beaujolais"); + fidl.addScalarAndColor(0.2f, "_Burgundy"); + fidl.addScalarAndColor(0.1f, "_Thrombin"); + fidl.addScalarAndColor(0.0f, "none"); + fidl.addScalarAndColor(-0.1f, "_Deep_Green"); + fidl.addScalarAndColor(-0.2f, "_British_Racing_Green"); + fidl.addScalarAndColor(-0.3f, "_Kelp"); + fidl.addScalarAndColor(-0.4f, "_Lime"); + fidl.addScalarAndColor(-0.5f, "_Mint"); + fidl.addScalarAndColor(-0.6f, "_Brussell_Sprout"); + fidl.addScalarAndColor(-0.7f, "_Bright_Green"); + fidl.addScalarAndColor(-0.8f, "_Periwinkle"); + fidl.addScalarAndColor(-0.9f, "_Azure"); + fidl.addScalarAndColor(-1.0f, "_Turquoise"); + addPalette(fidl); + } + + //------------------------------------------------------------------------ + // + // Colors by Russ H. + // + int _rbgyr20_10[3] = { 0x00, 0xff, 0x00 }; + this->addColor("_rbgyr20_10", _rbgyr20_10); + int _rbgyr20_15[3] = { 0xff, 0xff, 0x00 }; + this->addColor("_rbgyr20_15", _rbgyr20_15); + int _rbgyr20_20[3] = { 0xff, 0x00, 0x00 }; + this->addColor("_rbgyr20_20", _rbgyr20_20); + + int _rbgyr20_21[3] = { 0x9d, 0x22, 0xc1 }; + this->addColor("_rbgyr20_21", _rbgyr20_21); + int _rbgyr20_22[3] = { 0x81, 0x06, 0xa5 }; + this->addColor("_rbgyr20_22", _rbgyr20_22); + int _rbgyr20_23[3] = { 0xff, 0xec, 0x00 }; + this->addColor("_rbgyr20_23", _rbgyr20_23); + int _rbgyr20_24[3] = { 0xff, 0xd6, 0x00 }; + this->addColor("_rbgyr20_24", _rbgyr20_24); + int _rbgyr20_25[3] = { 0xff, 0xbc, 0x00 }; + this->addColor("_rbgyr20_25", _rbgyr20_25); + int _rbgyr20_26[3] = { 0xff, 0x9c, 0x00 }; + this->addColor("_rbgyr20_26", _rbgyr20_26); + int _rbgyr20_27[3] = { 0xff, 0x7c, 0x00 }; + this->addColor("_rbgyr20_27", _rbgyr20_27); + int _rbgyr20_28[3] = { 0xff, 0x5c, 0x00 }; + this->addColor("_rbgyr20_28", _rbgyr20_28); + int _rbgyr20_29[3] = { 0xff, 0x3d, 0x00 }; + this->addColor("_rbgyr20_29", _rbgyr20_29); + int _rbgyr20_30[3] = { 0xff, 0x23, 0x00 }; + this->addColor("_rbgyr20_30", _rbgyr20_30); + int _rbgyr20_31[3] = { 0x00, 0xed, 0x12 }; + this->addColor("_rbgyr20_31", _rbgyr20_31); + int _rbgyr20_32[3] = { 0x00, 0xd5, 0x2a }; + this->addColor("_rbgyr20_32", _rbgyr20_32); + int _rbgyr20_33[3] = { 0x00, 0xb9, 0x46 }; + this->addColor("_rbgyr20_33", _rbgyr20_33); + int _rbgyr20_34[3] = { 0x00, 0x9b, 0x64 }; + this->addColor("_rbgyr20_34", _rbgyr20_34); + int _rbgyr20_35[3] = { 0x00, 0x7b, 0x84 }; + this->addColor("_rbgyr20_35", _rbgyr20_35); + int _rbgyr20_36[3] = { 0x00, 0x5b, 0xa4 }; + this->addColor("_rbgyr20_36", _rbgyr20_36); + int _rbgyr20_37[3] = { 0x00, 0x44, 0xbb }; + this->addColor("_rbgyr20_37", _rbgyr20_37); + int _rbgyr20_38[3] = { 0x00, 0x24, 0xdb }; + this->addColor("_rbgyr20_38", _rbgyr20_38); + int _rbgyr20_39[3] = { 0x00, 0x00, 0xff }; + this->addColor("_rbgyr20_39", _rbgyr20_39); + + int _rbgyr20_40[3] = { 0xff, 0xf1, 0x00 }; + this->addColor("_rbgyr20_40", _rbgyr20_40); + int _rbgyr20_41[3] = { 0xff, 0xdc, 0x00 }; + this->addColor("_rbgyr20_41", _rbgyr20_41); + int _rbgyr20_42[3] = { 0xff, 0xcb, 0x00 }; + this->addColor("_rbgyr20_42", _rbgyr20_42); + int _rbgyr20_43[3] = { 0xff, 0xc2, 0x00 }; + this->addColor("_rbgyr20_43", _rbgyr20_43); + int _rbgyr20_44[3] = { 0xff, 0xae, 0x00 }; + this->addColor("_rbgyr20_44", _rbgyr20_44); + int _rbgyr20_45[3] = { 0xff, 0x9f, 0x00 }; + this->addColor("_rbgyr20_45", _rbgyr20_45); + int _rbgyr20_46[3] = { 0xff, 0x86, 0x00 }; + this->addColor("_rbgyr20_46", _rbgyr20_46); + int _rbgyr20_47[3] = { 0xff, 0x59, 0x00 }; + this->addColor("_rbgyr20_47", _rbgyr20_47); + int _rbgyr20_48[3] = { 0x00, 0xff, 0x2d }; + this->addColor("_rbgyr20_48", _rbgyr20_48); + int _rbgyr20_49[3] = { 0x00, 0xff, 0x65 }; + this->addColor("_rbgyr20_49", _rbgyr20_49); + int _rbgyr20_50[3] = { 0x00, 0xff, 0xa5 }; + this->addColor("_rbgyr20_50", _rbgyr20_50); + int _rbgyr20_51[3] = { 0x00, 0xff, 0xdd }; + this->addColor("_rbgyr20_51", _rbgyr20_51); + int _rbgyr20_52[3] = { 0x00, 0xff, 0xff }; + this->addColor("_rbgyr20_52", _rbgyr20_52); + int _rbgyr20_53[3] = { 0x00, 0xe9, 0xff }; + this->addColor("_rbgyr20_53", _rbgyr20_53); + int _rbgyr20_54[3] = { 0x00, 0xad, 0xff }; + this->addColor("_rbgyr20_54", _rbgyr20_54); + int _rbgyr20_55[3] = { 0x00, 0x69, 0xff }; + this->addColor("_rbgyr20_55", _rbgyr20_55); + int _rbgyr20_56[3] = { 0xff, 0x00, 0xb9 }; + this->addColor("_rbgyr20_56", _rbgyr20_56); + int _rbgyr20_57[3] = { 0xff, 0x00, 0x63 }; + this->addColor("_rbgyr20_57", _rbgyr20_57); + int _rbgyr20_58[3] = { 0xff, 0x05, 0x00 }; + this->addColor("_rbgyr20_58", _rbgyr20_58); + int _rbgyr20_59[3] = { 0xff, 0x32, 0x00 }; + this->addColor("_rbgyr20_59", _rbgyr20_59); + int _rbgyr20_60[3] = { 0xff, 0x70, 0x00 }; + this->addColor("_rbgyr20_60", _rbgyr20_60); + int _rbgyr20_61[3] = { 0xff, 0xa4, 0x00 }; + this->addColor("_rbgyr20_61", _rbgyr20_61); + int _rbgyr20_62[3] = { 0xff, 0xba, 0x00 }; + this->addColor("_rbgyr20_62", _rbgyr20_62); + int _rbgyr20_63[3] = { 0xff, 0xd3, 0x00 }; + this->addColor("_rbgyr20_63", _rbgyr20_63); + int _rbgyr20_64[3] = { 0x42, 0x21, 0xdb }; + this->addColor("_rbgyr20_64", _rbgyr20_64); + int _rbgyr20_65[3] = { 0x10, 0x08, 0xf6 }; + this->addColor("_rbgyr20_65", _rbgyr20_65); + int _rbgyr20_66[3] = { 0x00, 0x13, 0xff }; + this->addColor("_rbgyr20_66", _rbgyr20_66); + int _rbgyr20_67[3] = { 0x00, 0x5b, 0xff }; + this->addColor("_rbgyr20_67", _rbgyr20_67); + int _rbgyr20_68[3] = { 0x00, 0xb3, 0xff }; + this->addColor("_rbgyr20_68", _rbgyr20_68); + int _rbgyr20_69[3] = { 0x00, 0xfc, 0xff }; + this->addColor("_rbgyr20_69", _rbgyr20_69); + int _rbgyr20_70[3] = { 0x00, 0xff, 0xcd }; + this->addColor("_rbgyr20_70", _rbgyr20_70); + int _rbgyr20_71[3] = { 0x00, 0xff, 0x74 }; + this->addColor("_rbgyr20_71", _rbgyr20_71); + int _rbgyr20_72[3] = { 0xff, 0x00, 0xf9 }; + this->addColor("_rbgyr20_72", _rbgyr20_72); + int _rbgyr20_73[3] = { 0x62, 0x31, 0xc9 }; + this->addColor("_rbgyr20_73", _rbgyr20_73); + + //------------------------------------------------------------------------ + // + // Palette by Russ H. + // + if (this->getPaletteByName("raich4_clrmid") == NULL) { + Palette r4; + r4.setName("raich4_clrmid"); + r4.addScalarAndColor(1.000000f, "_rbgyr20_20"); + r4.addScalarAndColor(0.900000f, "_rbgyr20_30"); + r4.addScalarAndColor(0.800000f, "_rbgyr20_29"); + r4.addScalarAndColor(0.700000f, "_rbgyr20_28"); + r4.addScalarAndColor(0.600000f, "_rbgyr20_27"); + r4.addScalarAndColor(0.500000f, "_rbgyr20_26"); + r4.addScalarAndColor(0.400000f, "_rbgyr20_25"); + r4.addScalarAndColor(0.300000f, "_rbgyr20_24"); + r4.addScalarAndColor(0.200000f, "_rbgyr20_23"); + r4.addScalarAndColor(0.100000f, "_rbgyr20_15"); + r4.addScalarAndColor(0.000000f, "none"); + r4.addScalarAndColor(-0.100000f, "_rbgyr20_10"); + r4.addScalarAndColor(-0.200000f, "_rbgyr20_31"); + r4.addScalarAndColor(-0.300000f, "_rbgyr20_32"); + r4.addScalarAndColor(-0.400000f, "_rbgyr20_33"); + r4.addScalarAndColor(-0.500000f, "_rbgyr20_34"); + r4.addScalarAndColor(-0.600000f, "_rbgyr20_35"); + r4.addScalarAndColor(-0.700000f, "_rbgyr20_36"); + r4.addScalarAndColor(-0.800000f, "_rbgyr20_37"); + r4.addScalarAndColor(-0.900000f, "_rbgyr20_38"); + r4.addScalarAndColor(-1.000000f, "_rbgyr20_39"); + addPalette(r4); + } + + //------------------------------------------------------------------------ + // + // Palette by Russ H. + // + if (this->getPaletteByName("raich6_clrmid") == NULL) { + Palette r6; + r6.setName("raich6_clrmid"); + r6.addScalarAndColor(1.000000f, "_rbgyr20_20"); + r6.addScalarAndColor(0.900000f, "_rbgyr20_47"); + r6.addScalarAndColor(0.800000f, "_rbgyr20_46"); + r6.addScalarAndColor(0.700000f, "_rbgyr20_45"); + r6.addScalarAndColor(0.600000f, "_rbgyr20_44"); + r6.addScalarAndColor(0.500000f, "_rbgyr20_43"); + r6.addScalarAndColor(0.400000f, "_rbgyr20_42"); + r6.addScalarAndColor(0.300000f, "_rbgyr20_41"); + r6.addScalarAndColor(0.200000f, "_rbgyr20_40"); + r6.addScalarAndColor(0.100000f, "_rbgyr20_15"); + r6.addScalarAndColor(0.000000f, "none"); + r6.addScalarAndColor(-0.100000f, "_rbgyr20_10"); + r6.addScalarAndColor(-0.200000f, "_rbgyr20_48"); + r6.addScalarAndColor(-0.300000f, "_rbgyr20_49"); + r6.addScalarAndColor(-0.400000f, "_rbgyr20_50"); + r6.addScalarAndColor(-0.500000f, "_rbgyr20_51"); + r6.addScalarAndColor(-0.600000f, "_rbgyr20_52"); + r6.addScalarAndColor(-0.700000f, "_rbgyr20_53"); + r6.addScalarAndColor(-0.800000f, "_rbgyr20_54"); + r6.addScalarAndColor(-0.900000f, "_rbgyr20_55"); + r6.addScalarAndColor(-1.000000f, "_rbgyr20_39"); + addPalette(r6); + } + + //------------------------------------------------------------------------ + // + // Palette by Russ H. + // + if (this->getPaletteByName("HSB8_clrmid") == NULL) { + Palette hsb8; + hsb8.setName("HSB8_clrmid"); + hsb8.addScalarAndColor(1.000000f, "_rbgyr20_15"); + hsb8.addScalarAndColor(0.900000f, "_rbgyr20_63"); + hsb8.addScalarAndColor(0.800000f, "_rbgyr20_62"); + hsb8.addScalarAndColor(0.700000f, "_rbgyr20_61"); + hsb8.addScalarAndColor(0.600000f, "_rbgyr20_60"); + hsb8.addScalarAndColor(0.500000f, "_rbgyr20_59"); + hsb8.addScalarAndColor(0.400000f, "_rbgyr20_58"); + hsb8.addScalarAndColor(0.300000f, "_rbgyr20_57"); + hsb8.addScalarAndColor(0.200000f, "_rbgyr20_56"); + hsb8.addScalarAndColor(0.100000f, "_rbgyr20_72"); + hsb8.addScalarAndColor(0.000000f, "none"); + hsb8.addScalarAndColor(-0.100000f, "_rbgyr20_73"); + hsb8.addScalarAndColor(-0.200000f, "_rbgyr20_64"); + hsb8.addScalarAndColor(-0.300000f, "_rbgyr20_65"); + hsb8.addScalarAndColor(-0.400000f, "_rbgyr20_66"); + hsb8.addScalarAndColor(-0.500000f, "_rbgyr20_67"); + hsb8.addScalarAndColor(-0.600000f, "_rbgyr20_68"); + hsb8.addScalarAndColor(-0.700000f, "_rbgyr20_69"); + hsb8.addScalarAndColor(-0.800000f, "_rbgyr20_70"); + hsb8.addScalarAndColor(-0.900000f, "_rbgyr20_71"); + hsb8.addScalarAndColor(-1.000000f, "_rbgyr20_10"); + addPalette(hsb8); + } + + //---------------------------------------------------------------------- + // Positive/Negative palette + // + if (this->getPaletteByName("POS_NEG") == NULL) { + this->addColor("pos_neg_blue", 0x00, 0x00, 0xff ); + this->addColor("pos_neg_red", 0xff, 0x00, 0x00 ); + + Palette posNeg; + posNeg.setName("POS_NEG"); + + posNeg.addScalarAndColor(1.00f, "pos_neg_red"); + posNeg.addScalarAndColor(0.0001f, "none"); + posNeg.addScalarAndColor(-0.0001f, "pos_neg_blue"); + + addPalette(posNeg); + } + + if (modifiedStatus == false) { + this->clearModified();//this clears modified status on all members, to match our status + } + + static bool printAllPalettesFlag = false; + if (printAllPalettesFlag) { + printAllPalettesFlag = false; + for (const auto p : this->palettes) { + std::cout << "----------------------" << std::endl; + std::cout << p->toString() << std::endl; + std::cout << "INVERTED" << std::endl; + std::cout << p->getInvertedPalette()->toString() << std::endl; + std::cout << "SEPARATE SIGN" << std::endl; + std::cout << p->getSignSeparateInvertedPalette()->toString() << std::endl; + std::cout << "SEPARATE NONE"; + if (p->getSignSeparateInvertedPalette() == p->getNoneSeparateInvertedPalette()) { + std::cout << " same as SEPARATE SIGN" << std::endl; + } + else { + std::cout << std::endl; + std::cout << p->getNoneSeparateInvertedPalette()->toString() << std::endl; + } + } } } diff --git a/src/Files/RgbaFile.cxx b/src/Files/RgbaFile.cxx index 1c5a08f20447f42b1ae664abdce34fd93cdd800a..278b0783cb26ead16cf5c3822ad0a3943a01f2f6 100644 --- a/src/Files/RgbaFile.cxx +++ b/src/Files/RgbaFile.cxx @@ -20,6 +20,7 @@ #include "CaretAssert.h" +#include "DataFileException.h" #include "DataFileTypeEnum.h" #include "GiftiFile.h" #include "MathFunctions.h" @@ -95,7 +96,53 @@ RgbaFile::validateDataArraysAfterReading() { this->initializeMembersRgbaFile(); + const int32_t numberOfDataArrays = this->giftiFile->getNumberOfDataArrays(); + if (numberOfDataArrays != 1) { + throw DataFileException(getFileName(), + "GIFTI RGBA files must have one GIFTI Data Array"); + } + + const GiftiDataArray* gda = this->giftiFile->getDataArray(0); + if (gda->getNumberOfRows() <= 0) { + throw DataFileException(getFileName(), + "GIFTI RGBA file is empty (number of rows is zero)"); + } + + m_numberOfComponents = gda->getNumberOfComponents(); + if ((m_numberOfComponents < 3) + || (m_numberOfComponents > 4)) { + throw DataFileException(getFileName(), + "GIFTI RGBA file must have four components but contains " + + AString::number(m_numberOfComponents) + + " components."); + } + if (gda->getDataType() != NiftiDataTypeEnum::NIFTI_TYPE_FLOAT32) { + throw DataFileException("GIFTI RGBA files must be FLOAT data type. " + "Type of data array is: " + + NiftiDataTypeEnum::toName(gda->getDataType())); + } + this->verifyDataArraysHaveSameNumberOfRows(3, 4); + + m_dataArray = this->giftiFile->getDataArray(0); + m_numberOfVertices = gda->getNumberOfRows(); + + bool rescaleZeroToOneFlag = false; + const int32_t numValues = m_numberOfComponents * m_numberOfVertices; + float* floatData = m_dataArray->getDataPointerFloat(); + for (int32_t i = 0; i < numValues; i++) { + if (floatData[i] > 1.1f) { + rescaleZeroToOneFlag = true; + break; + } + } + + if (rescaleZeroToOneFlag) { + for (int32_t i = 0; i < numValues; i++) { + floatData[i] /= 255.0; + } + } + } /** @@ -107,12 +154,7 @@ RgbaFile::validateDataArraysAfterReading() int32_t RgbaFile::getNumberOfNodes() const { - int32_t numNodes = 0; - int32_t numDataArrays = this->giftiFile->getNumberOfDataArrays(); - if (numDataArrays > 0) { - numNodes = this->giftiFile->getDataArray(0)->getNumberOfRows(); - } - return numNodes; + return m_numberOfVertices; } /** @@ -135,6 +177,9 @@ RgbaFile::getNumberOfColumns() const void RgbaFile::initializeMembersRgbaFile() { + m_dataArray = 0; + m_numberOfVertices = 0; + m_numberOfComponents = 0; } /** @@ -149,4 +194,43 @@ RgbaFile::copyHelperRgbaFile(const RgbaFile& /*sf*/) this->validateDataArraysAfterReading(); } +/** + * Get the RGBA data for the given vertex. + * + * @param vertexIndex + * Index of the vertex. + * @param rgbaOut + * Output containing RGBA values for vertex. + */ +void +RgbaFile::getVertexRGBA(const int32_t vertexIndex, + float* rgbaOut) const +{ + CaretAssert((vertexIndex >= 0) + && (vertexIndex < m_numberOfVertices)); + + const float* floatData = m_dataArray->getDataPointerFloat(); + + if (m_numberOfComponents == 4) { + const int32_t v4 = vertexIndex * 4; + CaretAssertArrayIndex(floatData, m_numberOfVertices * m_numberOfComponents, v4 + 3); + rgbaOut[0] = floatData[v4]; + rgbaOut[1] = floatData[v4+1]; + rgbaOut[2] = floatData[v4+2]; + rgbaOut[3] = floatData[v4+3]; + } + else if (m_numberOfComponents == 3) { + const int32_t v3 = vertexIndex * 3; + CaretAssertArrayIndex(floatData, m_numberOfVertices * m_numberOfComponents, v3 + 2); + rgbaOut[0] = floatData[v3]; + rgbaOut[1] = floatData[v3+1]; + rgbaOut[2] = floatData[v3+2]; + rgbaOut[3] = 1.0f; + } + else { + CaretAssert(0); + } +} + + diff --git a/src/Files/RgbaFile.h b/src/Files/RgbaFile.h index 962a6df9928fdd0610a486c3f3175f7559f83a9b..3a57222f6c853ba4de95aabff6bd84a4db3eed31 100644 --- a/src/Files/RgbaFile.h +++ b/src/Files/RgbaFile.h @@ -51,6 +51,9 @@ namespace caret { virtual int32_t getNumberOfColumns() const; + void getVertexRGBA(const int32_t vertexIndex, + float* rgbaOut) const; + protected: /** * Validate the contents of the file after it @@ -64,6 +67,13 @@ namespace caret { void initializeMembersRgbaFile(); private: + + GiftiDataArray* m_dataArray = 0; + + int32_t m_numberOfVertices = 0; + + int32_t m_numberOfComponents = 0; + }; } // namespace diff --git a/src/Files/RibbonMappingHelper.cxx b/src/Files/RibbonMappingHelper.cxx index a01a4c26e7801367a4e5d6ff08a8cbbdeb838035..316b4a478f01455aac9b37c65c9eb91ff9bb5718 100644 --- a/src/Files/RibbonMappingHelper.cxx +++ b/src/Files/RibbonMappingHelper.cxx @@ -84,18 +84,43 @@ namespace { std::vector m_tris; std::vector m_quads; - PolyInfo(const SurfaceFile* innerSurf, const SurfaceFile* outerSurf, const int32_t node);//surfaces MUST be in node correspondence, otherwise SEVERE strangeness, possible crashes + PolyInfo(const SurfaceFile* innerSurf, const SurfaceFile* outerSurf, const int32_t node, const bool& thinColumn = false);//surfaces MUST be in node correspondence, otherwise SEVERE strangeness, possible crashes PolyInfo() {}; int isInside(const float* xyz);//0 for no, 2 for yes, 1 for if only half the triangulations (between the two triangulations of one of the quad faces) private: - void addTri(const SurfaceFile* innerSurf, const SurfaceFile* outerSurf, const int32_t root, const int32_t node2, const int32_t node3);//adds the tri for each surface, plus the quad + void addTri(const SurfaceFile* innerSurf, const SurfaceFile* outerSurf, const int32_t* myTri, const int rootIndex, const bool& thinColumn);//adds the tri for each surface, plus the quad }; - void PolyInfo::addTri(const SurfaceFile* innerSurf, const SurfaceFile* outerSurf, const int32_t root, const int32_t node2, const int32_t node3) - { - m_tris.push_back(TriInfo(innerSurf->getCoordinate(root), innerSurf->getCoordinate(node2), innerSurf->getCoordinate(node3))); - m_tris.push_back(TriInfo(outerSurf->getCoordinate(root), outerSurf->getCoordinate(node2), outerSurf->getCoordinate(node3))); - m_quads.push_back(QuadInfo(innerSurf->getCoordinate(node2), innerSurf->getCoordinate(node3), outerSurf->getCoordinate(node3), outerSurf->getCoordinate(node2))); + void PolyInfo::addTri(const SurfaceFile* innerSurf, const SurfaceFile* outerSurf, const int32_t* myTri, const int rootIndex, const bool& thinColumn) + {//tries to orient normals consistently with respect to inside/outside, but it doesn't currently matter + int root = myTri[rootIndex], node2 = myTri[(rootIndex + 1) % 3], node3 = myTri[(rootIndex + 2) % 3]; + if (thinColumn) + { + Vector3D innerRoot = innerSurf->getCoordinate(root), inner2 = innerSurf->getCoordinate(node2), inner3 = innerSurf->getCoordinate(node3); + Vector3D outerRoot = outerSurf->getCoordinate(root), outer2 = outerSurf->getCoordinate(node2), outer3 = outerSurf->getCoordinate(node3); + //use an ordering trick of the 3 vertices so that the summing order is always 01, 12, or 20 - doesn't actually matter (at least on x86), but hey + Vector3D innerEdge2 = (innerRoot + inner2) / 2, innerEdge3 = (inner3 + innerRoot) / 2; + Vector3D outerEdge2 = (outerRoot + outer2) / 2, outerEdge3 = (outer3 + outerRoot) / 2; + //for consistency in floating point rounding, we need to generate the sum of the 3 coordinates in the same order regardless of rootIndex + //so, use the original triangle definition and 0, 1, 2 order + //this doesn't matter for edges because there only 2 coordinates are added together + Vector3D innerCenter = (Vector3D(innerSurf->getCoordinate(myTri[0])) + + Vector3D(innerSurf->getCoordinate(myTri[1])) + + Vector3D(innerSurf->getCoordinate(myTri[2]))) / 3; + Vector3D outerCenter = (Vector3D(outerSurf->getCoordinate(myTri[0])) + + Vector3D(outerSurf->getCoordinate(myTri[1])) + + Vector3D(outerSurf->getCoordinate(myTri[2]))) / 3; + m_tris.push_back(TriInfo(innerRoot, innerEdge3, innerCenter)); + m_tris.push_back(TriInfo(innerRoot, innerCenter, innerEdge2)); + m_tris.push_back(TriInfo(outerRoot, outerEdge2, outerCenter)); + m_tris.push_back(TriInfo(outerRoot, outerCenter, outerEdge3)); + m_quads.push_back(QuadInfo(innerEdge2, innerCenter, outerCenter, outerEdge2)); + m_quads.push_back(QuadInfo(innerCenter, innerEdge3, outerEdge3, outerCenter)); + } else { + m_tris.push_back(TriInfo(innerSurf->getCoordinate(root), innerSurf->getCoordinate(node3), innerSurf->getCoordinate(node2))); + m_tris.push_back(TriInfo(outerSurf->getCoordinate(root), outerSurf->getCoordinate(node2), outerSurf->getCoordinate(node3))); + m_quads.push_back(QuadInfo(innerSurf->getCoordinate(node2), innerSurf->getCoordinate(node3), outerSurf->getCoordinate(node3), outerSurf->getCoordinate(node2))); + } } int PolyInfo::isInside(const float* xyz) @@ -117,7 +142,7 @@ namespace return 0; } - PolyInfo::PolyInfo(const caret::SurfaceFile* innerSurf, const caret::SurfaceFile* outerSurf, const int32_t node) + PolyInfo::PolyInfo(const caret::SurfaceFile* innerSurf, const caret::SurfaceFile* outerSurf, const int32_t node, const bool& thinColumn) { CaretPointer myTopoHelp = innerSurf->getTopologyHelper(); int numTiles; @@ -127,13 +152,13 @@ namespace const int32_t* myTri = innerSurf->getTriangle(myTiles[i]); if (myTri[0] == node) { - addTri(innerSurf, outerSurf, myTri[0], myTri[1], myTri[2]); + addTri(innerSurf, outerSurf, myTri, 0, thinColumn); } else { if (myTri[1] == node) { - addTri(innerSurf, outerSurf, myTri[1], myTri[2], myTri[0]); + addTri(innerSurf, outerSurf, myTri, 1, thinColumn); } else { - addTri(innerSurf, outerSurf, myTri[2], myTri[0], myTri[1]); + addTri(innerSurf, outerSurf, myTri, 2, thinColumn); } } } @@ -213,7 +238,7 @@ namespace } float computeVoxelFraction(const VolumeSpace& myVolSpace, const int64_t* ijk, PolyInfo& myPoly, const int divisions, - const Vector3D& ivec, const Vector3D& jvec, const Vector3D& kvec) + const Vector3D& ivec, const Vector3D& jvec, const Vector3D& kvec) { Vector3D myLowCorner; myVolSpace.indexToSpace(ijk[0] - 0.5f, ijk[1] - 0.5f, ijk[2] - 0.5f, myLowCorner); @@ -241,7 +266,7 @@ namespace } void RibbonMappingHelper::computeWeightsRibbon(vector >& myWeightsOut, const VolumeSpace& myVolSpace, const SurfaceFile* innerSurf, const SurfaceFile* outerSurf, - const float* roiFrame, const int& numDivisions) + const float* roiFrame, const int& numDivisions, const bool& thinColumn) { if (!innerSurf->hasNodeCorrespondence(*outerSurf)) { @@ -269,7 +294,7 @@ void RibbonMappingHelper::computeWeightsRibbon(vector >& myW myWeightsOut[node].reserve(maxVoxelCount); float tempf; int64_t node3 = node * 3; - PolyInfo myPoly(innerSurf, outerSurf, node);//build the polygon + PolyInfo myPoly(innerSurf, outerSurf, node, thinColumn);//build the polygon Vector3D minIndex, maxIndex, tempvec; myVolSpace.spaceToIndex(innerCoords + node3, minIndex);//find the bounding box in VOLUME INDEX SPACE, starting with the center nodes maxIndex = minIndex; diff --git a/src/Files/RibbonMappingHelper.h b/src/Files/RibbonMappingHelper.h index c69288db79fdb856eff89ee09dee36e82c3beaa1..5ca5fd247c1ef4520bd5cef7deae942e6413a340 100644 --- a/src/Files/RibbonMappingHelper.h +++ b/src/Files/RibbonMappingHelper.h @@ -50,7 +50,8 @@ namespace caret public: ///compute per-vertex ribbon mapping weights - surfaces must have vertex correspondence, or an exception is thrown static void computeWeightsRibbon(std::vector >& myWeightsOut, const VolumeSpace& myVolSpace, - const SurfaceFile* innerSurf, const SurfaceFile* outerSurf, const float* roiFrame = NULL, const int& numDivisions = 3); + const SurfaceFile* innerSurf, const SurfaceFile* outerSurf, + const float* roiFrame = NULL, const int& numDivisions = 3, const bool& thinColumn = false); }; } diff --git a/src/Files/SceneFile.cxx b/src/Files/SceneFile.cxx index fc0fcd0522675098e4bdcb5dd0fe1d4f2ad2cd40..9f9dbc275ac1d67f5cd93d37d00ada86184377c5 100644 --- a/src/Files/SceneFile.cxx +++ b/src/Files/SceneFile.cxx @@ -19,10 +19,12 @@ */ /*LICENSE_END*/ +#include #include #include #include +#include #define __SCENE_FILE_DECLARE__ #include "SceneFile.h" @@ -41,9 +43,11 @@ #include "SceneClassArray.h" #include "SceneFileSaxReader.h" #include "SceneInfo.h" +#include "ScenePathName.h" #include "SceneXmlElements.h" #include "SceneWriterXml.h" #include "SpecFile.h" +#include "SystemUtilities.h" #include "XmlSaxParser.h" #include "XmlWriter.h" @@ -63,7 +67,10 @@ SceneFile::SceneFile() : CaretDataFile(DataFileTypeEnum::SCENE) { m_balsaStudyID = ""; - m_baseDirectory = ""; + m_balsaStudyTitle = ""; + m_balsaCustomBaseDirectory = ""; + m_balsaExtractToDirectoryName = ""; + m_basePathType = SceneFileBasePathTypeEnum::AUTOMATIC; m_metadata = new GiftiMetaData(); } @@ -93,7 +100,10 @@ SceneFile::clear() m_metadata->clear(); m_balsaStudyID = ""; - m_baseDirectory = ""; + m_balsaStudyTitle = ""; + m_balsaCustomBaseDirectory = ""; + m_balsaExtractToDirectoryName = ""; + m_basePathType = SceneFileBasePathTypeEnum::AUTOMATIC; for (std::vector::iterator iter = m_scenes.begin(); iter != m_scenes.end(); @@ -490,25 +500,72 @@ SceneFile::setBalsaStudyID(const AString& balsaStudyID) } /** - * @return The Base Directory + * @return The BALSA Study Title. */ AString -SceneFile::getBaseDirectory() const +SceneFile::getBalsaStudyTitle() const { - return m_baseDirectory; + return m_balsaStudyTitle; } /** - * Set the Base Directory. + * Set the BALSA Study Title. + * + * @param balsaStudyTitle + * New value for BALSA Study Title. + */ +void +SceneFile::setBalsaStudyTitle(const AString& balsaStudyTitle) +{ + if (balsaStudyTitle != m_balsaStudyTitle) { + m_balsaStudyTitle = balsaStudyTitle; + setModified(); + } +} + +/** + * @return The Custom Base Directory + */ +AString +SceneFile::getBalsaCustomBaseDirectory() const +{ + return m_balsaCustomBaseDirectory; +} + +/** + * Set the Custom Base Directory. * * @param baseDirectory * New value for Base Directory. */ void -SceneFile::setBaseDirectory(const AString& baseDirectory) +SceneFile::setBalsaCustomBaseDirectory(const AString& balsaBaseDirectory) { - if (baseDirectory != m_baseDirectory) { - m_baseDirectory = baseDirectory; + if (balsaBaseDirectory != m_balsaCustomBaseDirectory) { + m_balsaCustomBaseDirectory = balsaBaseDirectory; + setModified(); + } +} + +/** + * @return The base path type. + */ +SceneFileBasePathTypeEnum::Enum +SceneFile::getBasePathType() const +{ + return m_basePathType; +} + +/** + * Set the base path type. + * + * @param basePathType + * New type for base path. + */ +void SceneFile::setBasePathType(const SceneFileBasePathTypeEnum::Enum basePathType) +{ + if (basePathType != m_basePathType) { + m_basePathType = basePathType; setModified(); } } @@ -534,7 +591,8 @@ SceneFile::readFile(const AString& filenameIn) checkFileReadability(filename); this->setFileName(filename); - SceneFileSaxReader saxReader(this); + SceneFileSaxReader saxReader(this, + filename); std::auto_ptr parser(XmlSaxParser::createXmlParser()); try { parser->parseFile(filename, &saxReader); @@ -646,8 +704,46 @@ SceneFile::writeFile(const AString& filename) xmlWriter.writeStartElement(SceneFile::XML_TAG_SCENE_INFO_DIRECTORY_TAG); xmlWriter.writeElementCData(SceneXmlElements::SCENE_INFO_BALSA_STUDY_ID_TAG, getBalsaStudyID()); - xmlWriter.writeElementCData(SceneXmlElements::SCENE_INFO_BASE_DIRECTORY_TAG, - getBaseDirectory()); + xmlWriter.writeElementCData(SceneXmlElements::SCENE_INFO_BALSA_STUDY_TITLE_TAG, + getBalsaStudyTitle()); + switch (getBasePathType()) { + case SceneFileBasePathTypeEnum::AUTOMATIC: + xmlWriter.writeElementCData(SceneXmlElements::SCENE_INFO_BALSA_BASE_DIRECTORY_TAG, + ""); + break; + case SceneFileBasePathTypeEnum::CUSTOM: + { + /* + * Write base path as a path RELATIVE to the scene file + * but only when base path type is CUSTOM + * Note: we do not use FileInformation::getCanonicalFilePath() + * because it returns an empty string if the file DOES NOT exist + * and this may occur since the file may be new and has not + * been closed. + */ + if ( ! getBalsaCustomBaseDirectory().isEmpty()) { + const AString baseDirAbsPath = FileInformation(getBalsaCustomBaseDirectory()).getAbsoluteFilePath(); + const AString sceneFileAbsPath = FileInformation(filename).getAbsoluteFilePath(); + ScenePathName basePathName("basePathName", + baseDirAbsPath); + + const AString relativeBasePath = basePathName.getRelativePathToSceneFile(sceneFileAbsPath); + + //std::cout << "baseDirAbsPath: " << baseDirAbsPath << std::endl; + //std::cout << "sceneFileAbsPath: " << sceneFileAbsPath << std::endl; + //std::cout << "relativeTempFileName: " << relativeBasePath << std::endl; + + xmlWriter.writeElementCData(SceneXmlElements::SCENE_INFO_BALSA_BASE_DIRECTORY_TAG, + relativeBasePath); + } + } + break; + } + xmlWriter.writeElementCData(SceneXmlElements::SCENE_INFO_BALSA_EXTRACT_TO_DIRECTORY_TAG, + getBalsaExtractToDirectoryName()); + xmlWriter.writeElementCData(SceneXmlElements::SCENE_INFO_BASE_PATH_TYPE, + SceneFileBasePathTypeEnum::toName(getBasePathType())); + for (int32_t i = 0; i < numScenes; i++) { m_scenes[i]->getSceneInfo()->writeSceneInfo(xmlWriter, i); @@ -746,3 +842,317 @@ SceneFile::addToDataFileContentInformation(DataFileContentInformation& dataFileI } } +/** + * @return Extract to directory name for zip file + */ +AString +SceneFile::getBalsaExtractToDirectoryName() const +{ + return m_balsaExtractToDirectoryName; +} + +/** + * Set Extract to directory name for zip file + * + * @param extractToDirectoryName + * New value for Extract to directory name for zip file + */ +void +SceneFile::setBalsaExtractToDirectoryName(const AString& extractToDirectoryName) +{ + if (extractToDirectoryName != m_balsaExtractToDirectoryName) { + setModified(); + m_balsaExtractToDirectoryName = extractToDirectoryName; + } +} + +/** + * Find the base directory that is a directory that is parent to all loaded data files + * and also including the scene file. + * + * @param baseDirectoryOut + * Output containing the base directory + * @param missingFileNamesOut + * Will contain data files that are in scenes but do not exist. + * @param errorMessageOut + * Error message if finding base directory fails + * @return + * True if the base directory is valid, else false. + */ +bool +SceneFile::findBaseDirectoryForDataFiles(AString& baseDirectoryOut, + std::vector& missingFileNamesOut, + AString& errorMessageOut) const +{ + baseDirectoryOut.clear(); + missingFileNamesOut.clear(); + errorMessageOut.clear(); + + const AString directorySeparator("/"); + + std::vector allFileNames = getAllDataFileNamesFromAllScenes(); + allFileNames.push_back(getFileName()); + + /* + * Find a unique set of directory names used by the data files and + * also verify that all files exist. + */ + std::set directoryNamesUniqueSet; + std::set missingFileNames; + for (auto name : allFileNames) { + if (DataFile::isFileOnNetwork(name)) { + /* assume file on network are valid */ + } + else { + FileInformation fileInfo(name); + if (fileInfo.exists()) { + AString dirName = fileInfo.getAbsolutePath().trimmed(); + if ( ! dirName.isEmpty()) { + /* + * QDir::cleanPath() removes multiple separators and resolves "." or ".." + * QDir::fromNativeSeparators() ensures "/" is used for the directory separator. + */ + dirName = QDir::cleanPath(dirName); + dirName = QDir::fromNativeSeparators(dirName); + if ( ! dirName.isEmpty()) { + directoryNamesUniqueSet.insert(dirName); + } + } + } + else { + missingFileNames.insert(name); + } + } + } + + const FileInformation fileInfo(getFileName()); + const AString sceneFilePath = fileInfo.getAbsolutePath().trimmed(); + + missingFileNamesOut.insert(missingFileNamesOut.end(), + missingFileNames.begin(), + missingFileNames.end()); + + const int32_t numDirs = static_cast(directoryNamesUniqueSet.size()); + if (numDirs <= 0) { + /* + * if no valid files in scene, user path of scene file. + */ + baseDirectoryOut = sceneFilePath; + return true; + } + + /* + * Compare the first directory with all other directories + * to find longest common directory path. + * Each directory is split into its path components and they + * are compared. + */ + bool firstFlag = true; + std::vector longestPathMatch; + for (const auto dirName : directoryNamesUniqueSet) { + if (firstFlag) { + firstFlag = false; + longestPathMatch = AString::stringListToVector(dirName.split(directorySeparator)); + } + else { + const std::vector otherPath = AString::stringListToVector(dirName.split(directorySeparator)); + const int32_t matchCount = AString::matchingCount(longestPathMatch, + otherPath); + longestPathMatch.resize(matchCount); + } + } + + baseDirectoryOut = sceneFilePath; + if ( ! longestPathMatch.empty()) { + /* + * Assemble the path components into a directory. + */ + baseDirectoryOut = AString::join(longestPathMatch, + directorySeparator); + } + + /* + * If no "longest path", files are on multiple disks + */ + if (baseDirectoryOut.isEmpty()) { + if (SystemUtilities::isWindowsOperatingSystem()) { + /* + * On Windows, there is no "root directory" + */ + baseDirectoryOut = ""; + errorMessageOut = ("Files appear to be on different disks. On the Windows Operating System, " + "there is no directory that is parent to the disks. Files will need to " + "be moved so that they are on one disk"); + return false; + } + else { + /* + * On Unix, user root directory + */ + baseDirectoryOut = directorySeparator; + } + } + + return true; +} + +/** + * @return The base directory for all data files and all of + * the base directorys ancestors (parent directory + * up to root directory). + * + * @param maximumAncestorCount + * Maximum number of ancestor directories for output + */ +std::vector +SceneFile::getBaseDirectoryHierarchyForDataFiles(const int32_t maximumAncestorCount) +{ + std::vector names; + + AString baseDirectoryName; + std::vector missingFileNames; + AString errorMessage; + if (findBaseDirectoryForDataFiles(baseDirectoryName, + missingFileNames, + errorMessage)) { + QDir dir(baseDirectoryName); + + for (int32_t i = 0; i < maximumAncestorCount; i++) { + names.push_back(dir.absolutePath()); + + if (dir.isRoot()) { + break; + } + else { + if ( ! dir.cdUp()) { + break; + } + } + } + } + + return names; +} + +/** + * @return A vector containing the names of all data files from all scenes. + */ +std::vector +SceneFile::getAllDataFileNamesFromAllScenes() const +{ + const bool includeSpecFileFlag = false; + + std::set filenamesSet; + + /** + * Find all 'path name' elements from ALL scenes + */ + for (Scene* scene: m_scenes) { + CaretAssert(scene); + std::vector children = scene->getDescendants(); + for (SceneObject* sceneObject : children) { + CaretAssert(sceneObject); + if (sceneObject->getDataType() == SceneObjectDataTypeEnum::SCENE_PATH_NAME) { + const ScenePathName* scenePathName = dynamic_cast(sceneObject); + /* + * Will be NULL for 'path name arrays' which we ignore + */ + if (scenePathName != NULL) { + /* + * files in spec file are named "fileName" in the scene + * specFile is named "specFileName" + * and these names are unique to name of files in the spec file + */ + bool useNameFlag = false; + if (sceneObject->getName() == "fileName") { + useNameFlag = true; + } + else if (sceneObject->getName() == "specFileName") { + useNameFlag = includeSpecFileFlag; + } + if (useNameFlag) { + AString pathName = scenePathName->stringValue().trimmed(); + if ( ! pathName.isEmpty()) { + QFileInfo fileInfo(pathName); + const QString absPathName = fileInfo.absoluteFilePath(); + if ( ! absPathName.isEmpty()) { + pathName = absPathName; + } + filenamesSet.insert(pathName); + } + } + } + } + } + } + + std::vector filenamesOut(filenamesSet.begin(), + filenamesSet.end()); + + return filenamesOut; +} + +/** + * @return Default name for a ZIP file containing the scene file and its data files. + */ +AString +SceneFile::getDefaultZipFileName() const +{ + return FileInformation::replaceExtension(getFileName(), ".zip"); +} + +/** + * @return The default extract to directory name (end of default base directory) + */ +AString +SceneFile::getDefaultExtractToDirectoryName() const +{ + AString directoryName; + + AString baseDirectoryName; + std::vector missingFileNames; + AString errorMessage; + if (findBaseDirectoryForDataFiles(baseDirectoryName, + missingFileNames, + errorMessage)) { + QDir dir(baseDirectoryName); + directoryName = dir.dirName(); + } + + if (directoryName.isEmpty()) { + QDir dir(SystemUtilities::systemCurrentDirectory()); + directoryName = dir.dirName(); + } + + return directoryName; +} + +/** + * @return True if this scene is modified. + */ +bool +SceneFile::isModified() const +{ + if (CaretDataFile::isModified()) { + return true; + } + for (const auto scene : m_scenes) { + if (scene->isModified()) { + return true; + } + } + + return false; +} + +/** + * Clear the modified status of this scene. + */ +void +SceneFile::clearModified() +{ + CaretDataFile::clearModified(); + for (auto scene : m_scenes) { + scene->clearModified(); + } +} diff --git a/src/Files/SceneFile.h b/src/Files/SceneFile.h index 9881cb424ebc23301113063496be763ab9a3f435..fdffd58976d8836621af74b12aac2d8406b9f5e0 100644 --- a/src/Files/SceneFile.h +++ b/src/Files/SceneFile.h @@ -23,6 +23,7 @@ #include "CaretDataFile.h" +#include "SceneFileBasePathTypeEnum.h" namespace caret { @@ -87,9 +88,25 @@ namespace caret { void setBalsaStudyID(const AString& balsaStudyID); - AString getBaseDirectory() const; + AString getBalsaStudyTitle() const; - void setBaseDirectory(const AString& baseDirectory); + void setBalsaStudyTitle(const AString& balsaStudyTitle); + + SceneFileBasePathTypeEnum::Enum getBasePathType() const; + + void setBasePathType(const SceneFileBasePathTypeEnum::Enum basePathType); + + AString getBalsaCustomBaseDirectory() const; + + void setBalsaCustomBaseDirectory(const AString& balsaBaseDirectory); + + bool findBaseDirectoryForDataFiles(AString& baseDirectoryOut, + std::vector& missingFileNamesOut, + AString& errorMessageOut) const; + + std::vector getBaseDirectoryHierarchyForDataFiles(const int32_t maximumAncestorCount = 25); + + std::vector getAllDataFileNamesFromAllScenes() const; void reorderScenes(std::vector& orderedScenes); @@ -97,6 +114,18 @@ namespace caret { Scene* releaseScene(const int32_t& index); + AString getDefaultZipFileName() const; + + AString getBalsaExtractToDirectoryName() const; + + void setBalsaExtractToDirectoryName(const AString& balsaExtractToDirectoryName); + + AString getDefaultExtractToDirectoryName() const; + + virtual bool isModified() const override; + + virtual void clearModified() override; + // ADD_NEW_METHODS_HERE /** Version of file */ @@ -124,8 +153,17 @@ namespace caret { /** the BALSA Study ID */ AString m_balsaStudyID; - /** the Base Directory */ - AString m_baseDirectory; + /** the BALSA Study Title */ + AString m_balsaStudyTitle; + + /** the Base Path Type */ + SceneFileBasePathTypeEnum::Enum m_basePathType; + + /** the Custom Base Directory */ + AString m_balsaCustomBaseDirectory; + + /** The "extract to" directory name */ + AString m_balsaExtractToDirectoryName; // ADD_NEW_MEMBERS_HERE @@ -137,7 +175,7 @@ namespace caret { const AString SceneFile::XML_TAG_SCENE_FILE = "SceneFile"; const AString SceneFile::XML_ATTRIBUTE_VERSION = "Version"; const AString SceneFile::XML_TAG_SCENE_INFO_DIRECTORY_TAG = "SceneInfoDirectory"; - const float SceneFile::s_sceneFileVersion = 2.0; + const float SceneFile::s_sceneFileVersion = 3.0; #endif // __SCENE_FILE_DECLARE__ } // namespace diff --git a/src/Files/SceneFileSaxReader.cxx b/src/Files/SceneFileSaxReader.cxx index a37d53e97264a49221448bd6f50e7d475f72a2e2..ed8f8a27db69ad27c6e04112e63be03186b8741d 100644 --- a/src/Files/SceneFileSaxReader.cxx +++ b/src/Files/SceneFileSaxReader.cxx @@ -22,6 +22,7 @@ #include "CaretAssert.h" #include "CaretLogger.h" +#include "FileInformation.h" #include "GiftiMetaDataSaxReader.h" #include "GiftiXmlElements.h" #include "Scene.h" @@ -29,6 +30,7 @@ #include "SceneFileSaxReader.h" #include "SceneInfo.h" #include "SceneInfoSaxReader.h" +#include "ScenePathName.h" #include "SceneXmlElements.h" #include "XmlAttributes.h" @@ -45,10 +47,12 @@ using namespace caret; /** * constructor. */ -SceneFileSaxReader::SceneFileSaxReader(SceneFile* sceneFile) +SceneFileSaxReader::SceneFileSaxReader(SceneFile* sceneFile, + const AString& sceneFileName) { CaretAssert(sceneFile); m_sceneFile = sceneFile; + m_sceneFileName = sceneFileName; m_state = STATE_NONE; m_stateStack.push(m_state); m_elementText = ""; @@ -56,6 +60,7 @@ SceneFileSaxReader::SceneFileSaxReader(SceneFile* sceneFile) m_sceneSaxReader = NULL; m_sceneInfoSaxReader = NULL; m_scene = NULL; + m_baseBathTypeWasFoundFlag = false; } /** @@ -128,8 +133,18 @@ SceneFileSaxReader::startElement(const AString& namespaceURI, else if (qName == SceneXmlElements::SCENE_INFO_BALSA_STUDY_ID_TAG) { m_state = STATE_SCENE_INFO_BALSA_STUDY_ID; } - else if (qName == SceneXmlElements::SCENE_INFO_BASE_DIRECTORY_TAG) { - m_state = STATE_SCENE_INFO_BASE_DIRECTORY; + else if (qName == SceneXmlElements::SCENE_INFO_BALSA_STUDY_TITLE_TAG) { + m_state = STATE_SCENE_INFO_BALSA_STUDY_TITLE; + } + else if ((qName == SceneXmlElements::SCENE_INFO_BALSA_BASE_DIRECTORY_TAG) + || (qName == SceneXmlElements::SCENE_INFO_BALSA_BASE_DIRECTORY_TAG_OLD)) { + m_state = STATE_SCENE_INFO_BALSA_BASE_DIRECTORY; + } + else if (qName == SceneXmlElements::SCENE_INFO_BALSA_EXTRACT_TO_DIRECTORY_TAG) { + m_state = STATE_SCENE_INFO_BALSA_EXTRACT_TO_DIRECTORY; + } + else if (qName == SceneXmlElements::SCENE_INFO_BASE_PATH_TYPE) { + m_state = STATE_SCENE_INFO_BASE_PATH_TYPE; } else { const AString msg = XmlUtilities::createInvalidChildElementMessage(SceneXmlElements::SCENE_INFO_TAG, @@ -142,7 +157,13 @@ SceneFileSaxReader::startElement(const AString& namespaceURI, break; case STATE_SCENE_INFO_BALSA_STUDY_ID: break; - case STATE_SCENE_INFO_BASE_DIRECTORY: + case STATE_SCENE_INFO_BALSA_STUDY_TITLE: + break; + case STATE_SCENE_INFO_BALSA_BASE_DIRECTORY: + break; + case STATE_SCENE_INFO_BALSA_EXTRACT_TO_DIRECTORY: + break; + case STATE_SCENE_INFO_BASE_PATH_TYPE: break; case STATE_SCENE_INFO: m_sceneInfoSaxReader->startElement(namespaceURI, localName, qName, attributes); @@ -228,8 +249,32 @@ SceneFileSaxReader::endElement(const AString& namespaceURI, case STATE_SCENE_INFO_BALSA_STUDY_ID: m_sceneFile->setBalsaStudyID(m_elementText); break; - case STATE_SCENE_INFO_BASE_DIRECTORY: - m_sceneFile->setBaseDirectory(m_elementText); + case STATE_SCENE_INFO_BALSA_STUDY_TITLE: + m_sceneFile->setBalsaStudyTitle(m_elementText); + break; + case STATE_SCENE_INFO_BALSA_BASE_DIRECTORY: + m_sceneFile->setBalsaCustomBaseDirectory(m_elementText); + break; + case STATE_SCENE_INFO_BALSA_EXTRACT_TO_DIRECTORY: + m_sceneFile->setBalsaExtractToDirectoryName(m_elementText); + break; + case STATE_SCENE_INFO_BASE_PATH_TYPE: + { + SceneFileBasePathTypeEnum::Enum basePathType = SceneFileBasePathTypeEnum::AUTOMATIC; + bool validFlag = false; + basePathType = SceneFileBasePathTypeEnum::fromName(m_elementText, &validFlag); + if (validFlag) { + m_baseBathTypeWasFoundFlag = true; + } + else { + basePathType = SceneFileBasePathTypeEnum::AUTOMATIC; + + AString msg("Invalid BasePathType name: " + m_elementText); + XmlSaxParserException e(msg); + warning(e); + } + m_sceneFile->setBasePathType(basePathType); + } break; case STATE_SCENE_INFO: CaretAssert(m_sceneInfo); @@ -265,6 +310,47 @@ SceneFileSaxReader::endElement(const AString& namespaceURI, CaretLogSevere(msg); } } + + if (m_baseBathTypeWasFoundFlag) { + switch (m_sceneFile->getBasePathType()) { + case SceneFileBasePathTypeEnum::AUTOMATIC: + m_sceneFile->setBalsaCustomBaseDirectory(""); + break; + case SceneFileBasePathTypeEnum::CUSTOM: + { + AString basePath = m_sceneFile->getBalsaCustomBaseDirectory(); + if ( ! basePath.isEmpty()) { + FileInformation basePathInfo(basePath); + if (basePathInfo.isRelative()) { +// FileInformation tempBaseFile(basePath, +// "temp.txt"); + ScenePathName basePathName("basePathName", + ""); + basePathName.setValueToAbsolutePath(m_sceneFileName, + (basePath)); // + "/temp.txt")); + basePath = basePathName.stringValue(); + } + } + m_sceneFile->setBalsaCustomBaseDirectory(basePath); + } + break; + } + } + else { + /* + * Base path type was added by WB- in Oct 2017. + * If it is not found, the file was created prior to adding the + * base path type. If there is no base path, the go ahead and + * use AUTOMATIC mode. If there is a base path assume it is valid + * and use CUSTOM mode. + */ + if (m_sceneFile->getBalsaCustomBaseDirectory().isEmpty()) { + m_sceneFile->setBasePathType(SceneFileBasePathTypeEnum::AUTOMATIC); + } + else { + m_sceneFile->setBasePathType(SceneFileBasePathTypeEnum::CUSTOM); + } + } } break; case STATE_METADATA: diff --git a/src/Files/SceneFileSaxReader.h b/src/Files/SceneFileSaxReader.h index 60e22e4a800e806662b2282ad27075a18f4854dd..531e1f5f9ef111e7d27f209660037114cff3bc18 100644 --- a/src/Files/SceneFileSaxReader.h +++ b/src/Files/SceneFileSaxReader.h @@ -44,7 +44,8 @@ namespace caret { class SceneFileSaxReader : public CaretObject, public XmlSaxParserHandlerInterface { public: - SceneFileSaxReader(SceneFile* sceneFile); + SceneFileSaxReader(SceneFile* sceneFile, + const AString& sceneFileName); virtual ~SceneFileSaxReader(); @@ -83,8 +84,14 @@ namespace caret { STATE_SCENE_INFO_DIRECTORY, /// processing scene info Balsa Study ID tag STATE_SCENE_INFO_BALSA_STUDY_ID, + /// processing scene info Balsa Study Title tag + STATE_SCENE_INFO_BALSA_STUDY_TITLE, /// processing scene info Base Directory tag - STATE_SCENE_INFO_BASE_DIRECTORY, + STATE_SCENE_INFO_BALSA_BASE_DIRECTORY, + /// processing scene info Balsa Extract to Directory + STATE_SCENE_INFO_BALSA_EXTRACT_TO_DIRECTORY, + /// processing base path type + STATE_SCENE_INFO_BASE_PATH_TYPE, /// processing SceneInfo tag STATE_SCENE_INFO, /// processing Scene tag @@ -106,6 +113,9 @@ namespace caret { /// scene that is being read Scene* m_scene; + /// name of scene file being read + AString m_sceneFileName; + /// scene info that is being read SceneInfo* m_sceneInfo; @@ -126,6 +136,8 @@ namespace caret { /// map that stores scene info by index std::map m_sceneInfoMap; + + bool m_baseBathTypeWasFoundFlag = false; }; } // namespace diff --git a/src/Files/SpecFile.cxx b/src/Files/SpecFile.cxx index fe170457f6d80134c1804df2d34775262b944d8c..e5ec858634ea88b30ccd836257f221d319209e8f 100644 --- a/src/Files/SpecFile.cxx +++ b/src/Files/SpecFile.cxx @@ -896,7 +896,7 @@ SpecFile::readFile(const AString& filenameIn) checkFileReadability(filename); SpecFileSaxReader saxReader(this); - std::auto_ptr parser(XmlSaxParser::createXmlParser()); + std::unique_ptr parser(XmlSaxParser::createXmlParser()); try { parser->parseFile(filename, &saxReader); } @@ -943,7 +943,7 @@ void SpecFile::readFileFromString(const AString& string) { SpecFileSaxReader saxReader(this); - std::auto_ptr parser(XmlSaxParser::createXmlParser()); + std::unique_ptr parser(XmlSaxParser::createXmlParser()); try { parser->parseString(string, &saxReader); } diff --git a/src/Files/StudyMetaDataLink.cxx b/src/Files/StudyMetaDataLink.cxx index 39c12456131760f230d96a518edf258ced89324b..a310da9d81f471b24dbcabdd536770d6b8fa22a4 100644 --- a/src/Files/StudyMetaDataLink.cxx +++ b/src/Files/StudyMetaDataLink.cxx @@ -325,7 +325,7 @@ StudyMetaDataLink::setLinkFromCodedText(const AString& txt) } else { std::cout << "Unrecognized StudyMetaDataLink key: " - << key.toAscii().constData() << std::endl; + << key.toLatin1().constData() << std::endl; } } } diff --git a/src/Files/SurfacePlaneIntersectionToContour.cxx b/src/Files/SurfacePlaneIntersectionToContour.cxx new file mode 100644 index 0000000000000000000000000000000000000000..8f37912ba785ab1683f19a62f76839b335d76481 --- /dev/null +++ b/src/Files/SurfacePlaneIntersectionToContour.cxx @@ -0,0 +1,433 @@ + +/*LICENSE_START*/ +/* + * Copyright (C) 2017 Washington University School of Medicine + * + * 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 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ +/*LICENSE_END*/ + +#include + +#define __SURFACE_PLANE_INTERSECTION_TO_CONTOUR_DECLARE__ +#include "SurfacePlaneIntersectionToContour.h" +#undef __SURFACE_PLANE_INTERSECTION_TO_CONTOUR_DECLARE__ + +#include "CaretAssert.h" +#include "CaretException.h" +#include "CaretLogger.h" +#include "CaretOMP.h" +#include "ElapsedTimer.h" +#include "GraphicsPrimitiveV3fC4f.h" +#include "Plane.h" +#include "SurfaceFile.h" +#include "TopologyHelper.h" + +using namespace caret; + + + +/** + * \class caret::SurfacePlaneIntersectionToContour + * \brief Create a contour from intersecting the plane with a surface. + * \ingroup Files + * + * This algorithm is model upon these papers: + * + * Utilizing Topological Information to Increase Scan Vector Generation Efficiency + * Stephen J. Rock Michael J. Wozny + * Rensselaer Design Research Center Rensselaer Polytechnic Institute Troy, New York 12180 + * Appears in Solid Freeform Fabrication Symposium Proceedings, H. L. Marcus, et. al. (eds.), pp. 28-36, The University of Texas at Austin, Austin, TX, 1991. + * https://pdfs.semanticscholar.org/1129/b21303dcae34fed2a523f5991f8a1f9b3e32.pdf + * + * + * A Simple Arbitrary Solid Slicer + * Jin Yao + * B-Division, Lawrence Livermore National Laboratory Livermore, California 94551 + * https://e-reports-ext.llnl.gov/pdf/321754.pdf + * + */ + +/** + * Constructor. + * + * @param surfaceFile + * The surface file. + * @param plane + * Plane intersected with the surface. + * @param caretColor + * Solid coloring or, if value is CUSTOM, use the vertex coloring + * @param vertexColoringRGBA + * The per-vertex coloring if 'caretColor' is CUSTOM + * @param contourThicknessPercentOfViewportHeight + * Thickness for the contour as a percentage of viewport height. + */ +SurfacePlaneIntersectionToContour::SurfacePlaneIntersectionToContour(const SurfaceFile* surfaceFile, + const Plane& plane, + const CaretColorEnum::Enum caretColor, + const float* vertexColoringRGBA, + const float contourThicknessPercentOfViewportHeight) +: CaretObject(), +m_surfaceFile(surfaceFile), +m_plane(plane), +m_caretColor(caretColor), +m_vertexColoringRGBA(vertexColoringRGBA), +m_contourThicknessPercentOfViewportHeight(contourThicknessPercentOfViewportHeight) +{ + CaretAssert(m_surfaceFile); + + CaretColorEnum::toRGBAFloat(caretColor, + m_solidRGBA.data()); +} + +/** + * Destructor. + */ +SurfacePlaneIntersectionToContour::~SurfacePlaneIntersectionToContour() +{ +} + +/** + * Create the contour(s). + * + * @param graphicsPrimitivesOut + * On exit, contains primitives for drawing the contours. + * @param errorMessageOut + * Output containing the error message. + * @return + * True if successful, otherwise false. + */ +bool +SurfacePlaneIntersectionToContour::createContours(std::vector& graphicsPrimitivesOut, + AString& errorMessageOut) +{ + graphicsPrimitivesOut.clear(); + errorMessageOut.clear(); + + try { + if (m_surfaceFile->getNumberOfNodes() <= 2) { + throw CaretException("Surface has an invalid number of vertices."); + } + if ( ! m_plane.isValidPlane()) { + throw CaretException("Plane is invalid."); + } + + m_topologyHelper = m_surfaceFile->getTopologyHelper(true); + if (m_topologyHelper->getNumberOfNodes() <= 2) { + throw CaretException("Toplogy helper has an invalid number of vertices."); + } + + if (m_surfaceFile->getNumberOfNodes() != m_topologyHelper->getNumberOfNodes()) { + throw CaretException("Surface File and its Topology contain a different number of vertices."); + } + static bool timingFlag = false; + ElapsedTimer timer; + if (timingFlag) { + timer.start(); + } + + prepareVertices(); + const float verticesTime = (timingFlag ? timer.getElapsedTimeMilliseconds(): 0.0f); + + prepareEdges(); + const float edgesTime = (timingFlag ? timer.getElapsedTimeMilliseconds() - verticesTime : 0.0f); + + generateContours(graphicsPrimitivesOut); + const float contoursTime = (timingFlag ? timer.getElapsedTimeMilliseconds() - edgesTime : 0.0f); + + if (timingFlag) { + std::cout << "Total Time=" << timer.getElapsedTimeMilliseconds() << std::endl; + std::cout << " Vertices=" << verticesTime << std::endl; + std::cout << " Edges=" << edgesTime << std::endl; + std::cout << " Contours=" << contoursTime << std::endl; + } + } + catch (const CaretException& caretException) { + errorMessageOut = caretException.whatString(); + return false; + } + + return true; +} + +/** + * Prepare the vertices by computing their signed distance from the plane. + * If vertex is on of very, very close to the plane, move the vertex + * away from the play by a very small amount so that the plane + * always intersects edges and never at a vertex. + */ +void +SurfacePlaneIntersectionToContour::prepareVertices() +{ + const float epsilon = 0.0000001f; + + CaretAssert(m_surfaceFile); + + float planeNormalVector[3]; + m_plane.getNormalVector(planeNormalVector); + const float abovePlaneOffset[3] = { planeNormalVector[0] * epsilon, planeNormalVector[1] * epsilon, planeNormalVector[2] * epsilon }; + + const float* surfaceXYZ = m_surfaceFile->getCoordinateData(); + + const int32_t numberOfVertices = m_surfaceFile->getNumberOfNodes(); + + m_vertices.resize(numberOfVertices); + +#pragma omp CARET_PARFOR + for (int32_t i = 0; i < numberOfVertices; i++) { + const int32_t i3 = i * 3; + std::array xyz = {{ surfaceXYZ[i3], surfaceXYZ[i3 + 1], surfaceXYZ[i3 + 2] }}; + + const float signedDistanceToPlane = m_plane.signedDistanceToPlane(xyz.data()); + if ((signedDistanceToPlane < epsilon) + && (signedDistanceToPlane > -epsilon)) { + /* + * Point is on or nearly on the plane so move it away from the plane + */ + float projectedXYZ[3]; + m_plane.projectPointToPlane(xyz.data(), projectedXYZ); + + if (signedDistanceToPlane >= 0) { + xyz[0] = projectedXYZ[0] + abovePlaneOffset[0]; + xyz[1] = projectedXYZ[1] + abovePlaneOffset[1]; + xyz[2] = projectedXYZ[2] + abovePlaneOffset[2]; + } + else { + xyz[0] = projectedXYZ[0] - abovePlaneOffset[0]; + xyz[1] = projectedXYZ[1] - abovePlaneOffset[1]; + xyz[2] = projectedXYZ[2] - abovePlaneOffset[2]; + } + } + + m_vertices[i].reset(new Vertex(xyz, signedDistanceToPlane)); + } + + CaretAssert(static_cast(m_vertices.size()) == numberOfVertices); +} + +/** + * Find the edges that intersect the plane and save + * information about these intersecting edges. + */ +void +SurfacePlaneIntersectionToContour::prepareEdges() +{ + const std::vector& allEdgeInfo = m_topologyHelper->getEdgeInfo(); + + const int32_t numEdges = static_cast(allEdgeInfo.size()); + + m_topoHelperEdgeToIntersectingEdgeIndices.reserve(numEdges); + + for (int32_t i = 0; i < numEdges; i++) { + CaretAssertVectorIndex(allEdgeInfo, i); + const TopologyEdgeInfo& edgeInfo = allEdgeInfo[i]; + const int32_t indexOne = edgeInfo.node1; + const int32_t indexTwo = edgeInfo.node2; + CaretAssertVectorIndex(m_vertices, indexOne); + CaretAssertVectorIndex(m_vertices, indexTwo); + + int32_t intersectingEdgeIndex = -1; + + /* + * We are only interested in edges that have one vertex below the plane + * and one vertex above the plane so the edge MUST intersect the plane. + */ + if (m_vertices[indexOne]->m_abovePlaneFlag != m_vertices[indexTwo]->m_abovePlaneFlag) { + const int32_t abovePlaneVertexIndex = (m_vertices[indexOne]->m_abovePlaneFlag + ? indexOne + : indexTwo); + const int32_t belowPlaneVertexIndex = (m_vertices[indexOne]->m_abovePlaneFlag + ? indexTwo + : indexOne); + + std::array intersectionXYZ; + const bool validFlag = m_plane.lineSegmentIntersectPlane(m_vertices[belowPlaneVertexIndex]->m_xyz.data(), + m_vertices[abovePlaneVertexIndex]->m_xyz.data(), + intersectionXYZ.data()); + if (validFlag) { + /* + * Get the edge's triangles + */ + if (edgeInfo.numTiles <= 0) { + const AString msg = ("Edge index=" + + AString::number(i) + + " contains zero triangles. This should never happend!"); + throw CaretException(msg); + } + + int32_t triangleOne(edgeInfo.tiles[0].tile); + int32_t triangleTwo(-1); + if (edgeInfo.numTiles > 1) { + triangleTwo = edgeInfo.tiles[1].tile; + } + + std::unique_ptr edge(new IntersectionEdge(intersectionXYZ, + belowPlaneVertexIndex, + abovePlaneVertexIndex, + triangleOne, + triangleTwo)); + m_intersectingEdges.push_back(std::move(edge)); + + intersectingEdgeIndex = m_intersectingEdges.size() - 1; + + m_numberOfIntersectingEdges++; + } + } + + m_topoHelperEdgeToIntersectingEdgeIndices.push_back(intersectingEdgeIndex); + } + + CaretAssert(static_cast(m_topoHelperEdgeToIntersectingEdgeIndices.size()) == numEdges); +} + +/** + * Examine edges and find contours by moving from edge to neighboring triangle to edge. + * In many instances, there will be more than one contour due to the way the + * cortex intersects the plane. + * + * @param graphicsPrimitivesOut + * On exit, contains primitives for drawing the contours. + */ +void +SurfacePlaneIntersectionToContour::generateContours(std::vector& graphicsPrimitivesOut) +{ + if (m_debugFlag) { + std::cout << "*********************************************" << std::endl; + } + + for (auto& edge : m_intersectingEdges) { + if ( ! edge->m_processedFlag) { + GraphicsPrimitive* primitive = generateContourFromEdge(edge.get()); + if (primitive != NULL) { + graphicsPrimitivesOut.push_back(primitive); + } + } + } +} + +/** + * Starting with the given edge, follow the intersecting edges to create a contour. + * The contour will end when it returns to the original edge or encounters an + * edge that is used by only one triangle. An edge with one triangle indicates + * open topology such as a surface without a medial wall. + * + * @param startingEdge + * First edge in the contour. + */ +GraphicsPrimitive* +SurfacePlaneIntersectionToContour::generateContourFromEdge(IntersectionEdge* startingEdge) +{ + static std::array debugRgba = {{ 1.0f, 1.0f, 0.0f, 1.0f }}; + static std::array startDebugRgba = {{ 0.0, 1.0, 0.0, 1.0 }}; + static std::array endDebugRgba = {{ 1.0, 0.0, 0.0, 1.0 }}; + + const std::vector& allTileInfo = m_topologyHelper->getTileInfo(); + + std::unique_ptr primitive(GraphicsPrimitive::newPrimitiveV3fC4f(GraphicsPrimitive::PrimitiveType::POLYGONAL_LINE_LOOP_BEVEL_JOIN)); + primitive->setLineWidth(GraphicsPrimitive::LineWidthType::PERCENTAGE_VIEWPORT_HEIGHT, + m_contourThicknessPercentOfViewportHeight); + primitive->reserveForNumberOfVertices(m_numberOfIntersectingEdges + 5); + + IntersectionEdge* edge = startingEdge; + + if (m_debugFlag) { + std::cout << "Start: " << startingEdge->toString() << std::endl; + } + + while (edge != NULL) { + edge->m_processedFlag = true; + + IntersectionEdge* nextEdge = NULL; + + for (int32_t iTile = 0; iTile < 2; iTile++) { + CaretAssert(edge); + const int32_t tileIndex = (iTile == 0) ? edge->m_triangleOneIndex : edge->m_triangleTwoIndex; + if (tileIndex >= 0) { + CaretAssertVectorIndex(allTileInfo, tileIndex); + const TopologyTileInfo& tileInfo = allTileInfo[tileIndex]; + + for (int32_t iEdge = 0; iEdge < 3; iEdge++) { + const int32_t tileEdgeIndex = tileInfo.edges[iEdge].edge; + const int32_t intersectingEdgeIndex = m_topoHelperEdgeToIntersectingEdgeIndices[tileEdgeIndex]; + if (intersectingEdgeIndex >= 0) { + CaretAssertVectorIndex(m_intersectingEdges, intersectingEdgeIndex); + if ( ! m_intersectingEdges[intersectingEdgeIndex]->m_processedFlag) { + nextEdge = m_intersectingEdges[intersectingEdgeIndex].get(); + break; + } + } + } + + if (nextEdge != NULL) { + break; + } + } + } + + std::array colorRGBA = m_solidRGBA; + if (m_debugFlag) { + colorRGBA = debugRgba; + if (primitive->getNumberOfVertices() == 0) { + colorRGBA = startDebugRgba; + } + if (nextEdge == NULL) { + colorRGBA = endDebugRgba; + if (m_debugFlag) { + std::cout << " End: " << edge->toString() << std::endl; + } + } + } + else { + if (m_caretColor == CaretColorEnum::CUSTOM) { + CaretAssert(m_vertexColoringRGBA); + const int32_t belowI4 = edge->m_belowPlaneVertexIndex * 4; + const int32_t aboveI4 = edge->m_abovePlaneVertexIndex * 4; + + for (int32_t m = 0; m < 4; m++) { + colorRGBA[m] = ((m_vertexColoringRGBA[belowI4+m] + m_vertexColoringRGBA[aboveI4+m]) / 2.0f); + } + } + } + + primitive->addVertex(edge->m_intersectionXYZ.data(), colorRGBA.data()); + + if (startingEdge->hasMatchingTriangle(edge)) { + if (primitive->getNumberOfVertices() > 3) { + if (m_debugFlag) { + primitive->addVertex(startingEdge->m_intersectionXYZ.data(), + startDebugRgba.data()); + std::cout << " Closed contour" << std::endl; + } + else { + primitive->addVertex(startingEdge->m_intersectionXYZ.data(), + colorRGBA.data()); + } + } + } + + edge = nextEdge; + } + + if (m_debugFlag) { + std::cout << " Contour vertex count: " << primitive->getNumberOfVertices() << std::endl; + } + + if (primitive->isValid()) { + return primitive.release(); + } + + return NULL; +} + diff --git a/src/Files/SurfacePlaneIntersectionToContour.h b/src/Files/SurfacePlaneIntersectionToContour.h new file mode 100644 index 0000000000000000000000000000000000000000..22013717bf1cd990e81a8d2abc9c68e7e7010b2f --- /dev/null +++ b/src/Files/SurfacePlaneIntersectionToContour.h @@ -0,0 +1,219 @@ +#ifndef __SURFACE_PLANE_INTERSECTION_TO_CONTOUR_H__ +#define __SURFACE_PLANE_INTERSECTION_TO_CONTOUR_H__ + +/*LICENSE_START*/ +/* + * Copyright (C) 2017 Washington University School of Medicine + * + * 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 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ +/*LICENSE_END*/ + + +#include +#include + +#include "CaretColorEnum.h" +#include "CaretObject.h" +#include "CaretPointer.h" +#include "TopologyHelper.h" + +namespace caret { + + class GraphicsPrimitive; + class Plane; + class SurfaceFile; + + class SurfacePlaneIntersectionToContour : public CaretObject { + + public: + SurfacePlaneIntersectionToContour(const SurfaceFile* surfaceFile, + const Plane& plane, + const CaretColorEnum::Enum caretColor, + const float* vertexColoringRGBA, + const float contourThicknessMillimeters); + + virtual ~SurfacePlaneIntersectionToContour(); + + bool createContours(std::vector& graphicsPrimitivesOut, + AString& errorMessageOut); + + // ADD_NEW_METHODS_HERE + private: + /** + * Contains information about each surface vertex + * and its relationship to the plane + */ + class Vertex { + public: + /** + * Constructor. + * + * @param xyz + * The coordinate of the vertex. + * @param signedDistanceAbovePlane + * Distance of vertex above the plane. + * (negative if below the plane). + */ + Vertex(const std::array& xyz, + const float signedDistanceAbovePlane) + : m_xyz(xyz), + m_signedDistanceAbovePlane(signedDistanceAbovePlane), + m_abovePlaneFlag(signedDistanceAbovePlane > 0.0f) { } + + ~Vertex() { } + + /** Coordinate of vertex */ + const std::array m_xyz; + + /** Distance above plane (negative if below plane) */ + const float m_signedDistanceAbovePlane; + + /** True if above plane, else false */ + const bool m_abovePlaneFlag; + }; + + /* + * Contains information about an edge that intersects the plane + */ + class IntersectionEdge { + public: + /** + * Constructor. + * + * @param intersectionXYZ + * Location in the edge that intersects the plane + * @param belowPlaneVertexIndex + * Vertex in the edge that is below the plane. + * @param abovePlaneVertexIndex + * Vertex in the edge that is above the plane. + * @param triangleOneIndex + * Index of a triangle that shares edge. This triangle + * will always be valid. + * @param triangleTwoIndex + * Index of a triangle that shares edge. This triangle + * may be invalid if the edge is on the boundary + * of an open topology. + */ + IntersectionEdge(const std::array& intersectionXYZ, + int32_t belowPlaneVertexIndex, + int32_t abovePlaneVertexIndex, + int32_t triangleOneIndex, + int32_t triangleTwoIndex) + : m_intersectionXYZ(intersectionXYZ), + m_belowPlaneVertexIndex(belowPlaneVertexIndex), + m_abovePlaneVertexIndex(abovePlaneVertexIndex), + m_triangleOneIndex(triangleOneIndex), + m_triangleTwoIndex(triangleTwoIndex) { } + + /** + * True if this and the other edge are connected to the same triangle. + * @param otherEdge + * The other edge + * @return + * True if connected to same triangle, else false. + */ + bool hasMatchingTriangle(const IntersectionEdge* otherEdge) { + if ((m_triangleOneIndex == otherEdge->m_triangleOneIndex) + || (m_triangleOneIndex == otherEdge->m_triangleTwoIndex)) { + return true; + } + if (m_triangleTwoIndex >= 0) { + if ((m_triangleTwoIndex == otherEdge->m_triangleOneIndex) + || (m_triangleTwoIndex == otherEdge->m_triangleTwoIndex)) { + return true; + } + } + return false; + } + + AString toString() const { + AString msg("Intersection=(" + + AString::fromNumbers(m_intersectionXYZ.data(), 3, ",") + + ") below=" + + AString::number(m_belowPlaneVertexIndex) + + " aboveV=" + + AString::number(m_abovePlaneVertexIndex) + + " T1=" + + AString::number(m_triangleOneIndex) + + " T2=" + + AString::number(m_triangleTwoIndex)); + return msg; + } + /** coordinate of where edge intersects plane */ + std::array m_intersectionXYZ; + + /** vertex below the plane (index for surface vertex, topology helper vertex, and this class' m_vertices */ + int32_t m_belowPlaneVertexIndex; + + /** vertex above the plane */ + int32_t m_abovePlaneVertexIndex; + + /** first triangle used by edge (index for ToplogyHelper's TopologyTileInfo) */ + int32_t m_triangleOneIndex; + + /** second triangle used by edge (index for ToplogyHelper's TopologyTileInfo) */ + int32_t m_triangleTwoIndex; + + bool m_processedFlag = false; + }; + + SurfacePlaneIntersectionToContour(const SurfacePlaneIntersectionToContour&); + + SurfacePlaneIntersectionToContour& operator=(const SurfacePlaneIntersectionToContour&); + + void prepareVertices(); + + void prepareEdges(); + + void generateContours(std::vector& graphicsPrimitivesOut); + + GraphicsPrimitive* generateContourFromEdge(IntersectionEdge* edge); + + const SurfaceFile* m_surfaceFile; + + const Plane& m_plane; + + const CaretColorEnum::Enum m_caretColor; + + const float* m_vertexColoringRGBA; + + const float m_contourThicknessPercentOfViewportHeight; + + std::array m_solidRGBA; + + CaretPointer m_topologyHelper; + + std::vector> m_vertices; + + std::vector> m_intersectingEdges; + + /** index with a topo helper edge index to get its intersecting edge (-1) if edge does not intersect */ + std::vector m_topoHelperEdgeToIntersectingEdgeIndices; + + int32_t m_numberOfIntersectingEdges = 0; + + bool m_debugFlag = false; + + // ADD_NEW_MEMBERS_HERE + + }; + +#ifdef __SURFACE_PLANE_INTERSECTION_TO_CONTOUR_DECLARE__ + // +#endif // __SURFACE_PLANE_INTERSECTION_TO_CONTOUR_DECLARE__ + +} // namespace +#endif //__SURFACE_PLANE_INTERSECTION_TO_CONTOUR_H__ diff --git a/src/Files/SurfaceProjectedItem.cxx b/src/Files/SurfaceProjectedItem.cxx index 4416191d4c25984c9424ac5424326002ddc8d39b..1191d78d4b376352052778ee6ba3cc2f895c69c1 100644 --- a/src/Files/SurfaceProjectedItem.cxx +++ b/src/Files/SurfaceProjectedItem.cxx @@ -226,7 +226,7 @@ SurfaceProjectedItem::getProjectedPosition(const SurfaceFile& surfaceFile, if (valid == false) { if (this->vanEssenProjection->isValid()) { - valid = this->vanEssenProjection->unprojectToSurface(surfaceFile, + valid = this->vanEssenProjection->unprojectToSurface(surfaceFile, xyzOut, 0.0, isUnprojectedOntoSurface); @@ -253,6 +253,8 @@ SurfaceProjectedItem::getProjectedPosition(const SurfaceFile& surfaceFile, * * @param surfaceFile * Surface File for positioning. + * @param topologyHelper + * The topology helper. This value * @param xyzOut * Output containing the projected position. * @param distanceAboveSurface @@ -262,6 +264,7 @@ SurfaceProjectedItem::getProjectedPosition(const SurfaceFile& surfaceFile, */ bool SurfaceProjectedItem::getProjectedPositionAboveSurface(const SurfaceFile& surfaceFile, + const TopologyHelper* topologyHelper, float xyzOut[3], const float distanceAboveSurface) const { @@ -269,7 +272,8 @@ SurfaceProjectedItem::getProjectedPositionAboveSurface(const SurfaceFile& surfac if (valid == false) { if (this->barycentricProjection->isValid()) { - valid = this->barycentricProjection->unprojectToSurface(surfaceFile, + valid = this->barycentricProjection->unprojectToSurface(surfaceFile, + topologyHelper, xyzOut, distanceAboveSurface, true); @@ -278,7 +282,8 @@ SurfaceProjectedItem::getProjectedPositionAboveSurface(const SurfaceFile& surfac if (valid == false) { if (this->vanEssenProjection->isValid()) { - valid = this->vanEssenProjection->unprojectToSurface(surfaceFile, + valid = this->vanEssenProjection->unprojectToSurface(surfaceFile, + topologyHelper, xyzOut, distanceAboveSurface, true); diff --git a/src/Files/SurfaceProjectedItem.h b/src/Files/SurfaceProjectedItem.h index cc43fca90761aa4c9a96a0b8123e15eb1a7bd404..33d747e1a100c1661b3cc1d4d58624a9a1b1f94e 100644 --- a/src/Files/SurfaceProjectedItem.h +++ b/src/Files/SurfaceProjectedItem.h @@ -33,6 +33,7 @@ namespace caret { class SurfaceFile; class SurfaceProjectionBarycentric; class SurfaceProjectionVanEssen; + class TopologyHelper; class XmlWriter; class SurfaceProjectedItem : public CaretObjectTracksModification { @@ -67,6 +68,7 @@ namespace caret { const bool isUnprojectedOntoSurface) const; bool getProjectedPositionAboveSurface(const SurfaceFile& sf, + const TopologyHelper* th, float xyzOut[3], const float distanceAboveSurface) const; diff --git a/src/Files/SurfaceProjection.h b/src/Files/SurfaceProjection.h index 4ffcd0e51172fe726391bdbeb20a9acde8055c96..d57b0def21966bd6b65e6d89c2160210ef70aa01 100644 --- a/src/Files/SurfaceProjection.h +++ b/src/Files/SurfaceProjection.h @@ -28,6 +28,7 @@ namespace caret { class SurfaceFile; + class TopologyHelper; class XmlWriter; class SurfaceProjection : public CaretObjectTracksModification { @@ -80,6 +81,33 @@ namespace caret { const float offsetFromSurface, const bool unprojectWithOffsetFromSurface) const = 0; + /** + * Unproject to the surface using 'this' projection. + * + * @param surfaceFile + * Surface file used for unprojecting. + * @param topologyHelperIn + * Topology helper. If NULL, topology helper from surfaceFile + * will be used but frequent calls to get the topology helper + * may be slow. + * @param xyzOut + * Output containing coordinate created by unprojecting. + * @param offsetFromSurface + * If 'unprojectWithOffsetFromSurface' is true, unprojected + * position will be this distance above (negative=below) + * the surface. + * @param unprojectWithOffsetFromSurface + * If true, ouput coordinate will be offset 'offsetFromSurface' + * distance from the surface. + * @return + * True if unprojection is successful, else false. + */ + virtual bool unprojectToSurface(const SurfaceFile& surfaceFile, + const TopologyHelper* topologyHelperIn, + float xyzOut[3], + const float offsetFromSurface, + const bool unprojectWithOffsetFromSurface) const = 0; + int32_t getProjectionSurfaceNumberOfNodes() const; void setProjectionSurfaceNumberOfNodes(const int surfaceNumberOfNodes); diff --git a/src/Files/SurfaceProjectionBarycentric.cxx b/src/Files/SurfaceProjectionBarycentric.cxx index 35b923164023a8183c0b223aac02126f72602aa2..b8d2b14186ba9134a148f205728ef7448f34951c 100644 --- a/src/Files/SurfaceProjectionBarycentric.cxx +++ b/src/Files/SurfaceProjectionBarycentric.cxx @@ -222,6 +222,132 @@ SurfaceProjectionBarycentric::setTriangleAreas(const float triangleAreas[3]) this->setModified(); } +/** + * Unproject to the surface using 'this' projection. + * + * @param surfaceFile + * Surface file used for unprojecting. + * @param topologyHelperIn + * Topology helper. If NULL, topology helper from surfaceFile + * will be used but frequent calls to get the topology helper + * may be slow. + * @param xyzOut + * Output containing coordinate created by unprojecting. + * @param offsetFromSurface + * If 'unprojectWithOffsetFromSurface' is true, unprojected + * position will be this distance above (negative=below) + * the surface. + * @param unprojectWithOffsetFromSurface + * If true, ouput coordinate will be offset 'offsetFromSurface' + * distance from the surface. + * @return + * True if unprojection was successful. + */ +bool +SurfaceProjectionBarycentric::unprojectToSurface(const SurfaceFile& surfaceFile, + const TopologyHelper* topologyHelperIn, + float xyzOut[3], + const float offsetFromSurface, + const bool unprojectWithOffsetFromSurface) const +{ + /* + * Make sure projection surface number of nodes matches surface. + */ + if (this->projectionSurfaceNumberOfNodes > 0) { + if (surfaceFile.getNumberOfNodes() != this->projectionSurfaceNumberOfNodes) { + return false; + } + } + + const int32_t n1 = this->triangleNodes[0]; + const int32_t n2 = this->triangleNodes[1]; + const int32_t n3 = this->triangleNodes[2]; + + CaretAssert(n1 < surfaceFile.getNumberOfNodes()); + CaretAssert(n2 < surfaceFile.getNumberOfNodes()); + CaretAssert(n3 < surfaceFile.getNumberOfNodes()); + + /* + * All nodes MUST have neighbors (connected) + */ + const TopologyHelper* topologyHelper = ((topologyHelperIn != NULL) + ? topologyHelperIn + : surfaceFile.getTopologyHelper().getPointer()); + if ((topologyHelper->getNodeHasNeighbors(n1) == false) + || (topologyHelper->getNodeHasNeighbors(n2) == false) + || (topologyHelper->getNodeHasNeighbors(n3) == false)) { + return false; + } + + const float* c1 = surfaceFile.getCoordinate(n1); + const float* c2 = surfaceFile.getCoordinate(n2); + const float* c3 = surfaceFile.getCoordinate(n3); + + float barycentricXYZ[3]; + float barycentricNormal[3]; + + /* + * If all the nodes are the same (object projects to a single node, not triangle) + */ + if ((n1 == n2) && + (n2 == n3)) { + /* + * Use node's normal vector and position + */ + barycentricXYZ[0] = c1[0]; + barycentricXYZ[1] = c1[1]; + barycentricXYZ[2] = c1[2]; + const float* nodeNormal = surfaceFile.getNormalVector(n1); + barycentricNormal[0] = nodeNormal[0]; + barycentricNormal[1] = nodeNormal[1]; + barycentricNormal[2] = nodeNormal[2]; + } + else { + /* + * Compute position using barycentric coordinates + */ + float t1[3]; + float t2[3]; + float t3[3]; + for (int i = 0; i < 3; i++) { + t1[i] = triangleAreas[0] * c1[i]; + t2[i] = triangleAreas[1] * c2[i]; + t3[i] = triangleAreas[2] * c3[i]; + } + float area = (triangleAreas[0] + + triangleAreas[1] + + triangleAreas[2]); + if (area != 0) { + for (int i = 0; i < 3; i++) { + barycentricXYZ[i] = (t1[i] + t2[i] + t3[i]) / area; + } + } + else { + return false; + } + + if (MathFunctions::normalVector(c1, c2, c3, barycentricNormal) == false) { + return false; + } + } + + /* + * Set output coordinate, possibly offsetting from surface. + */ + for (int j = 0; j < 3; j++) { + if (unprojectWithOffsetFromSurface) { + xyzOut[j] = (barycentricXYZ[j] + + (barycentricNormal[j] * offsetFromSurface)); + } + else { + xyzOut[j] = (barycentricXYZ[j] + + (barycentricNormal[j] * signedDistanceAboveSurface)); + } + } + + return true; +} + /** * Unproject to the surface using 'this' projection. * diff --git a/src/Files/SurfaceProjectionBarycentric.h b/src/Files/SurfaceProjectionBarycentric.h index abf20ea3cd5b29f1c85ae4ceac76ca7c708c8be0..6b99b4c8b7235a70445097adfcf4c1cd1c258ad5 100644 --- a/src/Files/SurfaceProjectionBarycentric.h +++ b/src/Files/SurfaceProjectionBarycentric.h @@ -28,6 +28,7 @@ class QXmlStreamReader; namespace caret { + class TopologyHelper; class SurfaceProjectionBarycentric : public SurfaceProjection { public: @@ -64,6 +65,12 @@ namespace caret { const float offsetFromSurface, const bool unprojectWithOffsetFromSurface) const; + bool unprojectToSurface(const SurfaceFile& surfaceFile, + const TopologyHelper* topologyHelper, + float xyzOut[3], + const float offsetFromSurface, + const bool unprojectWithOffsetFromSurface) const; + void reset(); bool isValid() const; diff --git a/src/Files/SurfaceProjectionVanEssen.cxx b/src/Files/SurfaceProjectionVanEssen.cxx index ef35a0c220069c0227b745ac60d209bb87832406..4c6fcfe75eeca58b01abdc358bfe69db7d1b6e8d 100644 --- a/src/Files/SurfaceProjectionVanEssen.cxx +++ b/src/Files/SurfaceProjectionVanEssen.cxx @@ -160,11 +160,43 @@ SurfaceProjectionVanEssen::copyHelperSurfaceProjectionVanEssen(const SurfaceProj this->projectionValid = obj.projectionValid; } +/** + * Unproject to the surface using 'this' projection. + * + * @param surfaceFile + * Surface file used for unprojecting. + * @param xyzOut + * Output containing coordinate created by unprojecting. + * @param offsetFromSurface + * If 'unprojectWithOffsetFromSurface' is true, unprojected + * position will be this distance above (negative=below) + * the surface. + * @param unprojectWithOffsetFromSurface + * If true, ouput coordinate will be offset 'offsetFromSurface' + * distance from the surface. + */ +bool +SurfaceProjectionVanEssen::unprojectToSurface(const SurfaceFile& surfaceFile, + float xyzOut[3], + const float offsetFromSurface, + const bool unprojectWithOffsetFromSurface) const +{ + return unprojectToSurface(surfaceFile, + NULL, + xyzOut, + offsetFromSurface, + unprojectWithOffsetFromSurface); +} + /** * Unproject to the surface using 'this' projection. * * @param surfaceFile * Surface file used for unprojecting. + * @param topologyHelperIn + * Topology helper. If NULL, topology helper from surfaceFile + * will be used but frequent calls to get the topology helper + * may be slow. * @param xyzOut * Output containing coordinate created by unprojecting. * @param offsetFromSurface @@ -177,6 +209,7 @@ SurfaceProjectionVanEssen::copyHelperSurfaceProjectionVanEssen(const SurfaceProj */ bool SurfaceProjectionVanEssen::unprojectToSurface(const SurfaceFile& surfaceFile, + const TopologyHelper* topologyHelperIn, float xyzOut[3], const float offsetFromSurface, const bool unprojectWithOffsetFromSurface) const @@ -202,8 +235,10 @@ SurfaceProjectionVanEssen::unprojectToSurface(const SurfaceFile& surfaceFile, /* * All nodes MUST have neighbors (connected) */ - const TopologyHelper* topologyHelper = surfaceFile.getTopologyHelper().getPointer(); - if ((topologyHelper->getNodeHasNeighbors(n1) == false) + const TopologyHelper* topologyHelper = ((topologyHelperIn != NULL) + ? topologyHelperIn + : surfaceFile.getTopologyHelper().getPointer()); + if ((topologyHelper->getNodeHasNeighbors(n1) == false) || (topologyHelper->getNodeHasNeighbors(n2) == false)) { return false; } @@ -236,9 +271,9 @@ SurfaceProjectionVanEssen::unprojectToSurface(const SurfaceFile& surfaceFile, const float* normalJ = surfaceFile.getNormalVector(pjs); float avgNormal[3] = { - ((normalI[0] + normalJ[0]) / 2.0), - ((normalI[1] + normalJ[1]) / 2.0), - ((normalI[2] + normalJ[2]) / 2.0), + ((normalI[0] + normalJ[0]) / 2.0f), + ((normalI[1] + normalJ[1]) / 2.0f), + ((normalI[2] + normalJ[2]) / 2.0f), }; MathFunctions::normalizeVector(avgNormal); diff --git a/src/Files/SurfaceProjectionVanEssen.h b/src/Files/SurfaceProjectionVanEssen.h index e8052a6620c91caa694d4778f587fd7633391c2f..ad4499503f4333b25127eb6a9a5454231671666a 100644 --- a/src/Files/SurfaceProjectionVanEssen.h +++ b/src/Files/SurfaceProjectionVanEssen.h @@ -26,6 +26,8 @@ namespace caret { + class TopologyHelper; + class SurfaceProjectionVanEssen : public SurfaceProjection { public: @@ -41,6 +43,12 @@ namespace caret { bool operator!=(const SurfaceProjectionVanEssen& rhs) { return !(*this == rhs); } + bool unprojectToSurface(const SurfaceFile& surfaceFile, + const TopologyHelper* topologyHelper, + float xyzOut[3], + const float offsetFromSurface, + const bool unprojectWithOffsetFromSurface) const; + bool unprojectToSurface(const SurfaceFile& surfaceFile, float xyzOut[3], const float offsetFromSurface, diff --git a/src/Files/VolumeFile.cxx b/src/Files/VolumeFile.cxx index 0ff7e6ba331bd859236d4ba8eb844d806974a81c..69a9a6657f67be30a558b7c1f60e0a50208732c8 100644 --- a/src/Files/VolumeFile.cxx +++ b/src/Files/VolumeFile.cxx @@ -32,10 +32,10 @@ #include "DataFileContentInformation.h" #include "ElapsedTimer.h" #include "EventManager.h" -#include "EventPaletteGetByName.h" #include "GroupAndNameHierarchyModel.h" #include "FastStatistics.h" #include "Histogram.h" +#include "MapFileDataSelector.h" #include "MultiDimIterator.h" #include "NiftiIO.h" #include "Palette.h" @@ -223,6 +223,13 @@ void VolumeFile::readFile(const AString& filename) NiftiIO myIO;//begin nifti specific code - should this go somewhere else? myIO.openRead(fileToRead); const NiftiHeader& inHeader = myIO.getHeader(); + for (int i = 0; i < (int)inHeader.m_extensions.size(); ++i) + {//check for actually being cifti + if (inHeader.m_extensions[i]->m_ecode == NIFTI_ECODE_CIFTI) + { + throw DataFileException(filename, "Cifti files cannot be used as volume files"); + } + } int numComponents = myIO.getNumComponents(); vector myDims = myIO.getDimensions(); int fullDims = 3;//deal with nifti with less than 3 dimensions @@ -233,8 +240,20 @@ void VolumeFile::readFile(const AString& filename) extraDims = vector(myDims.begin() + 3, myDims.end()); } while (myDims.size() < 3) myDims.push_back(1);//pretend we have 3 dimensions in header, always, things that use getOriginalDimensions assume this (because "VolumeFile") + if (myDims.size() > 3) + {//check for cifti-like dimensions here, so that we have the filename to report in the error + int64_t numFrames = myDims[3]; + for (int i = 4; i < (int)myDims.size(); ++i) + { + numFrames *= myDims[i]; + } + if (myDims[0] == 1 && myDims[1] == 1 && myDims[2] == 1 && numFrames > 10000) + { + throw DataFileException(filename, "volume FOV is 1x1x1 voxel, with over 10,000 frames, which suggests a broken cifti file (no header extension)"); + } + }//this check is also done in reinitialize(), but we don't want to call getSForm before this check when reading a file reinitialize(myDims, inHeader.getSForm(), numComponents); - setFileName(filename); // must be donw after reinitialize() since it calls clear() which clears the name of the file + setFileName(filename); // must be done after reinitialize() since it calls clear() which clears the name of the file int64_t frameSize = myDims[0] * myDims[1] * myDims[2]; if (numComponents != 1) { @@ -265,6 +284,7 @@ void VolumeFile::readFile(const AString& filename) + " seconds."); m_header.grabNew(new NiftiHeader(inHeader));//end nifti-specific code parseExtensions(); + updateAfterFileDataChanges(); clearModified(); } @@ -278,6 +298,10 @@ void VolumeFile::readFile(const AString& filename) getGroupAndNameHierarchyModel(); } + if ( ! hasGoodSpatialInformation()) { + addFileReadWarning("No spatial information in file (in NIFTI header, both qform and sform are invalid)."); + } + CaretLogFine("Total Time to read and process volume is " + AString::number(timer.getElapsedTimeSeconds(), 'f', 3) + " seconds."); @@ -330,10 +354,21 @@ VolumeFile::writeFile(const AString& filename) { myIO.writeData(getFrame(getBrickIndexFromNonSpatialIndexes(*myiter)), 3, *myiter);//NOTE: does not deal with multi-component volumes } + myIO.close();//call close explicitly to get a throw rather than a severe log when there is a problem m_header.grabNew(new NiftiHeader(outHeader));//update header to last written version, end nifti-specific code m_volumeFileEditorDelegate->clear(); m_volumeFileEditorDelegate->updateIfVolumeFileChangedNumberOfMaps(); + clearModified(); +} + +bool VolumeFile::hasGoodSpatialInformation() const +{ + if (m_header != NULL) + { + return m_header->hasGoodSpatialInformation(); + } + return true; } float VolumeFile::interpolateValue(const float* coordIn, InterpType interp, bool* validOut, const int64_t brickIndex, const int64_t component) const @@ -645,7 +680,7 @@ void VolumeFile::validateMembers() } } - setPaletteNormalizationMode(PaletteNormalizationModeEnum::NORMALIZATION_SELECTED_MAP_DATA); + //setPaletteNormalizationMode(PaletteNormalizationModeEnum::NORMALIZATION_SELECTED_MAP_DATA); m_singleSliceFlag = false; if ((dimensions[0] == 1) @@ -697,6 +732,8 @@ VolumeFile::clearModified() CaretMappableDataFile::clearModified(); clearModifiedVolumeBase(); + getFileMetaData()->clearModified(); + const int32_t numMaps = getNumberOfMaps(); if (isMappedWithPalette()) { @@ -710,6 +747,10 @@ VolumeFile::clearModified() getMapLabelTable(i)->clearModified(); } } + + for (int32_t i = 0; i < numMaps; i++) { + getMapMetaData(i)->clearModified(); + } } /** @@ -727,6 +768,10 @@ VolumeFile::isModifiedExcludingPaletteColorMapping() const return true; } + if (getFileMetaData()->isModified()) { + return true; + } + const int32_t numMaps = getNumberOfMaps(); if (isMappedWithLabelTable()) { for (int32_t i = 0; i < numMaps; i++) { @@ -735,6 +780,11 @@ VolumeFile::isModifiedExcludingPaletteColorMapping() const } } } + for (int32_t i = 0; i < numMaps; i++) { + if (getMapMetaData(i)->isModified()) { + return true; + } + } return false; } @@ -797,22 +847,14 @@ VolumeFile::setMapName(const int32_t mapIndex, const GiftiMetaData* VolumeFile::getMapMetaData(const int32_t mapIndex) const { - CaretAssertVectorIndex(m_brickAttributes, mapIndex); - if (m_brickAttributes[mapIndex].m_metadata == NULL) - { - m_brickAttributes[mapIndex].m_metadata.grabNew(new GiftiMetaData()); - } - return m_brickAttributes[mapIndex].m_metadata; + CaretAssertVectorIndex(m_caretVolExt.m_attributes, mapIndex); + return &m_caretVolExt.m_attributes[mapIndex]->m_metadata; } GiftiMetaData* VolumeFile::getMapMetaData(const int32_t mapIndex) { - CaretAssertVectorIndex(m_brickAttributes, mapIndex); - if (m_brickAttributes[mapIndex].m_metadata == NULL) - { - m_brickAttributes[mapIndex].m_metadata.grabNew(new GiftiMetaData()); - } - return m_brickAttributes[mapIndex].m_metadata; + CaretAssertVectorIndex(m_caretVolExt.m_attributes, mapIndex); + return &m_caretVolExt.m_attributes[mapIndex]->m_metadata; } void VolumeFile::checkStatisticsValid() @@ -847,9 +889,31 @@ const Histogram* VolumeFile::getMapHistogram(const int32_t mapIndex) CaretAssertVectorIndex(m_brickAttributes, mapIndex); checkStatisticsValid(); const int64_t* dimensions = getDimensionsPtr(); + + bool updateHistogramFlag = false; + int32_t numberOfBuckets = 0; + switch (getPaletteNormalizationMode()) { + case PaletteNormalizationModeEnum::NORMALIZATION_ALL_MAP_DATA: + numberOfBuckets = getFileHistogramNumberOfBuckets(); + break; + case PaletteNormalizationModeEnum::NORMALIZATION_SELECTED_MAP_DATA: + numberOfBuckets = getMapPaletteColorMapping(mapIndex)->getHistogramNumberOfBuckets(); + break; + } if (m_brickAttributes[mapIndex].m_histogram == NULL) { - m_brickAttributes[mapIndex].m_histogram.grabNew(new Histogram(100, getFrame(mapIndex), dimensions[0] * dimensions[1] * dimensions[2])); + m_brickAttributes[mapIndex].m_histogram.grabNew(new Histogram(numberOfBuckets)); + updateHistogramFlag = true; + } + else if (numberOfBuckets != m_brickAttributes[mapIndex].m_histogramNumberOfBuckets) + { + updateHistogramFlag = true; + } + + if (updateHistogramFlag) + { + m_brickAttributes[mapIndex].m_histogram->update(numberOfBuckets, getFrame(mapIndex), dimensions[0] * dimensions[1] * dimensions[2]); + m_brickAttributes[mapIndex].m_histogramNumberOfBuckets = numberOfBuckets; } return m_brickAttributes[mapIndex].m_histogram; } @@ -884,12 +948,22 @@ VolumeFile::getMapHistogram(const int32_t mapIndex, bool updateHistogramFlag = false; + int32_t numberOfBuckets = 0; + switch (getPaletteNormalizationMode()) { + case PaletteNormalizationModeEnum::NORMALIZATION_ALL_MAP_DATA: + numberOfBuckets = getFileHistogramNumberOfBuckets(); + break; + case PaletteNormalizationModeEnum::NORMALIZATION_SELECTED_MAP_DATA: + numberOfBuckets = getMapPaletteColorMapping(mapIndex)->getHistogramNumberOfBuckets(); + break; + } if (m_brickAttributes[mapIndex].m_histogramLimitedValues == NULL) { m_brickAttributes[mapIndex].m_histogramLimitedValues.grabNew(new Histogram(100)); updateHistogramFlag = true; } - else if ((mostPositiveValueInclusive != m_brickAttributes[mapIndex].m_histogramLimitedValuesMostPositiveValueInclusive) + else if ((numberOfBuckets != m_brickAttributes[mapIndex].m_histogramLimitedValuesNumberOfBuckets) + || (mostPositiveValueInclusive != m_brickAttributes[mapIndex].m_histogramLimitedValuesMostPositiveValueInclusive) || (leastPositiveValueInclusive != m_brickAttributes[mapIndex].m_histogramLimitedValuesLeastPositiveValueInclusive) || (leastNegativeValueInclusive != m_brickAttributes[mapIndex].m_histogramLimitedValuesLeastNegativeValueInclusive) || (mostNegativeValueInclusive != m_brickAttributes[mapIndex].m_histogramLimitedValuesMostNegativeValueInclusive) @@ -898,13 +972,15 @@ VolumeFile::getMapHistogram(const int32_t mapIndex, } if (updateHistogramFlag) { - m_brickAttributes[mapIndex].m_histogramLimitedValues->update(getFrame(mapIndex), + m_brickAttributes[mapIndex].m_histogramLimitedValues->update(numberOfBuckets, + getFrame(mapIndex), dimensions[0] * dimensions[1] * dimensions[2], mostPositiveValueInclusive, leastPositiveValueInclusive, leastNegativeValueInclusive, mostNegativeValueInclusive, includeZeroValues); + m_brickAttributes[mapIndex].m_histogramLimitedValuesNumberOfBuckets = numberOfBuckets; m_brickAttributes[mapIndex].m_histogramLimitedValuesMostPositiveValueInclusive = mostPositiveValueInclusive; m_brickAttributes[mapIndex].m_histogramLimitedValuesLeastPositiveValueInclusive = leastPositiveValueInclusive; m_brickAttributes[mapIndex].m_histogramLimitedValuesLeastNegativeValueInclusive = leastNegativeValueInclusive; @@ -972,15 +1048,32 @@ VolumeFile::getFileFastStatistics() const Histogram* VolumeFile::getFileHistogram() { - if (m_fileHistogram == NULL) { + const int32_t numBuckets = getFileHistogramNumberOfBuckets(); + bool updateHistogramFlag = false; + if (m_fileHistogram != NULL) { + if (numBuckets != m_fileHistogramNumberOfBuckets) { + updateHistogramFlag = true; + } + } + else { + updateHistogramFlag = true; + + } + + if (updateHistogramFlag) { std::vector fileData; getFileData(fileData); if ( ! fileData.empty()) { - m_fileHistogram.grabNew(new Histogram()); - m_fileHistogram->update(&fileData[0], + if (m_fileHistogram == NULL) { + m_fileHistogram.grabNew(new Histogram(numBuckets)); + } + m_fileHistogram->update(numBuckets, + &fileData[0], fileData.size()); + m_fileHistogramNumberOfBuckets = numBuckets; } } + return m_fileHistogram; } @@ -1010,9 +1103,11 @@ VolumeFile::getFileHistogram(const float mostPositiveValueInclusive, const float mostNegativeValueInclusive, const bool includeZeroValues) { + const int32_t numberOfBuckets = getFileHistogramNumberOfBuckets(); bool updateHistogramFlag = false; if (m_fileHistorgramLimitedValues != NULL) { - if ((mostPositiveValueInclusive != m_fileHistogramLimitedValuesMostPositiveValueInclusive) + if ((numberOfBuckets != m_fileHistogramLimitedValuesNumberOfBuckets) + || (mostPositiveValueInclusive != m_fileHistogramLimitedValuesMostPositiveValueInclusive) || (leastPositiveValueInclusive != m_fileHistogramLimitedValuesLeastPositiveValueInclusive) || (leastNegativeValueInclusive != m_fileHistogramLimitedValuesLeastNegativeValueInclusive) || (mostNegativeValueInclusive != m_fileHistogramLimitedValuesMostNegativeValueInclusive) @@ -1031,7 +1126,8 @@ VolumeFile::getFileHistogram(const float mostPositiveValueInclusive, if (m_fileHistorgramLimitedValues == NULL) { m_fileHistorgramLimitedValues.grabNew(new Histogram()); } - m_fileHistorgramLimitedValues->update(&fileData[0], + m_fileHistorgramLimitedValues->update(numberOfBuckets, + &fileData[0], fileData.size(), mostPositiveValueInclusive, leastPositiveValueInclusive, @@ -1039,6 +1135,7 @@ VolumeFile::getFileHistogram(const float mostPositiveValueInclusive, mostNegativeValueInclusive, includeZeroValues); + m_fileHistogramLimitedValuesNumberOfBuckets = numberOfBuckets; m_fileHistogramLimitedValuesMostPositiveValueInclusive = mostPositiveValueInclusive; m_fileHistogramLimitedValuesLeastPositiveValueInclusive = leastPositiveValueInclusive; m_fileHistogramLimitedValuesLeastNegativeValueInclusive = leastNegativeValueInclusive; @@ -1050,6 +1147,25 @@ VolumeFile::getFileHistogram(const float mostPositiveValueInclusive, return m_fileHistorgramLimitedValues; } +/** + * @return Pointer to file's metadata. + */ +GiftiMetaData* +VolumeFile::getFileMetaData() +{ + return &m_caretVolExt.m_metadata; +} + +/** + * @return Pointer to file's metadata. + */ +const GiftiMetaData* +VolumeFile::getFileMetaData() const +{ + return &m_caretVolExt.m_metadata; +} + + /** * Get all data for a volume file. If the file is very * large this method may take a large amount of time! @@ -1131,7 +1247,7 @@ VolumeFile::isMappedWithPalette() const * vector is assumed to be the default mode. */ void -VolumeFile::getPaletteNormalizationModesSupported(std::vector& modesSupportedOut) +VolumeFile::getPaletteNormalizationModesSupported(std::vector& modesSupportedOut) const { modesSupportedOut.clear(); @@ -1262,12 +1378,8 @@ VolumeFile::isMappedWithRGBA() const AString VolumeFile::getMapUniqueID(const int32_t mapIndex) const { - CaretAssertVectorIndex(m_brickAttributes, mapIndex); - if (m_brickAttributes[mapIndex].m_metadata == NULL) - { - m_brickAttributes[mapIndex].m_metadata.grabNew(new GiftiMetaData()); - } - return m_brickAttributes[mapIndex].m_metadata->getUniqueID(); + CaretAssertVectorIndex(m_caretVolExt.m_attributes, mapIndex); + return m_caretVolExt.m_attributes[mapIndex]->m_metadata.getUniqueID(); } /** @@ -1299,12 +1411,9 @@ VolumeFile::getVoxelSpaceBoundingBox(BoundingBox& boundingBoxOut) const * * @param mapIndex * Index of map. - * @param paletteFile - * File containing the palettes. */ void -VolumeFile::updateScalarColoringForMap(const int32_t mapIndex, - const PaletteFile* paletteFile) +VolumeFile::updateScalarColoringForMap(const int32_t mapIndex) { if (s_voxelColoringEnabled == false) { return; @@ -1313,41 +1422,9 @@ VolumeFile::updateScalarColoringForMap(const int32_t mapIndex, CaretAssertVectorIndex(m_caretVolExt.m_attributes, mapIndex); CaretAssert(m_voxelColorizer); - const bool usesPalette = isMappedWithPalette(); - const PaletteColorMapping* pcm = (usesPalette - ? getMapPaletteColorMapping(mapIndex) - : NULL); - const AString paletteName = (usesPalette - ? pcm->getSelectedPaletteName() - : ""); - Palette* palette = NULL; - - if (usesPalette) { - if (paletteFile != NULL) { - palette = paletteFile->getPaletteByName(paletteName); - } - - if (palette == NULL) { - EventPaletteGetByName getPaletteEvent(paletteName); - EventManager::get()->sendEvent(getPaletteEvent.getPointer()); - palette = getPaletteEvent.getPalette(); - } - } - - if (usesPalette - && (palette == NULL)) { - CaretLogSevere("No palette named \"" - + paletteName - + "\" found for coloring map index=" - + AString::number(mapIndex) - + " in " - + getFileNameNoPath()); - } + m_voxelColorizer->assignVoxelColorsForMap(mapIndex); - m_voxelColorizer->assignVoxelColorsForMap(mapIndex, - palette, - this, - mapIndex); + invalidateHistogramChartColoring(); } /** @@ -1355,8 +1432,6 @@ VolumeFile::updateScalarColoringForMap(const int32_t mapIndex, * Does nothing if coloring is not enabled and output colors are undefined * in this case. * - * @param paletteFile - * The palette file. * @param mapIndex * Index of the map. * @param slicePlane @@ -1373,13 +1448,12 @@ VolumeFile::updateScalarColoringForMap(const int32_t mapIndex, * Number of voxels with alpha greater than zero */ int64_t -VolumeFile::getVoxelColorsForSliceInMap(const PaletteFile* /*paletteFile*/, - const int32_t mapIndex, - const VolumeSliceViewPlaneEnum::Enum slicePlane, - const int64_t sliceIndex, +VolumeFile::getVoxelColorsForSliceInMap(const int32_t mapIndex, + const VolumeSliceViewPlaneEnum::Enum slicePlane, + const int64_t sliceIndex, const DisplayGroupEnum::Enum displayGroup, const int32_t tabIndex, - uint8_t* rgbaOut) const + uint8_t* rgbaOut) const { if (s_voxelColoringEnabled == false) { return 0; @@ -1450,8 +1524,6 @@ VolumeFile::getVoxelColorsForSliceInMap(const int32_t mapIndex, /** * Get the voxel colors for a sub slice in the map. * - * @param paletteFile - * The palette file. * @param mapIndex * Index of the map. * @param slicePlane @@ -1475,8 +1547,7 @@ VolumeFile::getVoxelColorsForSliceInMap(const int32_t mapIndex, * Number of voxels with alpha greater than zero */ int64_t -VolumeFile::getVoxelColorsForSubSliceInMap(const PaletteFile* /*paletteFile*/, - const int32_t mapIndex, +VolumeFile::getVoxelColorsForSubSliceInMap(const int32_t mapIndex, const VolumeSliceViewPlaneEnum::Enum slicePlane, const int64_t sliceIndex, const int64_t firstCornerVoxelIndex[3], @@ -1580,8 +1651,6 @@ VolumeFile::getVoxelValuesForSliceInMap(const int32_t mapIndex, * Does nothing if coloring is not enabled and output colors are undefined * in this case. * - * @param paletteFile - * The palette file. * @param i * Parasaggital index * @param j @@ -1598,14 +1667,13 @@ VolumeFile::getVoxelValuesForSliceInMap(const int32_t mapIndex, * Contains voxel coloring on exit. */ void -VolumeFile::getVoxelColorInMap(const PaletteFile* /*paletteFile*/, - const int64_t i, - const int64_t j, - const int64_t k, - const int64_t mapIndex, +VolumeFile::getVoxelColorInMap(const int64_t i, + const int64_t j, + const int64_t k, + const int64_t mapIndex, const DisplayGroupEnum::Enum displayGroup, const int32_t tabIndex, - uint8_t rgbaOut[4]) const + uint8_t rgbaOut[4]) const { if (s_voxelColoringEnabled == false) { return; @@ -2030,7 +2098,7 @@ VolumeFile::setLineSeriesChartingEnabled(const int32_t tabIndex, * Chart types supported by this file. */ void -VolumeFile::getSupportedLineSeriesChartDataTypes(std::vector& chartDataTypesOut) const +VolumeFile::getSupportedLineSeriesChartDataTypes(std::vector& chartDataTypesOut) const { helpGetSupportedLineSeriesChartDataTypes(chartDataTypesOut); } @@ -2122,4 +2190,87 @@ VolumeFile::loadLineSeriesChartDataForVoxelAtCoordinate(const float xyz[3]) return chartData; } +/** + * Get data from the file as requested in the given map file data selector. + * + * @param mapFileDataSelector + * Specifies selection of data. + * @param dataOut + * Output with data. Will be empty if data does not support the map file data selector. + */ +void +VolumeFile::getDataForSelector(const MapFileDataSelector& mapFileDataSelector, + std::vector& dataOut) const +{ + dataOut.clear(); + + switch (mapFileDataSelector.getDataSelectionType()) { + case MapFileDataSelector::DataSelectionType::INVALID: + break; + case MapFileDataSelector::DataSelectionType::COLUMN_DATA: + break; + case MapFileDataSelector::DataSelectionType::ROW_DATA: + break; + case MapFileDataSelector::DataSelectionType::SURFACE_VERTEX: + break; + case MapFileDataSelector::DataSelectionType::SURFACE_VERTICES_AVERAGE: + break; + case MapFileDataSelector::DataSelectionType::VOLUME_XYZ: + { + if (isMappedWithPalette()) { + float xyz[3]; + mapFileDataSelector.getVolumeVoxelXYZ(xyz); + + int64_t ijk[3]; + enclosingVoxel(xyz, + ijk); + + if (indexValid(ijk)) { + const int32_t numMaps = getNumberOfMaps(); + for (int32_t iMap = 0; iMap < numMaps; iMap++) { + dataOut.push_back(getValue(ijk, iMap)); + } + } + } + } + break; + } +} + +/** + * Are all brainordinates in this file also in the given file? + * That is, the brainordinates are equal to or a subset of the brainordinates + * in the given file. + * + * @param mapFile + * The given map file. + * @return + * Match status. + */ +CaretMappableDataFile::BrainordinateMappingMatch +VolumeFile::getBrainordinateMappingMatch(const CaretMappableDataFile* mapFile) const +{ + CaretAssert(mapFile); + if (mapFile->getDataFileType() == DataFileTypeEnum::VOLUME) { + const VolumeFile* otherVolumeFile = dynamic_cast(mapFile); + CaretAssert(otherVolumeFile); + + std::vector myDims, otherDims; + getDimensions(myDims); + otherVolumeFile->getDimensions(otherDims); + + for (int32_t i = 0; i < 3; i++) { + CaretAssertVectorIndex(myDims, i); + CaretAssertVectorIndex(otherDims, i); + if (myDims[i] != otherDims[i]) { + return BrainordinateMappingMatch::NO; + } + } + + return BrainordinateMappingMatch::EQUAL; + } + + return BrainordinateMappingMatch::NO; +} + diff --git a/src/Files/VolumeFile.h b/src/Files/VolumeFile.h index e5e968f98a01d471e541a03fb8e5fac94dac1100..bf7bfeae87c32649ebb03f4f70caeb38d36455f9 100644 --- a/src/Files/VolumeFile.h +++ b/src/Files/VolumeFile.h @@ -31,7 +31,6 @@ #include "StructureEnum.h" #include "GiftiMetaData.h" #include "BoundingBox.h" -#include "PaletteFile.h" #include "VolumeFileVoxelColorizer.h" #include "VoxelIJK.h" @@ -62,13 +61,14 @@ namespace caret { {//TODO: prune this once statistics gets straightened out CaretPointer m_fastStatistics; CaretPointer m_histogram; + int32_t m_histogramNumberOfBuckets = 100; CaretPointer m_histogramLimitedValues; + int32_t m_histogramLimitedValuesNumberOfBuckets = 100; float m_histogramLimitedValuesMostPositiveValueInclusive; float m_histogramLimitedValuesLeastPositiveValueInclusive; float m_histogramLimitedValuesLeastNegativeValueInclusive; float m_histogramLimitedValuesMostNegativeValueInclusive; bool m_histogramLimitedValuesIncludeZeroValues; - CaretPointer m_metadata;//NOTE: does not get saved currently! }; mutable std::vector m_brickAttributes;//because statistics and metadata construct lazily @@ -81,9 +81,12 @@ namespace caret { /** Histogram used when statistics computed on all data in file */ CaretPointer m_fileHistogram; + int32_t m_fileHistogramNumberOfBuckets = 100; + /** Histogram with limited values used when statistics computed on all data in file */ CaretPointer m_fileHistorgramLimitedValues; + int32_t m_fileHistogramLimitedValuesNumberOfBuckets; float m_fileHistogramLimitedValuesMostPositiveValueInclusive; float m_fileHistogramLimitedValuesLeastPositiveValueInclusive; float m_fileHistogramLimitedValuesLeastNegativeValueInclusive; @@ -186,13 +189,15 @@ namespace caret { bool isEmpty() const { return VolumeBase::isEmpty(); } + bool hasGoodSpatialInformation() const; + virtual void setModified(); virtual void clearModified(); virtual bool isModifiedExcludingPaletteColorMapping() const; - void getVoxelSpaceBoundingBox(BoundingBox& boundingBoxOut) const; + void getVoxelSpaceBoundingBox(BoundingBox& boundingBoxOut) const override; /** * @return The structure for this file. @@ -209,12 +214,12 @@ namespace caret { /** * @return Get access to the file's metadata. */ - GiftiMetaData* getFileMetaData() { return NULL; }//doesn't seem to be a spot for generic metadata in the nifti caret extension + GiftiMetaData* getFileMetaData(); /** * @return Get access to unmodifiable file's metadata. */ - const GiftiMetaData* getFileMetaData() const { return NULL; } + const GiftiMetaData* getFileMetaData() const; bool isSurfaceMappable() const { return false; } @@ -259,7 +264,7 @@ namespace caret { bool isMappedWithPalette() const; - virtual void getPaletteNormalizationModesSupported(std::vector& modesSupportedOut); + virtual void getPaletteNormalizationModesSupported(std::vector& modesSupportedOut) const; PaletteColorMapping* getMapPaletteColorMapping(const int32_t mapIndex); @@ -281,8 +286,7 @@ namespace caret { AString getMapUniqueID(const int32_t mapIndex) const; - void updateScalarColoringForMap(const int32_t mapIndex, - const PaletteFile* paletteFile); + void updateScalarColoringForMap(const int32_t mapIndex) override; virtual int64_t getVoxelColorsForSliceInMap(const int32_t mapIndex, const int64_t firstVoxelIJK[3], @@ -292,17 +296,17 @@ namespace caret { const int64_t numberOfColumns, const DisplayGroupEnum::Enum displayGroup, const int32_t tabIndex, - uint8_t* rgbaOut) const; + uint8_t* rgbaOut) const override; - virtual int64_t getVoxelColorsForSliceInMap(const PaletteFile* paletteFile, + virtual int64_t getVoxelColorsForSliceInMap( const int32_t mapIndex, const VolumeSliceViewPlaneEnum::Enum slicePlane, const int64_t sliceIndex, const DisplayGroupEnum::Enum displayGroup, const int32_t tabIndex, - uint8_t* rgbaOut) const; + uint8_t* rgbaOut) const override; - virtual int64_t getVoxelColorsForSubSliceInMap(const PaletteFile* paletteFile, + virtual int64_t getVoxelColorsForSubSliceInMap( const int32_t mapIndex, const VolumeSliceViewPlaneEnum::Enum slicePlane, const int64_t sliceIndex, @@ -311,21 +315,20 @@ namespace caret { const int64_t voxelCountIJK[3], const DisplayGroupEnum::Enum displayGroup, const int32_t tabIndex, - uint8_t* rgbaOut) const; + uint8_t* rgbaOut) const override; void getVoxelValuesForSliceInMap(const int32_t mapIndex, const VolumeSliceViewPlaneEnum::Enum slicePlane, const int64_t sliceIndex, float* sliceValues) const; - void getVoxelColorInMap(const PaletteFile* paletteFile, - const int64_t i, + void getVoxelColorInMap(const int64_t i, const int64_t j, const int64_t k, const int64_t mapIndex, const DisplayGroupEnum::Enum displayGroup, const int32_t tabIndex, - uint8_t rgbaOut[4]) const; + uint8_t rgbaOut[4]) const override; void clearVoxelColoringForMap(const int64_t mapIndex); @@ -354,8 +357,12 @@ namespace caret { virtual ChartDataCartesian* loadLineSeriesChartDataForVoxelAtCoordinate(const float xyz[3]); - virtual void getSupportedLineSeriesChartDataTypes(std::vector& chartDataTypesOut) const; + virtual void getSupportedLineSeriesChartDataTypes(std::vector& chartDataTypesOut) const; + + virtual void getDataForSelector(const MapFileDataSelector& mapFileDataSelector, + std::vector& dataOut) const override; + virtual BrainordinateMappingMatch getBrainordinateMappingMatch(const CaretMappableDataFile* mapFile) const override; }; } diff --git a/src/Files/VolumeFileEditorDelegate.cxx b/src/Files/VolumeFileEditorDelegate.cxx index 906bcd5b7dad4d047cbc5259bfb46082301673fd..b1e244f4788d1e2c3c570be45f25147fdf1e046d 100644 --- a/src/Files/VolumeFileEditorDelegate.cxx +++ b/src/Files/VolumeFileEditorDelegate.cxx @@ -605,7 +605,7 @@ VolumeFileEditorDelegate::performTurnOnOrOffOblique(const EditInfo& editInfo, float ijkFloat[3]; m_volumeFile->spaceToIndex(brushXYZ, ijkFloat); - int64_t ijk[3] = { ijkFloat[0], ijkFloat[1], ijkFloat[2] }; + int64_t ijk[3] = { (int64_t)ijkFloat[0], (int64_t)ijkFloat[1], (int64_t)ijkFloat[2] }; modifiedVoxels->addVoxelRedoUndo(ijk, redoVoxelValue, m_volumeFile->getValue(ijk, editInfo.m_mapIndex)); diff --git a/src/Files/VolumeFileVoxelColorizer.cxx b/src/Files/VolumeFileVoxelColorizer.cxx index ccf5d95aa2017de63f739a8e486677b063bf5177..51a914aa43eeb10b3bc374474dff7603a603f4d2 100644 --- a/src/Files/VolumeFileVoxelColorizer.cxx +++ b/src/Files/VolumeFileVoxelColorizer.cxx @@ -87,19 +87,9 @@ VolumeFileVoxelColorizer::~VolumeFileVoxelColorizer() * * @param mapIndex * Index of map. - * @param palette - * Palette used for scalar color assignment. May be NULL for data - * not mapped with a palette. - * @param thresholdVolume - * Volume that contains thresholding (if NULL indicates no thresholding). - * @param thresholdVolumeMapIndex - * Index of map in thresholding volume. */ void -VolumeFileVoxelColorizer::assignVoxelColorsForMap(const int32_t mapIndex, - const Palette* palette, - const VolumeFile* thresholdVolume, - const int32_t /*thresholdVolumeMapIndex*/) +VolumeFileVoxelColorizer::assignVoxelColorsForMap(const int32_t mapIndex) { CaretAssertVectorIndex(m_mapRGBA, mapIndex); @@ -111,10 +101,38 @@ VolumeFileVoxelColorizer::assignVoxelColorsForMap(const int32_t mapIndex, */ const float* mapDataPointer = m_volumeFile->getFrame(mapIndex); + VolumeFile* thresholdVolume = NULL; + int32_t thresholdVolumeMapIndex = -1; + switch (m_volumeFile->getMapPaletteColorMapping(mapIndex)->getThresholdType()) { + case PaletteThresholdTypeEnum::THRESHOLD_TYPE_FILE: + { + CaretMappableDataFileAndMapSelectionModel* threshSel = m_volumeFile->getMapThresholdFileSelectionModel(mapIndex); + CaretMappableDataFile* mapFile = threshSel->getSelectedFile(); + if (mapFile != NULL) { + thresholdVolume = dynamic_cast(mapFile); + CaretAssert(thresholdVolume); + thresholdVolumeMapIndex = threshSel->getSelectedMapIndex(); + } + } + break; + case PaletteThresholdTypeEnum::THRESHOLD_TYPE_MAPPED: + break; + case PaletteThresholdTypeEnum::THRESHOLD_TYPE_MAPPED_AVERAGE_AREA: + break; + case PaletteThresholdTypeEnum::THRESHOLD_TYPE_NORMAL: + /* + * Thresholding with 'self' + */ + thresholdVolume = m_volumeFile; + thresholdVolumeMapIndex = mapIndex; + break; + case PaletteThresholdTypeEnum::THRESHOLD_TYPE_OFF: + break; + } + /* * Get access to threshold data */ - //float* thresholdDataPointer = NULL; bool ignoreThresholding = true; if (thresholdVolume != NULL) { int64_t threshI, threshJ, threshK, threshMapCount, threshNumberOfComponents; @@ -131,12 +149,15 @@ VolumeFileVoxelColorizer::assignVoxelColorsForMap(const int32_t mapIndex, + ") dimensions do not match " + m_volumeFile->getFileNameNoPath()); } + else if ((thresholdVolumeMapIndex < 0) + || (thresholdVolumeMapIndex >= thresholdVolume->getNumberOfMaps())) { + CaretLogSevere("Threshold volume (" + + thresholdVolume->getFileNameNoPath() + + ") map index=" + + AString::number(thresholdVolumeMapIndex) + + " is invalid"); + } else { - /* - * Can use same voxel counter per map since volumes are - * identical dimensions; - */ - //thresholdDataPointer = thresholdVolume->m_data + m_voxelCountPerMap;//TSC: this is unused, and can easily be an invalid pointer - commenting out for unused warning ignoreThresholding = false; } } @@ -146,9 +167,6 @@ VolumeFileVoxelColorizer::assignVoxelColorsForMap(const int32_t mapIndex, case SubvolumeAttributes::ANATOMY: case SubvolumeAttributes::FUNCTIONAL: { - CaretAssert(palette); - - FastStatistics* statistics = NULL; switch (m_volumeFile->getPaletteNormalizationMode()) { case PaletteNormalizationModeEnum::NORMALIZATION_ALL_MAP_DATA: @@ -160,12 +178,18 @@ VolumeFileVoxelColorizer::assignVoxelColorsForMap(const int32_t mapIndex, } CaretAssert(statistics); + const float* thresholdDataPointer = (ignoreThresholding + ? mapDataPointer + : thresholdVolume->getFrame(thresholdVolumeMapIndex)); + const PaletteColorMapping* thresholdPaletteColorMapping = (ignoreThresholding + ? m_volumeFile->getMapPaletteColorMapping(mapIndex) + : thresholdVolume->getMapPaletteColorMapping(thresholdVolumeMapIndex)); - NodeAndVoxelColoring::colorScalarsWithPalette(statistics, //m_volumeFile->getMapFastStatistics(mapIndex), + NodeAndVoxelColoring::colorScalarsWithPalette(statistics, m_volumeFile->getMapPaletteColorMapping(mapIndex), - palette, - mapDataPointer, mapDataPointer, + thresholdPaletteColorMapping, + thresholdDataPointer, m_voxelCountPerMap, m_mapRGBA[mapIndex], ignoreThresholding); @@ -486,7 +510,7 @@ VolumeFileVoxelColorizer::getVoxelColorsForSubSliceInMap(const int32_t mapIndex, const int64_t sliceIndex, const int64_t firstCornerVoxelIndex[3], const int64_t lastCornerVoxelIndex[3], - const int64_t voxelCountIJK[3], + const int64_t* CaretParameterUsedInDebugCompileOnly(voxelCountIJK), const DisplayGroupEnum::Enum displayGroup, const int32_t tabIndex, uint8_t* rgbaOut) const @@ -544,8 +568,8 @@ VolumeFileVoxelColorizer::getVoxelColorsForSubSliceInMap(const int32_t mapIndex, CaretAssert(false); } - const int64_t voxelCount = (voxelCountIJK[0] * voxelCountIJK[1] * voxelCountIJK[2]); - const int64_t rgbaCount = voxelCount * 4; + CaretUsedInDebugCompileOnly(const int64_t voxelCount = (voxelCountIJK[0] * voxelCountIJK[1] * voxelCountIJK[2])); + CaretUsedInDebugCompileOnly(const int64_t rgbaCount = voxelCount * 4); /* * Pointer to maps RGBA values @@ -561,7 +585,7 @@ VolumeFileVoxelColorizer::getVoxelColorsForSubSliceInMap(const int32_t mapIndex, int64_t validVoxelCount = 0; - int64_t innerCount = std::abs(lastCornerVoxelIndex[innerLoop] - firstCornerVoxelIndex[innerLoop]) + 1;//to check validity of index + CaretUsedInDebugCompileOnly(int64_t innerCount = std::abs(lastCornerVoxelIndex[innerLoop] - firstCornerVoxelIndex[innerLoop]) + 1);//to check validity of index int64_t rgbaOutIndex = 0; for (iterijk[outerLoop] = firstCornerVoxelIndex[outerLoop]; diff --git a/src/Files/VolumeFileVoxelColorizer.h b/src/Files/VolumeFileVoxelColorizer.h index 544b01e0b48c7025f79255356b59e65f4dcba48e..8890d0394ff7c26db8c51bd4d968cbb98b405f73 100644 --- a/src/Files/VolumeFileVoxelColorizer.h +++ b/src/Files/VolumeFileVoxelColorizer.h @@ -28,7 +28,6 @@ namespace caret { - class Palette; class VolumeFile; class VolumeFileVoxelColorizer : public CaretObject { @@ -38,10 +37,7 @@ namespace caret { virtual ~VolumeFileVoxelColorizer(); - void assignVoxelColorsForMap(const int32_t mapIndex, - const Palette* palette, - const VolumeFile* thresholdVolume, - const int32_t thresholdVolumeMapIndex); + void assignVoxelColorsForMap(const int32_t mapIndex); int64_t getVoxelColorsForSliceInMap(const int32_t mapIndex, const int64_t firstVoxelIJK[3], @@ -65,7 +61,7 @@ namespace caret { const int64_t sliceIndex, const int64_t firstCornerVoxelIndex[3], const int64_t lastCornerVoxelIndex[3], - const int64_t voxelCountIJK[3], + const int64_t* voxelCountIJK, const DisplayGroupEnum::Enum displayGroup, const int32_t tabIndex, uint8_t* rgbaOut) const; diff --git a/src/Files/WarpfieldFile.cxx b/src/Files/WarpfieldFile.cxx index 38eece492baf0cc97e719851afe8d00380910748..717584c352350118b4ff7ed511d7a046e7d28584 100644 --- a/src/Files/WarpfieldFile.cxx +++ b/src/Files/WarpfieldFile.cxx @@ -27,27 +27,87 @@ using namespace caret; using namespace std; +CaretPointer WarpfieldFile::generateAbsolute() const +{ + if (m_warpfield == NULL) + { + throw DataFileException("generateAbsolute() called on uninitialized WarpfieldFile"); + } + CaretPointer ret(new VolumeFile()); + vector dims = m_warpfield->getDimensions(); + CaretAssert(dims[3] == 3);//make sure it looks like a warpfield + dims.resize(4);//drop number of components + ret->reinitialize(dims, m_warpfield->getSform()); + ret->setMapName(0, "x location"); + ret->setMapName(1, "y location"); + ret->setMapName(2, "z location"); + for (int b = 0; b < 3; ++b) + { + for (int64_t k = 0; k < dims[2]; ++k) + { + for (int64_t j = 0; j < dims[1]; ++j) + { + for (int64_t i = 0; i < dims[0]; ++i) + { + Vector3D voxelCenter; + m_warpfield->indexToSpace(i, j, k, voxelCenter); + ret->setValue(m_warpfield->getValue(i, j, k, b) + voxelCenter[b], i, j, k, b); + } + } + } + } + return ret; +} + +namespace +{ + void genericWarpfieldRead(VolumeFile& volOut, const AString& filename) + { + volOut.readFile(filename); + vector dims; + volOut.getDimensions(dims); + if (dims[3] != 3) + { + throw DataFileException("volume file '" + filename + "' has the wrong number of subvolumes for a warpfield"); + } + if (dims[4] != 1) + { + throw DataFileException("volume file '" + filename + "' has multiple components, which is not allowed in a warpfield"); + } + volOut.setMapName(0, "x displacement"); + volOut.setMapName(1, "y displacement"); + volOut.setMapName(2, "z displacement"); + } +} + void WarpfieldFile::readWorld(const AString& warpname) { CaretPointer newFile(new VolumeFile()); - newFile->readFile(warpname); + genericWarpfieldRead(*newFile, warpname); + m_warpfield = newFile;//drop the previous warpfield, and replace with the new one +} + +void WarpfieldFile::readITK(const AString& warpname) +{ + CaretPointer newFile(new VolumeFile()); + genericWarpfieldRead(*newFile, warpname); vector dims; newFile->getDimensions(dims); - if (dims[3] != 3) - { - throw DataFileException("volume file '" + warpname + "' has the wrong number of subvolumes for a warpfield"); - } - if (dims[4] != 1) + for (int64_t k = 0; k < dims[2]; ++k) { - throw DataFileException("volume file '" + warpname + "' has multiple components, which is not allowed in a warpfield"); + for (int64_t j = 0; j < dims[1]; ++j) + { + for (int64_t i = 0; i < dims[0]; ++i) + { + newFile->setValue(-newFile->getValue(i, j, k, 0), i, j, k, 0);//negate x and y to convert LPS displacements to RAS + newFile->setValue(-newFile->getValue(i, j, k, 1), i, j, k, 1); + } + } } - m_warpfield = newFile;//drop the previous warpfield, and replace with the new one - m_warpfield->setMapName(0, "x displacement"); - m_warpfield->setMapName(1, "y displacement"); - m_warpfield->setMapName(2, "z displacement"); + m_warpfield = newFile; } -void WarpfieldFile::readFnirt(const AString& warpName, const AString& sourceName) +void WarpfieldFile::readFnirt(const AString& warpName, const AString& sourceName, const bool& absolute) { FloatMatrix sourceSform, sourceFSL, refSform, refFSL; NiftiIO myIO; @@ -58,37 +118,35 @@ void WarpfieldFile::readFnirt(const AString& warpName, const AString& sourceName sourceSform = FloatMatrix(myIO.getHeader().getSForm()); sourceFSL = FloatMatrix(myIO.getHeader().getFSLSpace()); CaretPointer newFile(new VolumeFile()); - newFile->readFile(warpName); + genericWarpfieldRead(*newFile, warpName); vector dims; newFile->getDimensions(dims); - if (dims[3] != 3) - { - throw DataFileException("volume file '" + warpName + "' has the wrong number of subvolumes for a warpfield"); - } - if (dims[4] != 1) - { - throw DataFileException("volume file '" + warpName + "' has multiple components, which is not allowed in a warpfield"); - } FloatMatrix sourceTransform = sourceSform * sourceFSL.inverse();//goes from FSL source space to real source space Vector3D sourceTransX, sourceTransY, sourceTransZ, sourceTransOff; sourceTransform.getAffineVectors(sourceTransX, sourceTransY, sourceTransZ, sourceTransOff); Vector3D fslX, fslY, fslZ, fslOff; refFSL.getAffineVectors(fslX, fslY, fslZ, fslOff); - for (int k = 0; k < dims[2]; ++k) + for (int64_t k = 0; k < dims[2]; ++k) { - for (int j = 0; j < dims[1]; ++j) + for (int64_t j = 0; j < dims[1]; ++j) { - for (int i = 0; i < dims[0]; ++i) + for (int64_t i = 0; i < dims[0]; ++i) { Vector3D fslcoord = i * fslX + j * fslY + k * fslZ + fslOff; - Vector3D coord, fsldisplacement; + Vector3D coord, fslwarpval; newFile->indexToSpace(i, j, k, coord); - fsldisplacement[0] = newFile->getValue(i, j, k, 0); - fsldisplacement[1] = newFile->getValue(i, j, k, 1); - fsldisplacement[2] = newFile->getValue(i, j, k, 2); - Vector3D fslTransAbsolute = fslcoord + fsldisplacement; + fslwarpval[0] = newFile->getValue(i, j, k, 0); + fslwarpval[1] = newFile->getValue(i, j, k, 1); + fslwarpval[2] = newFile->getValue(i, j, k, 2); + Vector3D fslTransAbsolute; + if (absolute) + { + fslTransAbsolute = fslwarpval; + } else { + fslTransAbsolute = fslcoord + fslwarpval; + } Vector3D transAbsolute = fslTransAbsolute[0] * sourceTransX + fslTransAbsolute[1] * sourceTransY + fslTransAbsolute[2] * sourceTransZ + sourceTransOff; - Vector3D transdisplace = transAbsolute - coord; + Vector3D transdisplace = transAbsolute - coord;//internal format is always relative newFile->setValue(transdisplace[0], i, j, k, 0);//overwrite vectors in place to save memory newFile->setValue(transdisplace[1], i, j, k, 1); newFile->setValue(transdisplace[2], i, j, k, 2); @@ -96,18 +154,41 @@ void WarpfieldFile::readFnirt(const AString& warpName, const AString& sourceName } } m_warpfield = newFile;//drop the previous warpfield, and replace with the new one - m_warpfield->setMapName(0, "x displacement"); - m_warpfield->setMapName(1, "y displacement"); - m_warpfield->setMapName(2, "z displacement"); } -void WarpfieldFile::writeWorld(const AString& warpname) +void WarpfieldFile::writeWorld(const AString& warpname) const { if (m_warpfield == NULL) throw DataFileException("writeWorld called on uninitialized warpfield"); m_warpfield->writeFile(warpname); } -void WarpfieldFile::writeFnirt(const AString& warpname, const AString& sourceName) +void WarpfieldFile::writeITK(const AString& warpname) const +{ + if (m_warpfield == NULL) throw DataFileException("writeWorld called on uninitialized warpfield"); + vector dims; + m_warpfield->getDimensions(dims); + dims.resize(4);//drop number of components + VolumeFile outFile; + outFile.reinitialize(dims, m_warpfield->getSform()); + outFile.setMapName(0, "x displacement"); + outFile.setMapName(1, "y displacement"); + outFile.setMapName(2, "z displacement"); + for (int64_t k = 0; k < dims[2]; ++k) + { + for (int64_t j = 0; j < dims[1]; ++j) + { + for (int64_t i = 0; i < dims[0]; ++i) + { + outFile.setValue(-m_warpfield->getValue(i, j, k, 0), i, j, k, 0);//negate x and y to convert RAS displacements to LPS + outFile.setValue(-m_warpfield->getValue(i, j, k, 1), i, j, k, 1); + outFile.setValue(m_warpfield->getValue(i, j, k, 2), i, j, k, 2); + } + } + } + outFile.writeFile(warpname); +} + +void WarpfieldFile::writeFnirt(const AString& warpname, const AString& sourceName) const { if (m_warpfield == NULL) throw DataFileException("writeFnirt called on uninitialized warpfield"); FloatMatrix sourceSform, sourceFSL, refSform, refFSL; @@ -133,11 +214,11 @@ void WarpfieldFile::writeFnirt(const AString& warpname, const AString& sourceNam FSLTransform.getAffineVectors(FSLTransX, FSLTransY, FSLTransZ, FSLTransOff); Vector3D fslX, fslY, fslZ, fslOff; refFSL.getAffineVectors(fslX, fslY, fslZ, fslOff); - for (int k = 0; k < dims[2]; ++k) + for (int64_t k = 0; k < dims[2]; ++k) { - for (int j = 0; j < dims[1]; ++j) + for (int64_t j = 0; j < dims[1]; ++j) { - for (int i = 0; i < dims[0]; ++i) + for (int64_t i = 0; i < dims[0]; ++i) { Vector3D fslcoord = i * fslX + j * fslY + k * fslZ + fslOff; Vector3D coord, realdisplacement; diff --git a/src/Files/WarpfieldFile.h b/src/Files/WarpfieldFile.h index 551451f20b045c041e242d9a2c00c41e57fad8ce..7b64c4a470c19f21ca6a668a9998a054654a71f4 100644 --- a/src/Files/WarpfieldFile.h +++ b/src/Files/WarpfieldFile.h @@ -31,11 +31,14 @@ namespace caret { { CaretPointer m_warpfield; public: - const VolumeFile* getWarpfield() { return m_warpfield.getPointer(); } - void readFnirt(const AString& warpName, const AString& sourceName); + const VolumeFile* getWarpfield() const { return m_warpfield.getPointer(); } + CaretPointer generateAbsolute() const; void readWorld(const AString& warpname); - void writeFnirt(const AString& warpname, const AString& sourceName);//for completeness - void writeWorld(const AString& warpname); + void readITK(const AString& warpname); + void readFnirt(const AString& warpName, const AString& sourceName, const bool& absolute = false); + void writeWorld(const AString& warpname) const;//for completeness + void writeITK(const AString& warpname) const; + void writeFnirt(const AString& warpname, const AString& sourceName) const; }; } diff --git a/src/Files/XmlStreamReaderHelper.cxx b/src/Files/XmlStreamReaderHelper.cxx index 92e25b0bb05369e04498dff75647b090429f3410..34712bf96233ce02f875868c734c92e763250f6f 100644 --- a/src/Files/XmlStreamReaderHelper.cxx +++ b/src/Files/XmlStreamReaderHelper.cxx @@ -241,6 +241,42 @@ XmlStreamReaderHelper::getRequiredAttributeBoolValue(const QXmlStreamAttributes& return value; } +/** + * Get the int value for the given attribute name from the + * given attributes. If the attribute is missing or if the + * attributes value is empty text, the defaultValue is + * returned. This method WILL NOT throw an exception. + * + * @param attributes + * The XML attributes. + * @param elementName + * Name of element containing the attributes. + * @param attributeName + * Name of the attribute. + * @param defaultValue + * Value that is returned if the attribute is missing. + * @return + * int value for the attribute. + * @throw + * DataFileException if attribute is missing or value is + * an empty string. + */ +int +XmlStreamReaderHelper::getOptionalAttributeIntValue(const QXmlStreamAttributes& attributes, + const QString& elementName, + const QString& attributeName, + const int defaultValue) +{ + const AString stringValue = getOptionalAttributeStringValue(attributes, + elementName, + attributeName, + AString::number(defaultValue)); + + const int value = stringValue.toInt(); + + return value; +} + /** * Get the int value for the given attribute name from the * given attributes. If the attribute name is not found or diff --git a/src/Files/XmlStreamReaderHelper.h b/src/Files/XmlStreamReaderHelper.h index 3b1e5f857d0bde7adf4fd2cf4d8c44d1c7a9640c..3876684eba238cd8eca2dad2e0b7528d02595a12 100644 --- a/src/Files/XmlStreamReaderHelper.h +++ b/src/Files/XmlStreamReaderHelper.h @@ -49,6 +49,11 @@ namespace caret { const QString& alternateAttributeNameOne = "", const QString& alternateAttributeNameTwo = ""); + int getOptionalAttributeIntValue(const QXmlStreamAttributes& attributes, + const QString& elementName, + const QString& attributeName, + const int defaultValue); + int getRequiredAttributeIntValue(const QXmlStreamAttributes& attributes, const QString& elementName, const QString& attributeName); diff --git a/src/FilesBase/CMakeLists.txt b/src/FilesBase/CMakeLists.txt index 4c6e06e914475d13b3d2f2618ec0a43e90173f1c..d23a04fa1d95833b37547e3eeb14ab4085e73113 100644 --- a/src/FilesBase/CMakeLists.txt +++ b/src/FilesBase/CMakeLists.txt @@ -11,7 +11,12 @@ SET(QT_DONT_USE_QTGUI) # # Add QT for includes # -INCLUDE (${QT_USE_FILE}) +if(Qt5_FOUND) + include_directories(${Qt5Core_INCLUDE_DIRS}) +endif() +IF (QT4_FOUND) + INCLUDE(${QT_USE_FILE}) +ENDIF () # @@ -44,6 +49,8 @@ VolumeSliceViewPlaneEnum.cxx VolumeSpace.cxx ) +TARGET_LINK_LIBRARIES(FilesBase ${CARET_QT5_LINK}) + # # Find Headers # diff --git a/src/FilesBase/GiftiMetaData.cxx b/src/FilesBase/GiftiMetaData.cxx index 1364ebbe9071f5832fa73b30e9931ab50b269320..fd7747b41498c7283487ad3df0c0142405601203 100644 --- a/src/FilesBase/GiftiMetaData.cxx +++ b/src/FilesBase/GiftiMetaData.cxx @@ -677,3 +677,23 @@ GiftiMetaData::isModified() const return this->modifiedFlag; } +/** + * Is the metadata empty (no metadata)? + */ +bool +GiftiMetaData::isEmpty() const +{ + return this->metadata.empty(); +} + +/** + * @return Number of metadata + */ +int32_t +GiftiMetaData::getNumberOfMetaData() const +{ + return this->metadata.size(); +} + + + diff --git a/src/FilesBase/GiftiMetaData.h b/src/FilesBase/GiftiMetaData.h index 619d33e67623c4279f7402e0dacdb766273b9a47..a98f3724c16b0edb78060a142e7d438d07573e11 100644 --- a/src/FilesBase/GiftiMetaData.h +++ b/src/FilesBase/GiftiMetaData.h @@ -129,6 +129,11 @@ public: void resetUniqueIdentifier(); + bool isEmpty() const; + + int32_t getNumberOfMetaData() const; + + private: void readEntry(QXmlStreamReader& xml); diff --git a/src/FilesBase/GiftiMetaDataXmlElements.h b/src/FilesBase/GiftiMetaDataXmlElements.h index c2a51ac119c407a0b2f41f41f0d24f8e8cffc1ce..b468aa7f02ea5e444f8c61624c7a558c4c8bf2cc 100644 --- a/src/FilesBase/GiftiMetaDataXmlElements.h +++ b/src/FilesBase/GiftiMetaDataXmlElements.h @@ -205,6 +205,12 @@ namespace GiftiMetaDataXmlElements { /**name of palette color mapping stored in metadata */ static const AString METADATA_NAME_PALETTE_COLOR_MAPPING = "PaletteColorMapping"; + + /** name of palette normalization mode */ + static const AString METADATA_PALETTE_NORMALIZATION_MODE = "PaletteNormalizationMode"; + + /** name of histogram number of buckets */ + static const AString HISTOGRAM_NUMBER_OF_BUCKETS = "HistogramNumberOfBuckets"; } // namespace diff --git a/src/FilesBase/VolumeBase.h b/src/FilesBase/VolumeBase.h index 7731f267506ab016c0b27c82a3afe22cfd3d79ac..12b03e1402946268f86f63b27bf6f5cc0c1bd4c1 100644 --- a/src/FilesBase/VolumeBase.h +++ b/src/FilesBase/VolumeBase.h @@ -33,6 +33,8 @@ namespace caret { struct AbstractHeader { + virtual bool hasGoodSpatialInformation() const = 0; + enum HeaderType { NIFTI @@ -57,11 +59,11 @@ namespace caret { void reinitialize(int64_t dims[5]); void clear(); - void getDimensions(std::vector& dimOut) const;//NOTE: always returns a vector of 5 elements - void getDimensions(int64_t& dimOut1, int64_t& dimOut2, int64_t& dimOut3, int64_t& dimTimeOut, int64_t& numComponents) const; + virtual void getDimensions(std::vector& dimOut) const;//NOTE: always returns a vector of 5 elements + virtual void getDimensions(int64_t& dimOut1, int64_t& dimOut2, int64_t& dimOut3, int64_t& dimTimeOut, int64_t& numComponents) const; std::vector getDimensions() const; const int64_t* getDimensionsPtr() const { return m_dimensions; } - inline const int64_t& getNumberOfComponents() const { + inline const int64_t& getNumberOfComponents() const { return m_dimensions[4]; } @@ -157,7 +159,7 @@ namespace caret { return m_origDims; } - inline const int64_t& getNumberOfComponents() const { + inline const int64_t& getNumberOfComponents() const override { return m_storage.getNumberOfComponents(); } @@ -182,18 +184,18 @@ namespace caret { //not to worry, simple passthrough convenience functions like these get partially optimized to the main one by even -O1, and completely optimized together by -O2 or -O3 ///returns coordinate triplet of an index triplet - void indexToSpace(const int64_t* indexIn, float* coordOut) const; + void indexToSpace(const int64_t* indexIn, float* coordOut) const override; ///returns three coordinates of an index triplet void indexToSpace(const int64_t* indexIn, float& coordOut1, float& coordOut2, float& coordOut3) const; ///returns coordinate triplet of a floating point index triplet void indexToSpace(const float* indexIn, float* coordOut) const; ///returns coordinate triplet of three floating point indexes - void indexToSpace(const float& indexIn1, const float& indexIn2, const float& indexIn3, float* coordOut) const; + void indexToSpace(const float& indexIn1, const float& indexIn2, const float& indexIn3, float* coordOut) const override; ///returns three coordinates of a floating point index triplet void indexToSpace(const float* indexIn, float& coordOut1, float& coordOut2, float& coordOut3) const; ///returns three coordinates of three floating point indexes - void indexToSpace(const float& indexIn1, const float& indexIn2, const float& indexIn3, float& coordOut1, float& coordOut2, float& coordOut3) const; + void indexToSpace(const float& indexIn1, const float& indexIn2, const float& indexIn3, float& coordOut1, float& coordOut2, float& coordOut3) const override; ///returns floating point index triplet of a given coordinate triplet void spaceToIndex(const float* coordIn, float* indexOut) const; @@ -211,7 +213,7 @@ namespace caret { ///returns integer indexes of voxel whose center is closest to the coordinate triplet void enclosingVoxel(const float* coordIn, int64_t& indexOut1, int64_t& indexOut2, int64_t& indexOut3) const; ///returns integer indexes of voxel whose center is closest to the three coordinates - void enclosingVoxel(const float& coordIn1, const float& coordIn2, const float& coordIn3, int64_t& indexOut1, int64_t& indexOut2, int64_t& indexOut3) const; + void enclosingVoxel(const float& coordIn1, const float& coordIn2, const float& coordIn3, int64_t& indexOut1, int64_t& indexOut2, int64_t& indexOut3) const override; inline const VolumeSpace& getVolumeSpace() const { return m_volSpace; } @@ -245,7 +247,7 @@ namespace caret { inline float getVoxelValue(const float* coordinateIn, bool* validOut = NULL, const int64_t mapIndex = 0, - const int64_t component = 0) const + const int64_t component = 0) const override { return getVoxelValue(coordinateIn[0], coordinateIn[1], @@ -279,7 +281,7 @@ namespace caret { const float coordinateZ, bool* validOut = NULL, const int64_t mapIndex = 0, - const int64_t component = 0) const + const int64_t component = 0) const override { if (validOut != NULL) { *validOut = false; @@ -330,9 +332,9 @@ namespace caret { void setFrame(const float* frameIn, const int64_t brickIndex = 0, const int64_t component = 0) { m_storage.setFrame(frameIn, brickIndex, component); setModified(); } ///gets dimensions as a vector of 5 integers, 3 spatial, time, components - void getDimensions(std::vector& dimOut) const { m_storage.getDimensions(dimOut); } + void getDimensions(std::vector& dimOut) const override { m_storage.getDimensions(dimOut); } ///gets dimensions - void getDimensions(int64_t& dimOut1, int64_t& dimOut2, int64_t& dimOut3, int64_t& dimTimeOut, int64_t& numComponents) const + void getDimensions(int64_t& dimOut1, int64_t& dimOut2, int64_t& dimOut3, int64_t& dimTimeOut, int64_t& numComponents) const override { m_storage.getDimensions(dimOut1, dimOut2, dimOut3, dimTimeOut, numComponents); } diff --git a/src/FilesBase/VolumeMappableInterface.h b/src/FilesBase/VolumeMappableInterface.h index 32d686e2e338f04e2ee52d21ec946f347f08bff4..f9cfecb603f2141ce33653b714fb123dddfe7d70 100644 --- a/src/FilesBase/VolumeMappableInterface.h +++ b/src/FilesBase/VolumeMappableInterface.h @@ -28,7 +28,6 @@ namespace caret { class BoundingBox; - class PaletteFile; /** * \class caret::VolumeMappableInterface @@ -261,8 +260,6 @@ namespace caret { /** * Get the voxel colors for a slice in the map. * - * @param paletteFile - * The palette file. * @param mapIndex * Index of the map. * @param slicePlane @@ -279,8 +276,7 @@ namespace caret { * @return * Number of voxels with alpha greater than zero */ - virtual int64_t getVoxelColorsForSliceInMap(const PaletteFile* paletteFile, - const int32_t mapIndex, + virtual int64_t getVoxelColorsForSliceInMap(const int32_t mapIndex, const VolumeSliceViewPlaneEnum::Enum slicePlane, const int64_t sliceIndex, const DisplayGroupEnum::Enum displayGroup, @@ -324,8 +320,6 @@ namespace caret { /** * Get the voxel colors for a sub slice in the map. * - * @param paletteFile - * The palette file. * @param mapIndex * Index of the map. * @param slicePlane @@ -348,8 +342,7 @@ namespace caret { * @return * Number of voxels with alpha greater than zero */ - virtual int64_t getVoxelColorsForSubSliceInMap(const PaletteFile* paletteFile, - const int32_t mapIndex, + virtual int64_t getVoxelColorsForSubSliceInMap(const int32_t mapIndex, const VolumeSliceViewPlaneEnum::Enum slicePlane, const int64_t sliceIndex, const int64_t firstCornerVoxelIndex[3], @@ -362,8 +355,6 @@ namespace caret { /** * Get the voxel coloring for the voxel at the given indices. * - * @param paletteFile - * The palette file. * @param indexIn1 * First dimension (i). * @param indexIn2 @@ -379,8 +370,7 @@ namespace caret { * @param rgbaOut * Output containing RGBA values for voxel at the given indices. */ - virtual void getVoxelColorInMap(const PaletteFile* paletteFile, - const int64_t indexIn1, + virtual void getVoxelColorInMap(const int64_t indexIn1, const int64_t indexIn2, const int64_t indexIn3, const int64_t brickIndex, diff --git a/src/FtglFont/CMakeLists.txt b/src/FtglFont/CMakeLists.txt index db5489251e99e090c335b169922667042bf334ef..27afc4937a224ebc1e06c7339bfcafd62c2e967b 100644 --- a/src/FtglFont/CMakeLists.txt +++ b/src/FtglFont/CMakeLists.txt @@ -3,45 +3,16 @@ # PROJECT (Ftgl) -# -# Need XML from Qt -# -SET(QT_DONT_USE_QTGUI) - -# -# Use OpenGL from QT -# -#SET(QT_USE_QTOPENGL TRUE) - # # Add QT for includes # -INCLUDE (${QT_USE_FILE}) - -# -# Need OpenGL -# -FIND_PACKAGE(OpenGL REQUIRED) -IF (OPENGL_FOUND) - # - # Need help finding includes on Apple - # - IF (APPLE) - # When searching for the include directory, find the location - # for the OpenGL framework rather than an individual header file. - FIND_PATH(OPENGL_INCLUDE_DIR OpenGL.framework - /System/Library/Frameworks - /Library/Frameworks - ~/Library/Frameworks - ) - ENDIF (APPLE) - # - # OpenGL Include Directory - # - INCLUDE_DIRECTORIES(${OPENGL_INCLUDE_DIR}) -ELSE (OPENGL_FOUND) - MESSAGE(FATAL_ERROR "OpenGL Libraries were not found") -ENDIF (OPENGL_FOUND) +if(Qt5_FOUND) + include_directories(${Qt5Core_INCLUDE_DIRS}) +endif() +IF (QT4_FOUND) + SET(QT_DONT_USE_QTGUI) + INCLUDE(${QT_USE_FILE}) +ENDIF () # # Create the brain library @@ -138,6 +109,7 @@ FTVectoriser.cpp # INCLUDE_DIRECTORIES( ${CMAKE_SOURCE_DIR}/Common +${CMAKE_SOURCE_DIR}/Graphics ${FREETYPE_INCLUDE_DIR_ft2build} ${FREETYPE_INCLUDE_DIR_freetype2} ${CMAKE_SOURCE_DIR}/FtglFont diff --git a/src/FtglFont/FTGlyph/FTBufferGlyph.cpp b/src/FtglFont/FTGlyph/FTBufferGlyph.cpp index 8fb81310c2a11ee57b821ec29ee647727bf92b99..5a9a6cbc05496253cb5e63dd4be2a21a98dbe92e 100644 --- a/src/FtglFont/FTGlyph/FTBufferGlyph.cpp +++ b/src/FtglFont/FTGlyph/FTBufferGlyph.cpp @@ -98,12 +98,12 @@ const FTPoint& FTBufferGlyphImpl::RenderImpl(const FTPoint& pen, int /*renderMod int dy = buffer->Height() - (int)(pos.Yf() + 0.5f); unsigned char * dest = buffer->Pixels() + dx + dy * buffer->Width(); - for(int y = 0; y < bitmap.rows; y++) + for(int y = 0; y < ((int)bitmap.rows); y++) { // FIXME: change the loop bounds instead of doing this test if(y + dy < 0 || y + dy >= buffer->Height()) continue; - for(int x = 0; x < bitmap.width; x++) + for(int x = 0; x < ((int)bitmap.width); x++) { if(x + dx < 0 || x + dx >= buffer->Width()) continue; diff --git a/src/FtglFont/FTGlyph/FTTextureGlyph.cpp b/src/FtglFont/FTGlyph/FTTextureGlyph.cpp index e1753700b9cf955d8671fc78bcb3bf1cb1b4bea3..3c1b332c6fc1c82ce5186e6f93524b09b0376304 100644 --- a/src/FtglFont/FTGlyph/FTTextureGlyph.cpp +++ b/src/FtglFont/FTGlyph/FTTextureGlyph.cpp @@ -25,6 +25,8 @@ * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ +#include "CaretOpenGLInclude.h" // Must be here to prevent GLEW error on Windows + #include "FtglConfig.h" #include @@ -34,6 +36,9 @@ #include "FTInternals.h" #include "FTTextureGlyphImpl.h" +#include "CaretLogger.h" +#include "GraphicsOpenGLError.h" +#include "GraphicsUtilitiesOpenGL.h" // // FTGLTextureGlyph @@ -94,8 +99,24 @@ FTTextureGlyphImpl::FTTextureGlyphImpl(FT_GlyphSlot glyph, int id, int xOffset, glPixelStorei(GL_UNPACK_ALIGNMENT, 1); glBindTexture(GL_TEXTURE_2D, glTextureID); + + /* + * Check for an OpenGL error. + * We will ignore errors caused by glTexSubImage2D() as it will cause + * OpenGL "Invalid Value" errors when some text characters get too small. + */ + std::unique_ptr openglError = caret::GraphicsUtilitiesOpenGL::getOpenGLError("FTGL before call to glTexSubImage2D()"); + if (openglError) { + CaretLogWarning(openglError->getVerboseDescription()); + } + glTexSubImage2D(GL_TEXTURE_2D, 0, xOffset, yOffset, destWidth, destHeight, GL_ALPHA, GL_UNSIGNED_BYTE, bitmap.buffer); + /** + * Reset and ignore any OpenGL errors generated by glTexSubImage2D. + */ + caret::GraphicsUtilitiesOpenGL::resetOpenGLError(); + glPopClientAttrib(); } diff --git a/src/Gifti/CMakeLists.txt b/src/Gifti/CMakeLists.txt index df85db0955cd29e82b184d314cd3b9ad1209449c..04a024b61ce71f07972b558807ca30d9dcce6e1a 100644 --- a/src/Gifti/CMakeLists.txt +++ b/src/Gifti/CMakeLists.txt @@ -4,15 +4,16 @@ # PROJECT (Gifti) -# -# Need XML from Qt -# -SET(QT_DONT_USE_QTGUI) - # # Add QT for includes # -INCLUDE (${QT_USE_FILE}) +if(Qt5_FOUND) + include_directories(${Qt5Core_INCLUDE_DIRS}) +endif() +IF (QT4_FOUND) + SET(QT_DONT_USE_QTGUI) + INCLUDE(${QT_USE_FILE}) +ENDIF () # # Create GIFTI Library @@ -39,6 +40,8 @@ GiftiLabelTableSaxReader.cxx GiftiMetaDataSaxReader.cxx ) +TARGET_LINK_LIBRARIES(Gifti ${CARET_QT5_LINK}) + # # Find Headers # diff --git a/src/Gifti/GiftiDataArray.cxx b/src/Gifti/GiftiDataArray.cxx index 067654d98abc9ed957119c92ac795ef7faf9ed1f..a8fb0e34a654c79d53a1fd7a9ab98db84672b5de 100644 --- a/src/Gifti/GiftiDataArray.cxx +++ b/src/Gifti/GiftiDataArray.cxx @@ -222,7 +222,7 @@ GiftiDataArray::getDataTypeAppropriateForIntent(const NiftiIntentEnum::Enum inte } else if ((intent == NiftiIntentEnum::NIFTI_INTENT_RGB_VECTOR) || (intent == NiftiIntentEnum::NIFTI_INTENT_RGBA_VECTOR)) { - dataTypeOut = NiftiDataTypeEnum::NIFTI_TYPE_UINT8; + dataTypeOut = NiftiDataTypeEnum::NIFTI_TYPE_FLOAT32; } else if (intent == NiftiIntentEnum::NIFTI_INTENT_SHAPE) { dataTypeOut = NiftiDataTypeEnum::NIFTI_TYPE_FLOAT32; @@ -681,7 +681,7 @@ GiftiDataArray::readFromText(const AString text, // Decode the Base64 data using VTK's algorithm // unsigned char* dataBuffer = new unsigned char[data.size()]; - //const char* textChars = text.toAscii().constData(); + //const char* textChars = text.toLatin1().constData(); const uint64_t numDecoded = Base64::decode((unsigned char*)text.toStdString().c_str(), data.size(), @@ -1723,13 +1723,21 @@ const FastStatistics* GiftiDataArray::getFastStatistics() const return m_fastStatistics; } -const Histogram* GiftiDataArray::getHistogram() const +const Histogram* GiftiDataArray::getHistogram(const int32_t numberOfBuckets) const { if (this->getDataType() == NiftiDataTypeEnum::NIFTI_TYPE_FLOAT32) { + bool updateHistogramFlag = false; if (m_histogram == NULL) { - m_histogram.grabNew(new Histogram(100)); + m_histogram.grabNew(new Histogram(numberOfBuckets)); + updateHistogramFlag = true; + } + else if (numberOfBuckets != m_histogramNumberOfBuckets) { + updateHistogramFlag = true; + } + if (updateHistogramFlag) { + m_histogram->update(numberOfBuckets, dataPointerFloat, getTotalNumberOfElements()); + m_histogramNumberOfBuckets = numberOfBuckets; } - m_histogram->update(dataPointerFloat, getTotalNumberOfElements()); } return m_histogram; } @@ -1772,23 +1780,43 @@ GiftiDataArray::getDescriptiveStatistics(const float mostPositiveValueInclusive, return this->descriptiveStatisticsLimitedValues; } -const Histogram* GiftiDataArray::getHistogram(const float mostPositiveValueInclusive, +const Histogram* GiftiDataArray::getHistogram(const int32_t numberOfBuckets, + const float mostPositiveValueInclusive, const float leastPositiveValueInclusive, const float leastNegativeValueInclusive, const float mostNegativeValueInclusive, const bool includeZeroValues) const { if (this->getDataType() == NiftiDataTypeEnum::NIFTI_TYPE_FLOAT32) { + bool updateHistogramFlag = false; if (m_histogramLimitedValues == NULL) { - m_histogramLimitedValues.grabNew(new Histogram(100)); + m_histogramLimitedValues.grabNew(new Histogram(numberOfBuckets)); + updateHistogramFlag = true; + } + else if ((numberOfBuckets != m_histogramLimitedValuesNumberOfBuckets) + || (mostPositiveValueInclusive != m_histogramLimitedValuesMostPositiveValueInclusive) + || (leastPositiveValueInclusive != m_histogramLimitedValuesLeastPositiveValueInclusive) + || (leastNegativeValueInclusive != m_histogramLimitedValuesLeastNegativeValueInclusive) + || (mostNegativeValueInclusive != m_histogramLimitedValuesMostNegativeValueInclusive) + || (includeZeroValues != m_histogramLimitedValuesIncludeZeroValues)) { + updateHistogramFlag = true; + } + if (updateHistogramFlag) { + m_histogramLimitedValues->update(numberOfBuckets, + dataPointerFloat, getTotalNumberOfElements(), + mostPositiveValueInclusive, + leastPositiveValueInclusive, + leastNegativeValueInclusive, + mostNegativeValueInclusive, + includeZeroValues); + m_histogramLimitedValuesNumberOfBuckets = numberOfBuckets; + m_histogramLimitedValuesMostPositiveValueInclusive = mostPositiveValueInclusive; + m_histogramLimitedValuesLeastPositiveValueInclusive = leastPositiveValueInclusive; + m_histogramLimitedValuesLeastNegativeValueInclusive = leastNegativeValueInclusive; + m_histogramLimitedValuesMostNegativeValueInclusive = mostNegativeValueInclusive; + m_histogramLimitedValuesIncludeZeroValues = includeZeroValues; } - m_histogramLimitedValues->update(dataPointerFloat, getTotalNumberOfElements(), - mostPositiveValueInclusive, - leastPositiveValueInclusive, - leastNegativeValueInclusive, - mostNegativeValueInclusive, - includeZeroValues); } return m_histogramLimitedValues; } diff --git a/src/Gifti/GiftiDataArray.h b/src/Gifti/GiftiDataArray.h index 8666c01a24f83f130c5b5148435960b0b7416c71..e6898e4d305d94afa9a178a106e65f0d25916be5 100644 --- a/src/Gifti/GiftiDataArray.h +++ b/src/Gifti/GiftiDataArray.h @@ -289,7 +289,7 @@ namespace caret { const FastStatistics* getFastStatistics() const; - const Histogram* getHistogram() const; + const Histogram* getHistogram(const int32_t numberOfBuckets) const; const DescriptiveStatistics* getDescriptiveStatistics(const float mostPositiveValueInclusive, const float leastPositiveValueInclusive, @@ -297,7 +297,8 @@ namespace caret { const float mostNegativeValueInclusive, const bool includeZeroValues) const; - const Histogram* getHistogram(const float mostPositiveValueInclusive, + const Histogram* getHistogram(const int32_t numberOfBuckets, + const float mostPositiveValueInclusive, const float leastPositiveValueInclusive, const float leastNegativeValueInclusive, const float mostNegativeValueInclusive, @@ -410,11 +411,19 @@ namespace caret { mutable CaretPointer m_fastStatistics; mutable CaretPointer m_histogram; + mutable int32_t m_histogramNumberOfBuckets = 100; + + mutable CaretPointer m_histogramLimitedValues; + mutable int32_t m_histogramLimitedValuesNumberOfBuckets = 100; + mutable float m_histogramLimitedValuesMostPositiveValueInclusive; + mutable float m_histogramLimitedValuesLeastPositiveValueInclusive; + mutable float m_histogramLimitedValuesLeastNegativeValueInclusive; + mutable float m_histogramLimitedValuesMostNegativeValueInclusive; + mutable bool m_histogramLimitedValuesIncludeZeroValues; /// statistics about data (DO NOT COPY) mutable DescriptiveStatistics* descriptiveStatisticsLimitedValues; - mutable CaretPointer m_histogramLimitedValues; bool modifiedFlag; // DO NOT COPY // ***** BE SURE TO UPDATE copyHelper() if elements are added ****** diff --git a/src/Gifti/GiftiFile.cxx b/src/Gifti/GiftiFile.cxx index 7a4e281f3fd46595acc663ea085345a45d092b7a..84a7e8f8252ea7b376bda693c3b575ac8c5e39cb 100644 --- a/src/Gifti/GiftiFile.cxx +++ b/src/Gifti/GiftiFile.cxx @@ -856,9 +856,9 @@ GiftiFile::readFile(const AString& filename) int colNum = e.getColumnNumber(); std::ostringstream str; - str << "Parse error while reading"; + str << "File is not a valid GIFTI file"; if ((lineNum >= 0) && (colNum >= 0)) { - str << ", line/col (" + str << ", error parsing at line/col (" << e.getLineNumber() << "/" << e.getColumnNumber() @@ -994,7 +994,7 @@ GiftiFile::writeFile(const AString& filename) std::ofstream* externalBinaryOutputStream = NULL; if (encoding == GiftiDataArray::ENCODING_EXTERNAL_FILE_BINARY) { externalBinaryFileName = getFileNameNoPath() + ".data"; - externalBinaryOutputStream = new std::ofstream(externalBinaryFileName.toAscii().constData(), + externalBinaryOutputStream = new std::ofstream(externalBinaryFileName.toLatin1().constData(), std::ofstream::binary); if (externalBinaryOutputStream == NULL) { throw GiftiException("Unable to open " + externalBinaryFileName + " for output."); diff --git a/src/Graphics/CMakeLists.txt b/src/Graphics/CMakeLists.txt new file mode 100644 index 0000000000000000000000000000000000000000..53cac7b7a50f2a7cb4a4d983989f90b997676136 --- /dev/null +++ b/src/Graphics/CMakeLists.txt @@ -0,0 +1,64 @@ +# +# Name of Project +# +PROJECT (Graphics) + +# +# Create a Library +# +ADD_LIBRARY(Graphics +CaretOpenGLInclude.h +EventGraphicsOpenGLCreateBufferObject.h +EventGraphicsOpenGLCreateTextureName.h +EventGraphicsOpenGLDeleteBufferObject.h +EventGraphicsOpenGLDeleteTextureName.h +EventOpenGLObjectToWindowTransform.h +GraphicsEngineData.h +GraphicsEngineDataOpenGL.h +GraphicsOpenGLBufferObject.h +GraphicsOpenGLError.h +GraphicsOpenGLPolylineTriangles.h +GraphicsOpenGLTextureName.h +GraphicsPrimitive.h +GraphicsPrimitiveSelectionHelper.h +GraphicsPrimitiveV3f.h +GraphicsPrimitiveV3fC4f.h +GraphicsPrimitiveV3fC4ub.h +GraphicsPrimitiveV3fN3f.h +GraphicsPrimitiveV3fT3f.h +GraphicsShape.h +GraphicsUtilitiesOpenGL.h + +EventGraphicsOpenGLCreateBufferObject.cxx +EventGraphicsOpenGLCreateTextureName.cxx +EventGraphicsOpenGLDeleteBufferObject.cxx +EventGraphicsOpenGLDeleteTextureName.cxx +EventOpenGLObjectToWindowTransform.cxx +GraphicsEngineData.cxx +GraphicsEngineDataOpenGL.cxx +GraphicsOpenGLBufferObject.cxx +GraphicsOpenGLError.cxx +GraphicsOpenGLPolylineTriangles.cxx +GraphicsOpenGLTextureName.cxx +GraphicsPrimitive.cxx +GraphicsPrimitiveSelectionHelper.cxx +GraphicsPrimitiveV3f.cxx +GraphicsPrimitiveV3fC4f.cxx +GraphicsPrimitiveV3fC4ub.cxx +GraphicsPrimitiveV3fN3f.cxx +GraphicsPrimitiveV3fT3f.cxx +GraphicsShape.cxx +GraphicsUtilitiesOpenGL.cxx +) + +# +# Find headers +# +INCLUDE_DIRECTORIES( +${CMAKE_SOURCE_DIR}/Common +${CMAKE_SOURCE_DIR}/Graphics +${CMAKE_SOURCE_DIR}/Nifti +) + +TARGET_LINK_LIBRARIES(Graphics ${CARET_QT5_LINK}) + diff --git a/src/Graphics/CaretOpenGLInclude.h b/src/Graphics/CaretOpenGLInclude.h new file mode 100644 index 0000000000000000000000000000000000000000..16536c203e3e3b6f049c95a93c22c85586b9bbec --- /dev/null +++ b/src/Graphics/CaretOpenGLInclude.h @@ -0,0 +1,67 @@ +#ifndef __CARET_OPEN_GL_INCLUDE_H__ +#define __CARET_OPEN_GL_INCLUDE_H__ + +/*LICENSE_START*/ +/* + * Copyright (C) 2014 Washington University School of Medicine + * + * 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 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ +/*LICENSE_END*/ + +/** + * \file CaretOpenGLInclude.h + * \brief Contains OpenGL include declarations that work for all operating systems. + * \ingroup Common + */ + +/* + * When using GLEW, GL/glew.h MUST be included before Gl/gl.h. + * Note: Windows.h includes Gl/gl.h. + * + * Must define GLEW_STATIC when GLEW is in a static library + * or linking errors will occur with GLEW functions + */ +#ifdef HAVE_GLEW + #ifdef HAVE_GLEW_STATIC_LIB + #define GLEW_STATIC + #endif + + #include +#endif + +#ifdef CARET_OS_WINDOWS + #include +#endif + +#ifdef CARET_OS_MACOSX + #include + #include +#else + #ifndef HAVE_GLEW + #ifdef CARET_OS_LINUX + /* + * On linux, adds support for newer OpenGL + * as described at https://www.khronos.org/registry/OpenGL/ABI/ + */ + #define GL_GLEXT_PROTOTYPES + #endif + #endif + + #include + #include +#endif + +#endif //__CARET_OPEN_GL_INCLUDE_H__ diff --git a/src/Graphics/EventGraphicsOpenGLCreateBufferObject.cxx b/src/Graphics/EventGraphicsOpenGLCreateBufferObject.cxx new file mode 100644 index 0000000000000000000000000000000000000000..db65fe6efbd2b3562b488b5df0448f15e4c9b12b --- /dev/null +++ b/src/Graphics/EventGraphicsOpenGLCreateBufferObject.cxx @@ -0,0 +1,102 @@ + +/*LICENSE_START*/ +/* + * Copyright (C) 2017 Washington University School of Medicine + * + * 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 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ +/*LICENSE_END*/ + +#define __EVENT_GRAPHICS_OPEN_G_L_CREATE_BUFFER_OBJECT_DECLARE__ +#include "EventGraphicsOpenGLCreateBufferObject.h" +#undef __EVENT_GRAPHICS_OPEN_G_L_CREATE_BUFFER_OBJECT_DECLARE__ + +#include "CaretAssert.h" +#include "CaretLogger.h" +#include "EventGraphicsOpenGLDeleteBufferObject.h" +#include "EventManager.h" +#include "EventTypeEnum.h" + +using namespace caret; + + + +/** + * \class caret::EventGraphicsOpenGLCreateBufferObject + * \brief Create an OpenGL Buffer Object + * \ingroup Graphics + * + * Creates an OpenGL Buffer Object for the current context. + * There MUST BE an active OpenGL context when this event + * is processed. Otherwise, a buffer object cannot be + * created and a program failure will likely occur. + */ + +/** + * Constructor. + */ +EventGraphicsOpenGLCreateBufferObject::EventGraphicsOpenGLCreateBufferObject() +: Event(EventTypeEnum::EVENT_GRAPHICS_OPENGL_CREATE_BUFFER_OBJECT) +{ + +} + +/** + * Destructor. + */ +EventGraphicsOpenGLCreateBufferObject::~EventGraphicsOpenGLCreateBufferObject() +{ + if (m_openglBufferObject != NULL) { + /* + * If the caller did not get the buffer object we need to delete it. + */ + CaretLogSevere("Create OpenGL Buffer Object Event never had buffer object retrieved. Will attempt to delete it."); + EventGraphicsOpenGLDeleteBufferObject deleteEvent(m_openglBufferObject); + EventManager::get()->sendEvent(deleteEvent.getPointer()); + } +} + +/** + * Set the OpenGL Buffer Object. + * + * @param openglBufferObject + * The OpenGL Buffer Object. This instance will take ownership of the buffer so the caller + * MUST NOT ever reference the buffer object after calling this method. + */ +void +EventGraphicsOpenGLCreateBufferObject::setOpenGLBufferObject(GraphicsOpenGLBufferObject* openglBufferObject) +{ + m_openglBufferObject = openglBufferObject; +} + +/** + * @return OpenGL Buffer Object that was created or NULL if + * unable to create buffer object due to no valid context. + * Caller takes ownership of the buffer object. If this method + * is called more than once for an instance, NULL will be + * returned the second and all subsequent times. + */ +GraphicsOpenGLBufferObject* +EventGraphicsOpenGLCreateBufferObject::getOpenGLBufferObject() const +{ + /* + * Do not let buffer object be retrieved more than once. + */ + GraphicsOpenGLBufferObject* bufferObjectPointer = m_openglBufferObject; + m_openglBufferObject = NULL; + + return bufferObjectPointer; +} + diff --git a/src/Graphics/EventGraphicsOpenGLCreateBufferObject.h b/src/Graphics/EventGraphicsOpenGLCreateBufferObject.h new file mode 100644 index 0000000000000000000000000000000000000000..127f5333623788a8ae98319da86d2bf3074fb0f4 --- /dev/null +++ b/src/Graphics/EventGraphicsOpenGLCreateBufferObject.h @@ -0,0 +1,64 @@ +#ifndef __EVENT_GRAPHICS_OPEN_G_L_CREATE_BUFFER_OBJECT_H__ +#define __EVENT_GRAPHICS_OPEN_G_L_CREATE_BUFFER_OBJECT_H__ + +/*LICENSE_START*/ +/* + * Copyright (C) 2017 Washington University School of Medicine + * + * 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 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ +/*LICENSE_END*/ + + +#include "Event.h" + + + +namespace caret { + + class GraphicsOpenGLBufferObject; + + class EventGraphicsOpenGLCreateBufferObject : public Event { + + public: + EventGraphicsOpenGLCreateBufferObject(); + + virtual ~EventGraphicsOpenGLCreateBufferObject(); + + void setOpenGLBufferObject(GraphicsOpenGLBufferObject* openglBufferObject); + + GraphicsOpenGLBufferObject* getOpenGLBufferObject() const; + + // ADD_NEW_METHODS_HERE + + private: + EventGraphicsOpenGLCreateBufferObject(const EventGraphicsOpenGLCreateBufferObject&); + + EventGraphicsOpenGLCreateBufferObject& operator=(const EventGraphicsOpenGLCreateBufferObject&); + + mutable GraphicsOpenGLBufferObject* m_openglBufferObject = NULL; + + mutable bool m_openglBufferObjectWasTakenFlag = false; + + // ADD_NEW_MEMBERS_HERE + + }; + +#ifdef __EVENT_GRAPHICS_OPEN_G_L_CREATE_BUFFER_OBJECT_DECLARE__ + // +#endif // __EVENT_GRAPHICS_OPEN_G_L_CREATE_BUFFER_OBJECT_DECLARE__ + +} // namespace +#endif //__EVENT_GRAPHICS_OPEN_G_L_CREATE_BUFFER_OBJECT_H__ diff --git a/src/Graphics/EventGraphicsOpenGLCreateTextureName.cxx b/src/Graphics/EventGraphicsOpenGLCreateTextureName.cxx new file mode 100644 index 0000000000000000000000000000000000000000..03ce073c860c718b1d21783a76649c267e0bf1ad --- /dev/null +++ b/src/Graphics/EventGraphicsOpenGLCreateTextureName.cxx @@ -0,0 +1,102 @@ + +/*LICENSE_START*/ +/* + * Copyright (C) 2017 Washington University School of Medicine + * + * 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 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ +/*LICENSE_END*/ + +#define __EVENT_GRAPHICS_OPEN_G_L_CREATE_TEXTURE_NAME_DECLARE__ +#include "EventGraphicsOpenGLCreateTextureName.h" +#undef __EVENT_GRAPHICS_OPEN_G_L_CREATE_TEXTURE_NAME_DECLARE__ + +#include "CaretAssert.h" +#include "CaretLogger.h" +#include "EventGraphicsOpenGLDeleteTextureName.h" +#include "EventManager.h" +#include "EventTypeEnum.h" + +using namespace caret; + + + +/** + * \class caret::EventGraphicsOpenGLCreateTextureName + * \brief Create an OpenGL Texture Name + * \ingroup Graphics + * + * Creates an OpenGL Texture Name for the current context. + * There MUST BE an active OpenGL context when this event + * is processed. Otherwise, a texture name cannot be + * created and a program failure will likely occur. + */ + +/** + * Constructor. + */ +EventGraphicsOpenGLCreateTextureName::EventGraphicsOpenGLCreateTextureName() +: Event(EventTypeEnum::EVENT_GRAPHICS_OPENGL_CREATE_TEXTURE_NAME) +{ + +} + +/** + * Destructor. + */ +EventGraphicsOpenGLCreateTextureName::~EventGraphicsOpenGLCreateTextureName() +{ + if (m_openglTextureName != NULL) { + /* + * If the caller did not get the texture name we need to delete it. + */ + CaretLogSevere("Create OpenGL Texture Name Event never had texture name retrieved. Will attempt to delete it."); + EventGraphicsOpenGLDeleteTextureName deleteEvent(m_openglTextureName); + EventManager::get()->sendEvent(deleteEvent.getPointer()); + } +} + +/** + * Set the OpenGL Texture Name + * + * @param openglTextureName + * The OpenGL Texture Name. This instance will take ownership of the texture so the caller + * MUST NOT ever reference the texture name after calling this method. + */ +void +EventGraphicsOpenGLCreateTextureName::setOpenGLTextureName(GraphicsOpenGLTextureName* openglTextureName) +{ + m_openglTextureName = openglTextureName; +} + +/** + * @return OpenGL Texture Name that was created or NULL if + * unable to create texture name due to no valid context. + * Caller takes ownership of the texture name. If this method + * is called more than once for an instance, NULL will be + * returned the second and all subsequent times. + */ +GraphicsOpenGLTextureName* +EventGraphicsOpenGLCreateTextureName::getOpenGLTextureName() const +{ + /* + * Do not let texture name be retrieved more than once. + */ + GraphicsOpenGLTextureName* textureNamePointer = m_openglTextureName; + m_openglTextureName = NULL; + + return textureNamePointer; +} + diff --git a/src/Graphics/EventGraphicsOpenGLCreateTextureName.h b/src/Graphics/EventGraphicsOpenGLCreateTextureName.h new file mode 100644 index 0000000000000000000000000000000000000000..422303378875cdd918de080cbad347bf3e0e7b2c --- /dev/null +++ b/src/Graphics/EventGraphicsOpenGLCreateTextureName.h @@ -0,0 +1,64 @@ +#ifndef __EVENT_GRAPHICS_OPEN_G_L_CREATE_TEXTURE_NAME_H__ +#define __EVENT_GRAPHICS_OPEN_G_L_CREATE_TEXTURE_NAME_H__ + +/*LICENSE_START*/ +/* + * Copyright (C) 2017 Washington University School of Medicine + * + * 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 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ +/*LICENSE_END*/ + + +#include "Event.h" + + + +namespace caret { + + class GraphicsOpenGLTextureName; + + class EventGraphicsOpenGLCreateTextureName : public Event { + + public: + EventGraphicsOpenGLCreateTextureName(); + + virtual ~EventGraphicsOpenGLCreateTextureName(); + + void setOpenGLTextureName(GraphicsOpenGLTextureName* openglTextureName); + + GraphicsOpenGLTextureName* getOpenGLTextureName() const; + + // ADD_NEW_METHODS_HERE + + private: + EventGraphicsOpenGLCreateTextureName(const EventGraphicsOpenGLCreateTextureName&); + + EventGraphicsOpenGLCreateTextureName& operator=(const EventGraphicsOpenGLCreateTextureName&); + + mutable GraphicsOpenGLTextureName* m_openglTextureName = NULL; + + mutable bool m_openglTextureNameWasTakenFlag = false; + + // ADD_NEW_MEMBERS_HERE + + }; + +#ifdef __EVENT_GRAPHICS_OPEN_G_L_CREATE_TEXTURE_NAME_DECLARE__ + // +#endif // __EVENT_GRAPHICS_OPEN_G_L_CREATE_TEXTURE_NAME_DECLARE__ + +} // namespace +#endif //__EVENT_GRAPHICS_OPEN_G_L_CREATE_TEXTURE_NAME_H__ diff --git a/src/Graphics/EventGraphicsOpenGLDeleteBufferObject.cxx b/src/Graphics/EventGraphicsOpenGLDeleteBufferObject.cxx new file mode 100644 index 0000000000000000000000000000000000000000..a28ae6c2f8f41f93ae6115807dd3acf67a558ed7 --- /dev/null +++ b/src/Graphics/EventGraphicsOpenGLDeleteBufferObject.cxx @@ -0,0 +1,98 @@ + +/*LICENSE_START*/ +/* + * Copyright (C) 2017 Washington University School of Medicine + * + * 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 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ +/*LICENSE_END*/ + +#define __EVENT_GRAPHICS_OPEN_G_L_DELETE_BUFFER_OBJECT_DECLARE__ +#include "EventGraphicsOpenGLDeleteBufferObject.h" +#undef __EVENT_GRAPHICS_OPEN_G_L_DELETE_BUFFER_OBJECT_DECLARE__ + +#include "ApplicationInformation.h" +#include "CaretAssert.h" +#include "CaretLogger.h" +#include "EventTypeEnum.h" +#include "GraphicsOpenGLBufferObject.h" + +using namespace caret; + + + +/** + * \class caret::EventGraphicsOpenGLDeleteBufferObject + * \brief Delete an OpenGL Buffer Object + * \ingroup Graphics + * + * Deletes an OpenGL Buffer Object for the current context. + */ +/** + * Constructor. + * + * @param openglBufferObject + * The OpenGL Buffer Object whose buffer name is deleted. + * After this event the openglBufferObject should be deleted by the caller. + */ +EventGraphicsOpenGLDeleteBufferObject::EventGraphicsOpenGLDeleteBufferObject(const GraphicsOpenGLBufferObject* openglBufferObject) +: Event(EventTypeEnum::EVENT_GRAPHICS_OPENGL_DELETE_BUFFER_OBJECT), +m_openglBufferObject(openglBufferObject) +{ + +} + +/** + * Destructor. + */ +EventGraphicsOpenGLDeleteBufferObject::~EventGraphicsOpenGLDeleteBufferObject() +{ + switch (ApplicationInformation::getApplicationType()) { + case ApplicationTypeEnum::APPLICATION_TYPE_COMMAND_LINE: + /* + * Do not test for deletion of OpenGL buffers with + * the command line program as will require redesign + * of the -show-scene command and would likely require + * loading of the scene for each window. + */ + break; + case ApplicationTypeEnum::APPLICATION_TYPE_GRAPHICAL_USER_INTERFACE: + /* + * Only perform this test for the GUI application. + */ + if (this->getEventProcessCount() <= 0) { + CaretLogSevere("Deletion of OpenGL Buffer Object Name=" + + AString::number(m_openglBufferObject->getBufferObjectName()) + + ", context pointer=" + + AString::number((qulonglong)m_openglBufferObject->getOpenGLContextPointer()) + + " appears to have failed. The events processed count is zero and that " + "indicates that the event was not received in any listener or the " + "listener failed to set the event as processed."); + } + break; + case ApplicationTypeEnum::APPLICATION_TYPE_INVALID: + break; + } +} + +/** + * @return OpenGL Buffer Object that was deleted + */ +const GraphicsOpenGLBufferObject* +EventGraphicsOpenGLDeleteBufferObject::getOpenGLBufferObject() const +{ + return m_openglBufferObject; +} + diff --git a/src/Graphics/EventGraphicsOpenGLDeleteBufferObject.h b/src/Graphics/EventGraphicsOpenGLDeleteBufferObject.h new file mode 100644 index 0000000000000000000000000000000000000000..9efb4cdeeb123bb809471e375539bf24db5b643e --- /dev/null +++ b/src/Graphics/EventGraphicsOpenGLDeleteBufferObject.h @@ -0,0 +1,60 @@ +#ifndef __EVENT_GRAPHICS_OPEN_G_L_DELETE_BUFFER_OBJECT_H__ +#define __EVENT_GRAPHICS_OPEN_G_L_DELETE_BUFFER_OBJECT_H__ + +/*LICENSE_START*/ +/* + * Copyright (C) 2017 Washington University School of Medicine + * + * 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 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ +/*LICENSE_END*/ + + +#include "Event.h" + + + +namespace caret { + + class GraphicsOpenGLBufferObject; + + class EventGraphicsOpenGLDeleteBufferObject : public Event { + + public: + EventGraphicsOpenGLDeleteBufferObject(const GraphicsOpenGLBufferObject* openglBufferObject); + + virtual ~EventGraphicsOpenGLDeleteBufferObject(); + + const GraphicsOpenGLBufferObject* getOpenGLBufferObject() const; + + // ADD_NEW_METHODS_HERE + + private: + EventGraphicsOpenGLDeleteBufferObject(const EventGraphicsOpenGLDeleteBufferObject&); + + EventGraphicsOpenGLDeleteBufferObject& operator=(const EventGraphicsOpenGLDeleteBufferObject&); + + const GraphicsOpenGLBufferObject* m_openglBufferObject = NULL; + + // ADD_NEW_MEMBERS_HERE + + }; + +#ifdef __EVENT_GRAPHICS_OPEN_G_L_DELETE_BUFFER_OBJECT_DECLARE__ + // +#endif // __EVENT_GRAPHICS_OPEN_G_L_DELETE_BUFFER_OBJECT_DECLARE__ + +} // namespace +#endif //__EVENT_GRAPHICS_OPEN_G_L_DELETE_BUFFER_OBJECT_H__ diff --git a/src/Graphics/EventGraphicsOpenGLDeleteTextureName.cxx b/src/Graphics/EventGraphicsOpenGLDeleteTextureName.cxx new file mode 100644 index 0000000000000000000000000000000000000000..93c08011440bd68b8e4110b0d64301a06ccc622b --- /dev/null +++ b/src/Graphics/EventGraphicsOpenGLDeleteTextureName.cxx @@ -0,0 +1,80 @@ + +/*LICENSE_START*/ +/* + * Copyright (C) 2017 Washington University School of Medicine + * + * 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 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ +/*LICENSE_END*/ + +#define __EVENT_GRAPHICS_OPEN_G_L_DELETE_TEXTURE_NAME_DECLARE__ +#include "EventGraphicsOpenGLDeleteTextureName.h" +#undef __EVENT_GRAPHICS_OPEN_G_L_DELETE_TEXTURE_NAME_DECLARE__ + +#include "CaretAssert.h" +#include "CaretLogger.h" +#include "EventTypeEnum.h" +#include "GraphicsOpenGLTextureName.h" + +using namespace caret; + + + +/** + * \class caret::EventGraphicsOpenGLDeleteTextureName + * \brief Delete an OpenGL Texture Name + * \ingroup Graphics + * + * Deletes an OpenGL Texture Name for the current context. + */ +/** + * Constructor. + * + * @param openglTextureName + * The OpenGL Texture Name whose texture name is deleted. + * After this event the openglTextureName should be deleted by the caller. + */ +EventGraphicsOpenGLDeleteTextureName::EventGraphicsOpenGLDeleteTextureName(const GraphicsOpenGLTextureName* openglTextureName) +: Event(EventTypeEnum::EVENT_GRAPHICS_OPENGL_DELETE_TEXTURE_NAME), +m_openglTextureName(openglTextureName) +{ + +} + +/** + * Destructor. + */ +EventGraphicsOpenGLDeleteTextureName::~EventGraphicsOpenGLDeleteTextureName() +{ + if (this->getEventProcessCount() <= 0) { + CaretLogSevere("Deletion of OpenGL Texture Name=" + + AString::number(m_openglTextureName->getTextureName()) + + ", context pointer=" + + AString::number((qulonglong)m_openglTextureName->getOpenGLContextPointer()) + + " appears to have failed. The events processed count is zero and that " + "indicates that the event was not received in any listener or the " + "listener failed to set the event as processed."); + } +} + +/** + * @return OpenGL Texture Name that was deleted + */ +const GraphicsOpenGLTextureName* +EventGraphicsOpenGLDeleteTextureName::getOpenGLTextureName() const +{ + return m_openglTextureName; +} + diff --git a/src/Graphics/EventGraphicsOpenGLDeleteTextureName.h b/src/Graphics/EventGraphicsOpenGLDeleteTextureName.h new file mode 100644 index 0000000000000000000000000000000000000000..a13ae67e404a7d99238bfdf9ef59ac03da140e1e --- /dev/null +++ b/src/Graphics/EventGraphicsOpenGLDeleteTextureName.h @@ -0,0 +1,60 @@ +#ifndef __EVENT_GRAPHICS_OPEN_G_L_DELETE_TEXTURE_NAME_H__ +#define __EVENT_GRAPHICS_OPEN_G_L_DELETE_TEXTURE_NAME_H__ + +/*LICENSE_START*/ +/* + * Copyright (C) 2017 Washington University School of Medicine + * + * 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 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ +/*LICENSE_END*/ + + +#include "Event.h" + + + +namespace caret { + + class GraphicsOpenGLTextureName; + + class EventGraphicsOpenGLDeleteTextureName : public Event { + + public: + EventGraphicsOpenGLDeleteTextureName(const GraphicsOpenGLTextureName* openglTextureName); + + virtual ~EventGraphicsOpenGLDeleteTextureName(); + + const GraphicsOpenGLTextureName* getOpenGLTextureName() const; + + // ADD_NEW_METHODS_HERE + + private: + EventGraphicsOpenGLDeleteTextureName(const EventGraphicsOpenGLDeleteTextureName&); + + EventGraphicsOpenGLDeleteTextureName& operator=(const EventGraphicsOpenGLDeleteTextureName&); + + const GraphicsOpenGLTextureName* m_openglTextureName = NULL; + + // ADD_NEW_MEMBERS_HERE + + }; + +#ifdef __EVENT_GRAPHICS_OPEN_G_L_DELETE_TEXTURE_NAME_DECLARE__ + // +#endif // __EVENT_GRAPHICS_OPEN_G_L_DELETE_TEXTURE_NAME_DECLARE__ + +} // namespace +#endif //__EVENT_GRAPHICS_OPEN_G_L_DELETE_TEXTURE_NAME_H__ diff --git a/src/Graphics/EventOpenGLObjectToWindowTransform.cxx b/src/Graphics/EventOpenGLObjectToWindowTransform.cxx new file mode 100644 index 0000000000000000000000000000000000000000..3b26b1be762d6cbcbbffa60a0f8bd51fff846787 --- /dev/null +++ b/src/Graphics/EventOpenGLObjectToWindowTransform.cxx @@ -0,0 +1,290 @@ + +/*LICENSE_START*/ +/* + * Copyright (C) 2017 Washington University School of Medicine + * + * 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 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ +/*LICENSE_END*/ + +#define __EVENT_OPEN_G_L_OBJECT_TO_WINDOW_TRANSFORM_DECLARE__ +#include "EventOpenGLObjectToWindowTransform.h" +#undef __EVENT_OPEN_G_L_OBJECT_TO_WINDOW_TRANSFORM_DECLARE__ + +#include + +#include "CaretAssert.h" +#include "CaretLogger.h" +#include "EventTypeEnum.h" + +using namespace caret; + + + +/** + * \class caret::EventOpenGLObjectToWindowTransform + * \brief Get a transform for converting OpenGL Object to Window Coordinates + * \ingroup Graphics + * + * Creates a transform (matrix) that is used to convert + * object coordinates to window coordinates. + * There must be a valid OpenGL context that is "current". + */ + +/** + * Constructor. + * + * @param spaceType + * Type of space of objects being transformed + */ +EventOpenGLObjectToWindowTransform::EventOpenGLObjectToWindowTransform(const SpaceType spaceType) +: Event(EventTypeEnum::EVENT_OPENGL_OBJECT_TO_WINDOW_TRANSFORM), +m_spaceType(spaceType) +{ + +} + +/** + * Destructor. + */ +EventOpenGLObjectToWindowTransform::~EventOpenGLObjectToWindowTransform() +{ +} + +/** + * @return The object to window matrix. + */ +Matrix4x4 +EventOpenGLObjectToWindowTransform::getMatrix() const +{ + return m_transformMatrix; +} + + +/** + * @return Is the data for performing transformations valid? + */ +bool +EventOpenGLObjectToWindowTransform::isValid() const +{ + return m_validFlag; +} + +/** + * Transform a window coordinate to an object coordinate. + * + * @param windowXYZ + * The window coordinate. + * @param objectXYZOut + * Output containing the computed object coordinate. + * @return + * True if output coordinate is valid, else false. + */ +bool +EventOpenGLObjectToWindowTransform::inverseTransformPoint(const float windowXYZ[3], + float objectXYZOut[3]) const +{ + if ( ! m_validFlag) { + CaretAssert(0); + CaretLogSevere("Program Error: EventOpenGLObjectToWindowTransform is not valid"); + return false; + } + + /* + * If needed, create the inverse matrix + */ + if ( ! m_inverseTransformMatrix) { + m_inverseTransformMatrix.reset(new Matrix4x4(m_transformMatrix)); + + if ( ! m_inverseTransformMatrix->invert()) { + CaretAssert(0); + CaretLogSevere("Program Error: EventOpenGLObjectToWindowTransform unable to invert matrix"); + return false; + } + } + + float xyzw[4] { + (2.0f * (windowXYZ[0] - m_viewport[0]) / m_viewport[2]) - 1.0f, + (2.0f * (windowXYZ[1] - m_viewport[1]) / m_viewport[3]) - 1.0f, + (2.0f * windowXYZ[2]) - 1.0f, + 1.0f + }; + + m_inverseTransformMatrix->multiplyPoint4(xyzw); + + objectXYZOut[0] = xyzw[0]; + objectXYZOut[1] = xyzw[1]; + objectXYZOut[2] = xyzw[2]; + + return true; +} + +/** + * Transform an object coordinate to a window coordinate. + * + * @param objectXYZ + * The object's coordinate. + * @param windowXYZOut + * Output containing the computed window coordinate. + * @return + * True if output coordinate is valid, else false. + */ +bool +EventOpenGLObjectToWindowTransform::transformPoint(const float objectXYZ[3], + float windowXYZOut[3]) const +{ + if ( ! m_validFlag) { + CaretAssert(0); + CaretLogSevere("Program Error: EventOpenGLObjectToWindowTransform is not valid"); + return false; + } + + float xyzw[4] = { objectXYZ[0], objectXYZ[1], objectXYZ[2], 1.0f }; + m_transformMatrix.multiplyPoint4(xyzw); + windowXYZOut[0] = m_viewport[0] + (m_viewport[2] * ((xyzw[0] + 1.0f) / 2.0f)); + windowXYZOut[1] = m_viewport[1] + (m_viewport[3] * ((xyzw[1] + 1.0f) / 2.0f)); + windowXYZOut[2] = (xyzw[2] + 1.0f) / 2.0f; + + /* + * From OpenGL Programming Guide 3rd Ed, p 133: + * + * If the near value is 1.0 and the far value is 3.0, + * objects must have z-coordinates between -1.0 and -3.0 in order to be visible. + * So, negate the Z-value to be negative. + */ + const float windowZ = windowXYZOut[2]; + windowXYZOut[2] = -windowXYZOut[2]; + + bool modelSpaceFlag = false; + bool volumeSpaceFlag = false; + switch (m_spaceType) { + case SpaceType::MODEL: + modelSpaceFlag = true; + break; + case SpaceType::VOLUME_SLICE_MODEL: + modelSpaceFlag = true; + volumeSpaceFlag = true; + break; + case SpaceType::WINDOW: + break; + } + /* + * If window space, we DO NOT need to offset for the 'eye' location + * Even when we do, the offset for the eye is very small + */ + if (modelSpaceFlag) { + if ((m_projectionMatrixArray[0] != 0.0) + && (m_projectionMatrixArray[5] != 0.0) + && (m_projectionMatrixArray[10] != 0.0)) { + /* + * From http://lektiondestages.blogspot.com/2013/11/decompose-opengl-projection-matrix.html + */ + float nearValue = (1.0f + m_projectionMatrixArray[14]) / m_projectionMatrixArray[10]; + float farValue = -(1.0f - m_projectionMatrixArray[14]) / m_projectionMatrixArray[10]; + float left = -(1.0f + m_projectionMatrixArray[12]) / m_projectionMatrixArray[0]; + float right = (1.0f - m_projectionMatrixArray[12]) / m_projectionMatrixArray[0]; + + /* + * Depending upon view, near may be positive and far negative + */ + const float farNearRange = std::fabs(farValue - nearValue); + if ((m_centerToEyeDistance > 0.0) + && (farNearRange > 0.0)) { + /* + * Using gluLookAt moves the eye away from the center which + * causes the window Z to also move. Thus, we need to remove + * this amount from the window's Z-coordinate. + */ + const float eyeAdjustment = (m_centerToEyeDistance / farNearRange); + if (volumeSpaceFlag) { + windowXYZOut[2] = -(windowZ - eyeAdjustment); + } + else if (left > right) { + windowXYZOut[2] = -(windowZ - eyeAdjustment); + } + else { + windowXYZOut[2] = -(windowZ + eyeAdjustment); + } + } + } + } + + return true; +} + +/** + * Setup the transformation's data. + * + * @param modelviewMatrixArray + * The OpenGL modelview matrix as an array. + * @param projectionMatrixArray + * The OpenGL projection matrix as an array. + * @param viewport + * The OpenGL Viewport (x, y, w, h) + * @param depthRange + * The depth range (near, far) + * @param centerToEyeDistance + * Distance from object space center to the viewer's eye along Z-axis. + */ +void +EventOpenGLObjectToWindowTransform::setup(const std::array& modelviewMatrixArray, + const std::array& projectionMatrixArray, + const std::array& viewport, + const std::array& depthRange, + const double centerToEyeDistance) +{ + m_projectionMatrixArray = projectionMatrixArray; + m_viewport = viewport; + m_depthRange = depthRange; + m_centerToEyeDistance = centerToEyeDistance; + + Matrix4x4 modelviewMatrix; + modelviewMatrix.setMatrixFromOpenGL(modelviewMatrixArray.data()); + + m_transformMatrix.setMatrixFromOpenGL(m_projectionMatrixArray.data()); + //m_projectionMatrix.postmultiply(modelviewMatrix); + m_transformMatrix.premultiply(modelviewMatrix); + + const bool autoDetectWindowSpaceFlag = false; + if (autoDetectWindowSpaceFlag) { + /* + * Far/near from http://lektiondestages.blogspot.com/2013/11/decompose-opengl-projection-matrix.html + */ + const float nearValue = (1.0f + m_projectionMatrixArray[14]) / m_projectionMatrixArray[10]; + const float farValue = -(1.0f - m_projectionMatrixArray[14]) / m_projectionMatrixArray[10]; + + /* + * Window space when near is zero, far is one + */ + const float smallValue = 0.005; + if ((std::fabs(nearValue) < smallValue) + && (std::fabs(farValue - 1.0) < smallValue)) { + //m_windowDepthFlag = true; + } + } + + m_validFlag = true; +} + +/** + * @return The viewport. + */ +std::array +EventOpenGLObjectToWindowTransform::getViewport() const +{ + return m_viewport; +} + + diff --git a/src/Graphics/EventOpenGLObjectToWindowTransform.h b/src/Graphics/EventOpenGLObjectToWindowTransform.h new file mode 100644 index 0000000000000000000000000000000000000000..2dd7c2573cb7cd3941360f8e439368378c13cffd --- /dev/null +++ b/src/Graphics/EventOpenGLObjectToWindowTransform.h @@ -0,0 +1,103 @@ +#ifndef __EVENT_OPEN_G_L_OBJECT_TO_WINDOW_TRANSFORM_H__ +#define __EVENT_OPEN_G_L_OBJECT_TO_WINDOW_TRANSFORM_H__ + +/*LICENSE_START*/ +/* + * Copyright (C) 2017 Washington University School of Medicine + * + * 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 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ +/*LICENSE_END*/ + + +#include +#include + +#include "Event.h" +#include "Matrix4x4.h" + + +namespace caret { + + class EventOpenGLObjectToWindowTransform : public Event { + + public: + /** + * Space in which data is drawn + */ + enum class SpaceType { + /** Transformation data that is being drawn in model space (except volume)*/ + MODEL, + /** Transformation data that is being drawn in VOLUME model space*/ + VOLUME_SLICE_MODEL, + /** Transformation data that is being drawn in window space*/ + WINDOW + }; + + EventOpenGLObjectToWindowTransform(const SpaceType spaceType); + + virtual ~EventOpenGLObjectToWindowTransform(); + + Matrix4x4 getMatrix() const; + + bool isValid() const; + + bool inverseTransformPoint(const float windowXYZ[3], + float objectXYZOut[3]) const; + + bool transformPoint(const float objectXYZ[3], + float windowXYZOut[3]) const; + + void setup(const std::array& modelviewMatrixArray, + const std::array& projectionMatrixArray, + const std::array& viewport, + const std::array& depthRange, + const double centerToEyeDistance); + + std::array getViewport() const; + + // ADD_NEW_METHODS_HERE + + private: + EventOpenGLObjectToWindowTransform(const EventOpenGLObjectToWindowTransform&); + + EventOpenGLObjectToWindowTransform& operator=(const EventOpenGLObjectToWindowTransform&); + + const SpaceType m_spaceType; + + Matrix4x4 m_transformMatrix; + + mutable std::unique_ptr m_inverseTransformMatrix; + + std::array m_projectionMatrixArray; + + std::array m_viewport; + + std::array m_depthRange; + + double m_centerToEyeDistance; + + bool m_validFlag = false; + + // ADD_NEW_MEMBERS_HERE + + }; + +#ifdef __EVENT_OPEN_G_L_OBJECT_TO_WINDOW_TRANSFORM_DECLARE__ + // +#endif // __EVENT_OPEN_G_L_OBJECT_TO_WINDOW_TRANSFORM_DECLARE__ + +} // namespace +#endif //__EVENT_OPEN_G_L_OBJECT_TO_WINDOW_TRANSFORM_H__ diff --git a/src/Graphics/GraphicsEngineData.cxx b/src/Graphics/GraphicsEngineData.cxx new file mode 100644 index 0000000000000000000000000000000000000000..b608d779ca813d1aae9006a12e3e4b366f8f6e97 --- /dev/null +++ b/src/Graphics/GraphicsEngineData.cxx @@ -0,0 +1,82 @@ + +/*LICENSE_START*/ +/* + * Copyright (C) 2017 Washington University School of Medicine + * + * 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 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ +/*LICENSE_END*/ + +#define __GRAPHICS_ENGINE_DATA_DECLARE__ +#include "GraphicsEngineData.h" +#undef __GRAPHICS_ENGINE_DATA_DECLARE__ + +#include "CaretAssert.h" +#include "EventManager.h" +using namespace caret; + + + +/** + * \class caret::GraphicsEngineData + * \brief Base class for data a graphics engine may associate with primitive data. + * \ingroup Graphics + */ + +/** + * Constructor. + */ +GraphicsEngineData::GraphicsEngineData() +: CaretObject() +{ + +// EventManager::get()->addEventListener(this, EventTypeEnum::); +} + +/** + * Destructor. + */ +GraphicsEngineData::~GraphicsEngineData() +{ + EventManager::get()->removeAllEventsFromListener(this); +} + +/** + * Get a description of this object's content. + * @return String describing this object's content. + */ +AString +GraphicsEngineData::toString() const +{ + return "GraphicsEngineData"; +} + +/** + * Receive an event. + * + * @param event + * An event for which this instance is listening. + */ +void +GraphicsEngineData::receiveEvent(Event* /*event*/) +{ +// if (event->getEventType() == EventTypeEnum::) { +// eventName = dynamic_cast(event); +// CaretAssert(eventName); +// +// event->setEventProcessed(); +// } +} + diff --git a/src/Graphics/GraphicsEngineData.h b/src/Graphics/GraphicsEngineData.h new file mode 100644 index 0000000000000000000000000000000000000000..a8a56759a3afe3eed605c95f74b56864fbfbaada --- /dev/null +++ b/src/Graphics/GraphicsEngineData.h @@ -0,0 +1,63 @@ +#ifndef __GRAPHICS_ENGINE_DATA_H__ +#define __GRAPHICS_ENGINE_DATA_H__ + +/*LICENSE_START*/ +/* + * Copyright (C) 2017 Washington University School of Medicine + * + * 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 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ +/*LICENSE_END*/ + + + +#include + +#include "CaretObject.h" + +#include "EventListenerInterface.h" + + +namespace caret { + + class GraphicsEngineData : public CaretObject, public EventListenerInterface { + + public: + GraphicsEngineData(); + + virtual ~GraphicsEngineData(); + + + // ADD_NEW_METHODS_HERE + + virtual AString toString() const; + + virtual void receiveEvent(Event* event); + + private: + GraphicsEngineData(const GraphicsEngineData&); + + GraphicsEngineData& operator=(const GraphicsEngineData&); + + // ADD_NEW_MEMBERS_HERE + + }; + +#ifdef __GRAPHICS_ENGINE_DATA_DECLARE__ + // +#endif // __GRAPHICS_ENGINE_DATA_DECLARE__ + +} // namespace +#endif //__GRAPHICS_ENGINE_DATA_H__ diff --git a/src/Graphics/GraphicsEngineDataOpenGL.cxx b/src/Graphics/GraphicsEngineDataOpenGL.cxx new file mode 100644 index 0000000000000000000000000000000000000000..79b76e725ecdeacfcb21ea011ac94fc99752adbf --- /dev/null +++ b/src/Graphics/GraphicsEngineDataOpenGL.cxx @@ -0,0 +1,1212 @@ + +/*LICENSE_START*/ +/* + * Copyright (C) 2017 Washington University School of Medicine + * + * 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 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ +/*LICENSE_END*/ + +#define __GRAPHICS_ENGINE_DATA_OPEN_G_L_DECLARE__ +#include "GraphicsEngineDataOpenGL.h" +#undef __GRAPHICS_ENGINE_DATA_OPEN_G_L_DECLARE__ + +#include "CaretAssert.h" +#include "CaretLogger.h" +#include "DeveloperFlagsEnum.h" +#include "EventGraphicsOpenGLCreateBufferObject.h" +#include "EventGraphicsOpenGLCreateTextureName.h" +#include "EventOpenGLObjectToWindowTransform.h" +#include "EventManager.h" +#include "GraphicsOpenGLBufferObject.h" +#include "GraphicsOpenGLPolylineTriangles.h" +#include "GraphicsOpenGLTextureName.h" +#include "GraphicsPrimitive.h" +#include "GraphicsPrimitiveSelectionHelper.h" +#include "GraphicsShape.h" +#include "Matrix4x4.h" + +using namespace caret; + + + +/** + * \class caret::GraphicsEngineDataOpenGL + * \brief Contains data the OpenGL Graphics Engine may associate with primtive data. + * \ingroup Graphics + */ + +/** + * Constructor. + */ +GraphicsEngineDataOpenGL::GraphicsEngineDataOpenGL() +: GraphicsEngineData() +{ + +} + +/** + * Destructor. + */ +GraphicsEngineDataOpenGL::~GraphicsEngineDataOpenGL() +{ + deleteBuffers(); +} + +/** + * Delete the OpenGL buffer objects. + */ +void +GraphicsEngineDataOpenGL::deleteBuffers() +{ + if (m_textureImageDataName != NULL) { + delete m_textureImageDataName; + m_textureImageDataName = NULL; + } +} + +/** + * Invalidate the coordinates after they have + * changed in the graphics primitive. + */ +void +GraphicsEngineDataOpenGL::invalidateCoordinates() +{ + m_reloadCoordinatesFlag = true; +} + +/** + * Invalidate the colors after they have + * changed in the graphics primitive. + */ +void +GraphicsEngineDataOpenGL::invalidateColors() +{ + m_reloadColorsFlag = true; +} + +/** + * Get the OpenGL Buffer Usage Hint from the primitive. + * + * @param primitiveUsageType + * The graphics primitive usage type + * @return + * The OpenGL Buffer Usage Hint. + */ +GLenum +GraphicsEngineDataOpenGL::getOpenGLBufferUsageHint(const GraphicsPrimitive::UsageType primitiveUsageType) const +{ + GLenum usageHint = GL_STREAM_DRAW; + switch (primitiveUsageType) { + case GraphicsPrimitive::UsageType::MODIFIED_ONCE_DRAWN_FEW_TIMES: + usageHint = GL_STREAM_DRAW; + break; + case GraphicsPrimitive::UsageType::MODIFIED_ONCE_DRAWN_MANY_TIMES: + usageHint = GL_STATIC_DRAW; + break; + case GraphicsPrimitive::UsageType::MODIFIED_MANY_DRAWN_MANY_TIMES: + usageHint = GL_DYNAMIC_DRAW; + break; + } + + return usageHint; +} + +/** + * Load the coordinate buffer. + * + * @param primitive + * The graphics primitive that will be drawn. + */ +void +GraphicsEngineDataOpenGL::loadCoordinateBuffer(GraphicsPrimitive* primitive) +{ + CaretAssert(primitive); + + GLenum usageHint = getOpenGLBufferUsageHint(primitive->getUsageTypeCoordinates()); + + + GLsizei coordinateCount = 0; + switch (primitive->m_vertexDataType) { + case GraphicsPrimitive::VertexDataType::FLOAT_XYZ: + m_coordinateDataType = GL_FLOAT; + m_coordinatesPerVertex = 3; // X, Y, Z + + coordinateCount = primitive->m_xyz.size(); + const GLuint xyzSizeBytes = coordinateCount * sizeof(float); + CaretAssert(xyzSizeBytes > 0); + const GLvoid* xyzDataPointer = (const GLvoid*)&primitive->m_xyz[0]; + + /* + * Coordinate buffer may have been created. + * Coordinates may change but the number of coordinates does not change. + */ + if (m_coordinateBufferObject == NULL) { + EventGraphicsOpenGLCreateBufferObject createEvent; + EventManager::get()->sendEvent(createEvent.getPointer()); + m_coordinateBufferObject.reset(createEvent.getOpenGLBufferObject()); + CaretAssert(m_coordinateBufferObject); + } + + CaretAssert(m_coordinateBufferObject->getBufferObjectName()); + + glBindBuffer(GL_ARRAY_BUFFER, + m_coordinateBufferObject->getBufferObjectName()); + glBufferData(GL_ARRAY_BUFFER, + xyzSizeBytes, + xyzDataPointer, + usageHint); + break; + } + + if (m_coordinatesPerVertex > 0) { + m_arrayIndicesCount = coordinateCount / m_coordinatesPerVertex; + } + else { + m_arrayIndicesCount = 0; + } + + m_reloadCoordinatesFlag = false; +} + +/** + * Load the normal vector buffer. + * + * @param primitive + * The graphics primitive that will be drawn. + */ +void +GraphicsEngineDataOpenGL::loadNormalVectorBuffer(GraphicsPrimitive* primitive) +{ + CaretAssert(primitive); + + GLenum usageHint = getOpenGLBufferUsageHint(primitive->getUsageTypeNormals()); + + + switch (primitive->m_normalVectorDataType) { + case GraphicsPrimitive::NormalVectorDataType::NONE: + break; + case GraphicsPrimitive::NormalVectorDataType::FLOAT_XYZ: + { + m_normalVectorDataType = GL_FLOAT; + const GLuint normalSizeBytes = primitive->m_floatNormalVectorXYZ.size() * sizeof(float); + CaretAssert(normalSizeBytes > 0); + const GLvoid* normalDataPointer = (const GLvoid*)&primitive->m_floatNormalVectorXYZ[0]; + + EventGraphicsOpenGLCreateBufferObject createEvent; + EventManager::get()->sendEvent(createEvent.getPointer()); + m_normalVectorBufferObject.reset(createEvent.getOpenGLBufferObject()); + CaretAssert(m_normalVectorBufferObject->getBufferObjectName()); + + glBindBuffer(GL_ARRAY_BUFFER, + m_normalVectorBufferObject->getBufferObjectName()); + glBufferData(GL_ARRAY_BUFFER, + normalSizeBytes, + normalDataPointer, + usageHint); + } + break; + } +} + +/** + * Load the color buffer. + * @param primitive + * The graphics primitive that will be drawn. + */ +void +GraphicsEngineDataOpenGL::loadColorBuffer(GraphicsPrimitive* primitive) +{ + CaretAssert(primitive); + + GLenum usageHint = getOpenGLBufferUsageHint(primitive->getUsageTypeColors()); + + switch (primitive->m_colorDataType) { + case GraphicsPrimitive::ColorDataType::NONE: + break; + case GraphicsPrimitive::ColorDataType::FLOAT_RGBA: + { + EventGraphicsOpenGLCreateBufferObject createEvent; + EventManager::get()->sendEvent(createEvent.getPointer()); + m_colorBufferObject.reset(createEvent.getOpenGLBufferObject()); + CaretAssert(m_colorBufferObject->getBufferObjectName()); + + m_componentsPerColor = 4; + m_colorDataType = GL_FLOAT; + + const GLuint colorSizeBytes = primitive->m_floatRGBA.size() * sizeof(float); + CaretAssert(colorSizeBytes > 0); + const GLvoid* colorDataPointer = (const GLvoid*)&primitive->m_floatRGBA[0]; + + glBindBuffer(GL_ARRAY_BUFFER, + m_colorBufferObject->getBufferObjectName()); + glBufferData(GL_ARRAY_BUFFER, + colorSizeBytes, + colorDataPointer, + usageHint); + } + break; + case GraphicsPrimitive::ColorDataType::UNSIGNED_BYTE_RGBA: + { + EventGraphicsOpenGLCreateBufferObject createEvent; + EventManager::get()->sendEvent(createEvent.getPointer()); + m_colorBufferObject.reset(createEvent.getOpenGLBufferObject()); + CaretAssert(m_colorBufferObject->getBufferObjectName()); + + m_componentsPerColor = 4; + m_colorDataType = GL_UNSIGNED_BYTE; + + const GLuint colorSizeBytes = primitive->m_unsignedByteRGBA.size() * sizeof(uint8_t); + CaretAssert(colorSizeBytes > 0); + const GLvoid* colorDataPointer = (const GLvoid*)&primitive->m_unsignedByteRGBA[0]; + + glBindBuffer(GL_ARRAY_BUFFER, + m_colorBufferObject->getBufferObjectName()); + glBufferData(GL_ARRAY_BUFFER, + colorSizeBytes, + colorDataPointer, + usageHint); + } + break; + } + + m_reloadColorsFlag = false; +} + +/** + * Load the texture coordinate buffer. + * @param primitive + * The graphics primitive that will be drawn. + */ +void +GraphicsEngineDataOpenGL::loadTextureCoordinateBuffer(GraphicsPrimitive* primitive) +{ + CaretAssert(primitive); + + GLenum usageHint = getOpenGLBufferUsageHint(primitive->getUsageTypeTextureCoordinates()); + + switch (primitive->m_textureDataType) { + case GraphicsPrimitive::TextureDataType::FLOAT_STR: + { + EventGraphicsOpenGLCreateBufferObject createEvent; + EventManager::get()->sendEvent(createEvent.getPointer()); + m_textureCoordinatesBufferObject.reset(createEvent.getOpenGLBufferObject()); + CaretAssert(m_textureCoordinatesBufferObject->getBufferObjectName()); + + m_textureCoordinatesDataType = GL_FLOAT; + const GLuint textureSizeBytes = primitive->m_floatTextureSTR.size() * sizeof(float); + CaretAssert(textureSizeBytes > 0); + const GLvoid* textureDataPointer = (const GLvoid*)&primitive->m_floatTextureSTR[0]; + + glBindBuffer(GL_ARRAY_BUFFER, + m_textureCoordinatesBufferObject->getBufferObjectName()); + glBufferData(GL_ARRAY_BUFFER, + textureSizeBytes, + textureDataPointer, + usageHint); + + } + break; + case GraphicsPrimitive::TextureDataType::NONE: + break; + } +} + + +/** + * Load the buffers with data from the grpahics primitive. + * + * @param primitive + * The graphics primitive that will be drawn. + */ +void +GraphicsEngineDataOpenGL::loadAllBuffers(GraphicsPrimitive* primitive) +{ + loadCoordinateBuffer(primitive); + loadNormalVectorBuffer(primitive); + loadColorBuffer(primitive); + loadTextureCoordinateBuffer(primitive); +} + +/** + * If needed, load the texture buffer. + * Note that the texture buffer may be invalidated when + * an image is captures as the image capture wipes out all + * textures. + * + * @param primitive + * The graphics primitive that will be drawn. +*/ +void +GraphicsEngineDataOpenGL::loadTextureImageDataBuffer(GraphicsPrimitive* primitive) +{ + if (m_textureImageDataName != NULL) { + return; + } + + switch (primitive->m_textureDataType) { + case GraphicsPrimitive::TextureDataType::FLOAT_STR: + { + const int32_t imageWidth = primitive->m_textureImageWidth; + const int32_t imageHeight = primitive->m_textureImageHeight; + const int32_t imageNumberOfBytes = imageWidth * imageHeight * 4; + if (imageNumberOfBytes <= 0) { + CaretLogWarning("Invalid texture (empty data) for drawing primitive with texture"); + return; + } + if (imageNumberOfBytes != static_cast(primitive->m_textureImageBytesRGBA.size())) { + CaretLogWarning("Image bytes size incorrect for image width/height"); + return; + } + + glPushClientAttrib(GL_CLIENT_PIXEL_STORE_BIT); + + const GLubyte* imageBytesRGBA = &primitive->m_textureImageBytesRGBA[0]; + EventGraphicsOpenGLCreateTextureName createEvent; + EventManager::get()->sendEvent(createEvent.getPointer()); + m_textureImageDataName = createEvent.getOpenGLTextureName(); + CaretAssert(m_textureImageDataName); + + const GLuint openGLTextureName = m_textureImageDataName->getTextureName(); + + glBindTexture(GL_TEXTURE_2D, openGLTextureName); + + bool useMipMapFlag = true; + if (useMipMapFlag) { + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR); + + /* + * This code seems to work if OpenGL 3.0 or later and + * replaces gluBuild2DMipmaps() + * + * glTexImage2D(GL_TEXTURE_2D, // MUST BE GL_TEXTURE_2D + * 0, // level of detail 0=base, n is nth mipmap reduction + * GL_RGBA, // number of components + * imageWidth, // width of image + * imageHeight, // height of image + * 0, // border + * GL_RGBA, // format of the pixel data + * GL_UNSIGNED_BYTE, // data type of pixel data + * imageBytesRGBA); // pointer to image data + * glGenerateMipmap(GL_TEXTURE_2D); + */ + + + const int errorCode = gluBuild2DMipmaps(GL_TEXTURE_2D, // MUST BE GL_TEXTURE_2D + GL_RGBA, // number of components + imageWidth, // width of image + imageHeight, // height of image + GL_RGBA, // format of the pixel data + GL_UNSIGNED_BYTE, // data type of pixel data + imageBytesRGBA); // pointer to image data + if (errorCode != 0) { + useMipMapFlag = false; + + const GLubyte* errorChars = gluErrorString(errorCode); + if (errorChars != NULL) { + const QString errorText = ("ERROR building mipmaps for annotation image: " + + AString((char*)errorChars)); + CaretLogSevere(errorText); + } + } + } + + if ( ! useMipMapFlag) { + CaretAssert(0); // image must be 2^N by 2^M + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); + + glTexImage2D(GL_TEXTURE_2D, // MUST BE GL_TEXTURE_2D + 0, // level of detail 0=base, n is nth mipmap reduction + GL_RGBA, // number of components + imageWidth, // width of image + imageHeight, // height of image + 0, // border + GL_RGBA, // format of the pixel data + GL_UNSIGNED_BYTE, // data type of pixel data + imageBytesRGBA); // pointer to image data + } + + glBindTexture(GL_TEXTURE_2D, 0); + glPopClientAttrib(); + } + break; + case GraphicsPrimitive::TextureDataType::NONE: + break; + } +} + +/** + * Draw the given graphics primitive. + * + * @param primitive + * Primitive that is drawn. + */ +void +GraphicsEngineDataOpenGL::draw(GraphicsPrimitive* primitive) +{ + CaretAssert(primitive); + + if (primitive->getNumberOfVertices() <= 0) { + return; + } + if ( ! primitive->isValid()) { + CaretLogWarning("Attempting to draw invalid Graphics Primitive"); + } + + GraphicsPrimitive* primitiveToDraw = primitive; + + /* Conversion to window space creates a new primitive that must be deleted */ + std::unique_ptr windowSpacePrimitive; + + bool workbenchLineFlag = false; + bool millimeterPointsFlag = false; + bool spheresFlag = false; + switch (primitive->m_primitiveType) { + case GraphicsPrimitive::PrimitiveType::OPENGL_LINE_LOOP: + break; + case GraphicsPrimitive::PrimitiveType::OPENGL_LINE_STRIP: + break; + case GraphicsPrimitive::PrimitiveType::OPENGL_LINES: + break; + case GraphicsPrimitive::PrimitiveType::OPENGL_POINTS: + switch (primitive->m_pointSizeType) { + case GraphicsPrimitive::PointSizeType::MILLIMETERS: + millimeterPointsFlag = true; + break; + case GraphicsPrimitive::PointSizeType::PERCENTAGE_VIEWPORT_HEIGHT: + break; + case GraphicsPrimitive::PointSizeType::PIXELS: + break; + } + break; + case GraphicsPrimitive::PrimitiveType::OPENGL_TRIANGLE_FAN: + break; + case GraphicsPrimitive::PrimitiveType::OPENGL_TRIANGLE_STRIP: + break; + case GraphicsPrimitive::PrimitiveType::OPENGL_TRIANGLES: + break; + case GraphicsPrimitive::PrimitiveType::POLYGONAL_LINE_LOOP_BEVEL_JOIN: + case GraphicsPrimitive::PrimitiveType::POLYGONAL_LINE_LOOP_MITER_JOIN: + case GraphicsPrimitive::PrimitiveType::POLYGONAL_LINE_STRIP_BEVEL_JOIN: + case GraphicsPrimitive::PrimitiveType::POLYGONAL_LINE_STRIP_MITER_JOIN: + case GraphicsPrimitive::PrimitiveType::POLYGONAL_LINES: + workbenchLineFlag = true; + break; + case GraphicsPrimitive::PrimitiveType::SPHERES: + spheresFlag = true; + break; + } + + if (millimeterPointsFlag) { + /* + * Special case for points in millimeters + */ + drawPointsPrimitiveMillimeters(primitive); + } + else if (spheresFlag) { + drawSpheresPrimitive(primitive); + } + else if (workbenchLineFlag) { + AString errorMessage; + primitiveToDraw = GraphicsOpenGLPolylineTriangles::convertWorkbenchLinePrimitiveTypeToOpenGL(primitive, + errorMessage); + if (primitiveToDraw == NULL) { + CaretLogSevere(errorMessage); + return; + } + windowSpacePrimitive.reset(primitiveToDraw); + drawWindowSpace(PrivateDrawMode::DRAW_NORMAL, + primitiveToDraw, + NULL); + } + else { + drawPrivate(PrivateDrawMode::DRAW_NORMAL, + primitiveToDraw, + NULL); + } +} + +/** + * Draw the graphics primitive in window space. + * + * @param drawMode + * Mode for drawing. + * @param primitive + * Primitive that is drawn. + * @param primitiveSelectionHelper + * Selection helper when draw mode is selection. + */ +void +GraphicsEngineDataOpenGL::drawWindowSpace(const PrivateDrawMode drawMode, + GraphicsPrimitive* primitive, + GraphicsPrimitiveSelectionHelper* primitiveSelectionHelper) +{ + int32_t polygonMode[2]; + int32_t viewport[4]; + saveOpenGLStateForWindowSpaceDrawing(polygonMode, + viewport); + + glMatrixMode(GL_PROJECTION); + glLoadIdentity(); + glOrtho(viewport[0], viewport[0] + viewport[2], + viewport[1], viewport[1] + viewport[3], + 0, 1); + glMatrixMode(GL_MODELVIEW); + glLoadIdentity(); + + glPolygonMode(GL_FRONT, GL_FILL); + + drawPrivate(drawMode, + primitive, + primitiveSelectionHelper); + + restoreOpenGLStateForWindowSpaceDrawing(polygonMode, + viewport); +} + +/** + * Draw a point primitive type in millimeters (same as model space). + * + * @param primitive + * The points primitive type. + */ +void +GraphicsEngineDataOpenGL::drawPointsPrimitiveMillimeters(const GraphicsPrimitive* primitive) +{ + CaretAssert(primitive); + CaretAssert(primitive->getPrimitiveType() == GraphicsPrimitive::PrimitiveType::OPENGL_POINTS); + CaretAssert(primitive->m_pointSizeType == GraphicsPrimitive::PointSizeType::MILLIMETERS); + + GraphicsPrimitive::PointSizeType sizeType; + float sizeValue = 0.0f; + primitive->getPointDiameter(sizeType, + sizeValue); + + const int32_t numberOfVertices = primitive->getNumberOfVertices(); + switch (primitive->m_pointSizeType) { + case GraphicsPrimitive::PointSizeType::MILLIMETERS: + { + switch (primitive->m_vertexColorType) { + case GraphicsPrimitive::VertexColorType::NONE: + CaretAssert(0); + CaretLogSevere("NONE vertex color type, should never occur when drawing points in millimeters"); + return; + break; + case GraphicsPrimitive::VertexColorType::PER_VERTEX_RGBA: + { + for (int32_t i = 0; i < numberOfVertices; i++) { + const int32_t i3 = i * 3; + CaretAssertVectorIndex(primitive->m_xyz, i3 + 2); + const float* xyz = &primitive->m_xyz[i3]; + const int32_t i4 = i * 4; + + uint8_t* rgba = NULL; + switch (primitive->m_colorDataType) { + case GraphicsPrimitive::ColorDataType::FLOAT_RGBA: + CaretAssert(0); + break; + case GraphicsPrimitive::ColorDataType::UNSIGNED_BYTE_RGBA: + CaretAssertVectorIndex(primitive->m_unsignedByteRGBA, i4 + 3); + rgba = const_cast(&primitive->m_unsignedByteRGBA[i4]); + break; + case GraphicsPrimitive::ColorDataType::NONE: + CaretAssert(0); + break; + } + + GraphicsShape::drawSquare(xyz, rgba, sizeValue); + } + } + break; + case GraphicsPrimitive::VertexColorType::SOLID_RGBA: + { + const int32_t numberOfVertices = primitive->getNumberOfVertices(); + CaretAssertVectorIndex(primitive->m_xyz, (numberOfVertices - 1) * 3 + 1); + GraphicsShape::drawSquares(&primitive->m_xyz[0], + numberOfVertices, + &primitive->m_unsignedByteRGBA[0], + sizeValue); + } + break; + } + } + break; + case GraphicsPrimitive::PointSizeType::PERCENTAGE_VIEWPORT_HEIGHT: + case GraphicsPrimitive::PointSizeType::PIXELS: + CaretAssert(0); + break; + } +} + +/** + * Draw a sphere primitive type. + * + * @param primitive + * The spheres primitive type. + */ +void +GraphicsEngineDataOpenGL::drawSpheresPrimitive(const GraphicsPrimitive* primitive) +{ + CaretAssert(primitive); + CaretAssert(primitive->getPrimitiveType() == GraphicsPrimitive::PrimitiveType::SPHERES); + + GraphicsPrimitive::SphereSizeType sizeType; + float sizeValue = 0.0f; + primitive->getSphereDiameter(sizeType, + sizeValue); + + switch (primitive->m_vertexColorType) { + case GraphicsPrimitive::VertexColorType::NONE: + CaretAssert(0); + CaretLogSevere("NONE vertex color type, should never occur when drawing spheres"); + return; + break; + case GraphicsPrimitive::VertexColorType::PER_VERTEX_RGBA: + { + const int32_t numberOfVertices = primitive->getNumberOfVertices(); + for (int32_t i = 0; i < numberOfVertices; i++) { + const int32_t i3 = i * 3; + CaretAssertVectorIndex(primitive->m_xyz, i3 + 2); + const int32_t i4 = i * 4; + + switch (primitive->m_colorDataType) { + case GraphicsPrimitive::ColorDataType::FLOAT_RGBA: + CaretAssert(0); + break; + case GraphicsPrimitive::ColorDataType::UNSIGNED_BYTE_RGBA: + CaretAssertVectorIndex(primitive->m_unsignedByteRGBA, i4 + 3); + GraphicsShape::drawSphereByteColor(&primitive->m_xyz[i3], + &primitive->m_unsignedByteRGBA[i4], + sizeValue); + break; + case GraphicsPrimitive::ColorDataType::NONE: + CaretAssert(0); + break; + } + } + } + break; + case GraphicsPrimitive::VertexColorType::SOLID_RGBA: + { + const int32_t numberOfVertices = primitive->getNumberOfVertices(); + CaretAssertVectorIndex(primitive->m_xyz, (numberOfVertices - 1) * 3 + 1); + GraphicsShape::drawSpheresByteColor(&primitive->m_xyz[0], + numberOfVertices, + &primitive->m_unsignedByteRGBA[0], + sizeValue); + } + break; + } +} + +/** + * Save the OpenGL State for drawing in window space. + * + * @param polygonMode + * Output with the polygon mode. + * @param viewport + * Output with viewport. + */ +void +GraphicsEngineDataOpenGL::saveOpenGLStateForWindowSpaceDrawing(int32_t polygonMode[2], + int32_t viewport[4]) +{ + glPushAttrib(GL_COLOR_BUFFER_BIT + | GL_ENABLE_BIT + | GL_HINT_BIT + | GL_LIGHTING_BIT + | GL_POLYGON_BIT); + + /* + * For anti-aliasing requires sorting polygons + * and turning of depth testing + */ + const bool drawWithAntiAliasingFlag = false; + if (drawWithAntiAliasingFlag) { + glBlendFunc (GL_SRC_ALPHA_SATURATE, GL_ONE); + glEnable(GL_BLEND); + glHint(GL_POLYGON_SMOOTH_HINT, + GL_NICEST); + glEnable(GL_POLYGON_SMOOTH); + } + + glDisable(GL_LIGHTING); + glDisable(GL_COLOR_MATERIAL); + glGetIntegerv(GL_POLYGON_MODE, + polygonMode); + + glGetIntegerv(GL_VIEWPORT, + viewport); + glMatrixMode(GL_PROJECTION); + glPushMatrix(); + glMatrixMode(GL_MODELVIEW); + glPushMatrix(); +} + +/** + * Restore the OpenGL State from drawing in window space. + + * @param polygonMode + * Polygon mode. + * @param viewport + * Viewport. + */ +void +GraphicsEngineDataOpenGL::restoreOpenGLStateForWindowSpaceDrawing(int32_t polygonMode[2], + int32_t viewport[4]) +{ + glPolygonMode(GL_FRONT, polygonMode[0]); + glPolygonMode(GL_BACK, polygonMode[1]); + glMatrixMode(GL_PROJECTION); + glPopMatrix(); + glMatrixMode(GL_MODELVIEW); + glPopMatrix(); + glViewport(viewport[0], viewport[1], viewport[2], viewport[3]); + + glPopAttrib(); +} + +/** + * Draw to determine the index of primitive at the given (X, Y) location + * + * @param primitive + * Primitive that is drawn. + * @param pixelX + * X location for selection. + * @param pixelY + * Y location for selection. + * @param selectedPrimitiveIndexOut + * Output with the index of the primitive (invalid no primitve at location + * @param selectedPrimitiveDepthOut + * Output with depth at the location. + */ +void +GraphicsEngineDataOpenGL::drawWithSelection(GraphicsPrimitive* primitive, + const int32_t pixelX, + const int32_t pixelY, + int32_t& selectedPrimitiveIndexOut, + float& selectedPrimitiveDepthOut) +{ + CaretAssert(primitive); + + switch (primitive->m_primitiveType) { + case GraphicsPrimitive::PrimitiveType::OPENGL_LINE_LOOP: + break; + case GraphicsPrimitive::PrimitiveType::OPENGL_LINE_STRIP: + break; + case GraphicsPrimitive::PrimitiveType::OPENGL_LINES: + break; + case GraphicsPrimitive::PrimitiveType::OPENGL_POINTS: + break; + case GraphicsPrimitive::PrimitiveType::OPENGL_TRIANGLE_FAN: + break; + case GraphicsPrimitive::PrimitiveType::OPENGL_TRIANGLE_STRIP: + break; + case GraphicsPrimitive::PrimitiveType::OPENGL_TRIANGLES: + break; + case GraphicsPrimitive::PrimitiveType::POLYGONAL_LINE_LOOP_BEVEL_JOIN: + case GraphicsPrimitive::PrimitiveType::POLYGONAL_LINE_LOOP_MITER_JOIN: + break; + case GraphicsPrimitive::PrimitiveType::POLYGONAL_LINE_STRIP_BEVEL_JOIN: + case GraphicsPrimitive::PrimitiveType::POLYGONAL_LINE_STRIP_MITER_JOIN: + break; + case GraphicsPrimitive::PrimitiveType::POLYGONAL_LINES: + break; + case GraphicsPrimitive::PrimitiveType::SPHERES: + CaretAssertMessage(0, "Not yet implemented"); + break; + } + + selectedPrimitiveIndexOut = -1; + selectedPrimitiveDepthOut = 0.0; + + GLint viewport[4]; + glGetIntegerv(GL_VIEWPORT, + viewport); + if ((pixelX >= viewport[0]) + && (pixelX < (viewport[0] + viewport[2])) + && (pixelY >= viewport[1]) + && (pixelY < (viewport[1] + viewport[3]))) { + /* + * Saves glPixelStore parameters + */ + glPushClientAttrib(GL_CLIENT_PIXEL_STORE_BIT); + glPushAttrib(GL_LIGHTING_BIT); + + glShadeModel(GL_FLAT); + glDisable(GL_LIGHTING); + + GraphicsPrimitiveSelectionHelper selectionHelper(primitive); + selectionHelper.setupSelectionBeforeDrawing(); + drawPrivate(PrivateDrawMode::DRAW_SELECTION, + primitive, + &selectionHelper); + + /* + * QOpenGLWidget Note: The QOpenGLWidget always renders in a + * frame buffer object (see its documentation). This is + * probably why calls to glReadBuffer() always cause an + * OpenGL error. + */ +#ifdef WORKBENCH_USE_QT5_QOPENGL_WIDGET + /* do not call glReadBuffer() */ +#else + glReadBuffer(GL_BACK); +#endif + uint8_t pixelRGBA[4]; + + glPixelStorei(GL_PACK_SKIP_ROWS, 0); + glPixelStorei(GL_PACK_SKIP_PIXELS, 0); + glPixelStorei(GL_PACK_ALIGNMENT, 1); // bytes + glReadPixels(pixelX, + pixelY, + 1, + 1, + GL_RGBA, + GL_UNSIGNED_BYTE, + pixelRGBA); + + /* + * Get depth from depth buffer + */ + glPixelStorei(GL_PACK_ALIGNMENT, 4); // float + glReadPixels(pixelX, + pixelY, + 1, + 1, + GL_DEPTH_COMPONENT, + GL_FLOAT, + &selectedPrimitiveDepthOut); + + glPopAttrib(); + glPopClientAttrib(); + + selectedPrimitiveIndexOut = selectionHelper.getPrimitiveIndexFromEncodedRGBA(pixelRGBA); + } +} + +/** + * Draw the graphics primitive. + * + * @param drawMode + * Mode for drawing. + * @param primitive + * Primitive that is drawn. + * @param primitiveSelectionHelper + * Selection helper when draw mode is selection. + */ +void +GraphicsEngineDataOpenGL::drawPrivate(const PrivateDrawMode drawMode, + GraphicsPrimitive* primitive, + GraphicsPrimitiveSelectionHelper* primitiveSelectionHelper) +{ + CaretAssert(primitive); + if ( ! primitive->isValid()) { + return; + } + + switch (drawMode) { + case PrivateDrawMode::DRAW_NORMAL: + break; + case PrivateDrawMode::DRAW_SELECTION: + break; + } + + GraphicsEngineDataOpenGL* openglData = primitive->getGraphicsEngineDataForOpenGL(); + if (openglData == NULL) { + openglData = new GraphicsEngineDataOpenGL(); + primitive->setGraphicsEngineDataForOpenGL(openglData); + + openglData->loadAllBuffers(primitive); + } + else { + /* + * Coordinates may get updated. + */ + if (openglData->m_reloadCoordinatesFlag) { + openglData->loadCoordinateBuffer(primitive); + } + + /* + * Colors may get updated + */ + if (openglData->m_reloadColorsFlag) { + openglData->loadColorBuffer(primitive); + } + } + + openglData->loadTextureImageDataBuffer(primitive); + + const GLuint coordBufferID = openglData->m_coordinateBufferObject->getBufferObjectName(); + CaretAssert(coordBufferID); + if ( ! glIsBuffer(coordBufferID)) { + CaretAssertMessage(0, "Coordinate buffer is INVALID"); + CaretLogSevere("Coordinate buffer is INVALID"); + return; + } + + switch (drawMode) { + case PrivateDrawMode::DRAW_NORMAL: + break; + case PrivateDrawMode::DRAW_SELECTION: + break; + } + + /* + * Setup vertices for drawing + */ + glEnableClientState(GL_VERTEX_ARRAY); + CaretAssert(glIsBuffer(coordBufferID)); + glBindBuffer(GL_ARRAY_BUFFER, + coordBufferID); + glVertexPointer(openglData->m_coordinatesPerVertex, + openglData->m_coordinateDataType, + 0, + (GLvoid*)0); + + /* + * Setup normals for drawing + */ + if (openglData->m_normalVectorBufferObject != NULL) { + glEnableClientState(GL_NORMAL_ARRAY); + CaretAssert(glIsBuffer(openglData->m_normalVectorBufferObject->getBufferObjectName())); + glBindBuffer(GL_ARRAY_BUFFER, openglData->m_normalVectorBufferObject->getBufferObjectName()); + glNormalPointer(openglData->m_normalVectorDataType, 0, (GLvoid*)0); + } + else { + glDisableClientState(GL_NORMAL_ARRAY); + } + + bool hasColorFlag = false; + switch (primitive->getColorDataType()) { + case GraphicsPrimitive::ColorDataType::NONE: + break; + case GraphicsPrimitive::ColorDataType::FLOAT_RGBA: + hasColorFlag = true; + break; + case GraphicsPrimitive::ColorDataType::UNSIGNED_BYTE_RGBA: + hasColorFlag = true; + break; + } + /* + * Setup colors for drawing + */ + GLuint localColorBufferName = 0; + if (hasColorFlag) { + switch (drawMode) { + case PrivateDrawMode::DRAW_NORMAL: + { + glEnableClientState(GL_COLOR_ARRAY); + CaretAssert(glIsBuffer(openglData->m_colorBufferObject->getBufferObjectName())); + glBindBuffer(GL_ARRAY_BUFFER, openglData->m_colorBufferObject->getBufferObjectName()); + glColorPointer(openglData->m_componentsPerColor, openglData->m_colorDataType, 0, (GLvoid*)0); + } + break; + case PrivateDrawMode::DRAW_SELECTION: + { + const int32_t numberOfVertices = primitive->m_xyz.size() / 3; + if (numberOfVertices > 0) { + glGenBuffers(1, &localColorBufferName); + CaretAssert(localColorBufferName > 0); + + const GLint componentsPerColor = 4; + const GLenum colorDataType = GL_UNSIGNED_BYTE; + + const std::vector selectionRGBA = primitiveSelectionHelper->getSelectionEncodedRGBA(); + CaretAssert((selectionRGBA.size() / 4) == numberOfVertices); + + const GLuint colorSizeBytes = selectionRGBA.size() * sizeof(GLubyte); + const GLvoid* colorDataPointer = (const GLvoid*)&selectionRGBA[0]; + glBindBuffer(GL_ARRAY_BUFFER, + localColorBufferName); + glBufferData(GL_ARRAY_BUFFER, + colorSizeBytes, + colorDataPointer, + GL_STREAM_DRAW); + glEnableClientState(GL_COLOR_ARRAY); + glColorPointer(componentsPerColor, colorDataType, 0, (GLvoid*)0); + } + } + break; + } + } + + bool hasTextureFlag = false; + switch (primitive->getTextureDataType()) { + case GraphicsPrimitive::TextureDataType::NONE: + break; + case GraphicsPrimitive::TextureDataType::FLOAT_STR: + hasTextureFlag = true; + break; + } + + /* + * Setup textures for drawing + */ + if (hasTextureFlag + && (drawMode == PrivateDrawMode::DRAW_NORMAL) + && (openglData->m_textureImageDataName->getTextureName() > 0)) { + glEnable(GL_TEXTURE_2D); + glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE); + glBindTexture(GL_TEXTURE_2D, openglData->m_textureImageDataName->getTextureName()); + + glEnableClientState(GL_TEXTURE_COORD_ARRAY); + CaretAssert(glIsBuffer(openglData->m_textureCoordinatesBufferObject->getBufferObjectName())); + glBindBuffer(GL_ARRAY_BUFFER, openglData->m_textureCoordinatesBufferObject->getBufferObjectName()); + glTexCoordPointer(3, openglData->m_textureCoordinatesDataType, 0, (GLvoid*)0); + } + + /* + * Unbind buffers + */ + glBindBuffer(GL_ARRAY_BUFFER, + 0); + glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, + 0); + + /* + * Draw the primitive + */ + GLenum openGLPrimitiveType = GL_INVALID_ENUM; + switch (primitive->m_primitiveType) { + case GraphicsPrimitive::PrimitiveType::OPENGL_LINE_LOOP: + case GraphicsPrimitive::PrimitiveType::POLYGONAL_LINE_LOOP_BEVEL_JOIN: + case GraphicsPrimitive::PrimitiveType::POLYGONAL_LINE_LOOP_MITER_JOIN: + openGLPrimitiveType = GL_LINE_LOOP; + glLineWidth(getLineWidthForDrawingInPixels(primitive)); + break; + case GraphicsPrimitive::PrimitiveType::OPENGL_LINE_STRIP: + case GraphicsPrimitive::PrimitiveType::POLYGONAL_LINE_STRIP_BEVEL_JOIN: + case GraphicsPrimitive::PrimitiveType::POLYGONAL_LINE_STRIP_MITER_JOIN: + openGLPrimitiveType = GL_LINE_STRIP; + glLineWidth(getLineWidthForDrawingInPixels(primitive)); + break; + case GraphicsPrimitive::PrimitiveType::OPENGL_LINES: + case GraphicsPrimitive::PrimitiveType::POLYGONAL_LINES: + openGLPrimitiveType = GL_LINES; + glLineWidth(getLineWidthForDrawingInPixels(primitive)); + break; + case GraphicsPrimitive::PrimitiveType::OPENGL_POINTS: + openGLPrimitiveType = GL_POINTS; + glPointSize(getPointDiameterForDrawingInPixels(primitive)); + break; + case GraphicsPrimitive::PrimitiveType::OPENGL_TRIANGLE_FAN: + openGLPrimitiveType = GL_TRIANGLE_FAN; + break; + case GraphicsPrimitive::PrimitiveType::OPENGL_TRIANGLE_STRIP: + openGLPrimitiveType = GL_TRIANGLE_STRIP; + break; + case GraphicsPrimitive::PrimitiveType::OPENGL_TRIANGLES: + openGLPrimitiveType = GL_TRIANGLES; + break; + case GraphicsPrimitive::PrimitiveType::SPHERES: + CaretAssertMessage(0, "SPHERES are not drawn with this method"); + break; + } + + CaretAssert(openGLPrimitiveType != GL_INVALID_ENUM); + glDrawArrays(openGLPrimitiveType, 0, openglData->m_arrayIndicesCount); + + /* + * Disable drawing + */ + glDisableClientState(GL_VERTEX_ARRAY); + glDisableClientState(GL_NORMAL_ARRAY); + glDisableClientState(GL_COLOR_ARRAY); + glDisableClientState(GL_TEXTURE_COORD_ARRAY); + + if (localColorBufferName > 0) { + glDeleteBuffers(1, &localColorBufferName); + } + + glBindTexture(GL_TEXTURE_2D, 0); + glDisable(GL_TEXTURE_2D); +} + +/** + * Get the line width in pixels for drawing lines. + * If the primitive's line width type is a percentage + * of viewport height, convert it to pixels. + * + * @param primtive + * The graphics primitive. + * @return + * Width of line in pixels. + */ +float +GraphicsEngineDataOpenGL::getLineWidthForDrawingInPixels(const GraphicsPrimitive* primitive) +{ + CaretAssert(primitive); + + float width = primitive->m_lineWidthValue; + + switch (primitive->m_lineWidthType) { + case GraphicsPrimitive::LineWidthType::PERCENTAGE_VIEWPORT_HEIGHT: + { + GLint viewport[4]; + glGetIntegerv(GL_VIEWPORT, viewport); + + width = (viewport[3] * (width / 100.0f)); + } + break; + case GraphicsPrimitive::LineWidthType::PIXELS: + break; + } + + return width; +} + +/** + * Get the point diameter in pixels for drawing points. + * If the primitive's point diameter type is a percentage + * of viewport height, convert it to pixels. + * + * @param primtive + * The graphics primitive. + * @return + * Diameter of point in pixels. + */ +float +GraphicsEngineDataOpenGL::getPointDiameterForDrawingInPixels(const GraphicsPrimitive* primitive) +{ + CaretAssert(primitive); + + float pointSize = primitive->m_pointDiameterValue; + + switch (primitive->m_pointSizeType) { + case GraphicsPrimitive::PointSizeType::MILLIMETERS: + CaretAssert(0); /* millimeters not convertible to pixels */ + break; + case GraphicsPrimitive::PointSizeType::PERCENTAGE_VIEWPORT_HEIGHT: + { + GLint viewport[4]; + glGetIntegerv(GL_VIEWPORT, viewport); + + pointSize = (viewport[3] * (pointSize / 100.0f)); + } + break; + case GraphicsPrimitive::PointSizeType::PIXELS: + break; + } + + return pointSize; +} + diff --git a/src/Graphics/GraphicsEngineDataOpenGL.h b/src/Graphics/GraphicsEngineDataOpenGL.h new file mode 100644 index 0000000000000000000000000000000000000000..6289ae561ad7c1fa33b204dc0551a2cf6508fc30 --- /dev/null +++ b/src/Graphics/GraphicsEngineDataOpenGL.h @@ -0,0 +1,144 @@ +#ifndef __GRAPHICS_ENGINE_DATA_OPEN_G_L_H__ +#define __GRAPHICS_ENGINE_DATA_OPEN_G_L_H__ + +/*LICENSE_START*/ +/* + * Copyright (C) 2017 Washington University School of Medicine + * + * 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 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ +/*LICENSE_END*/ + +#include +#include + +#include "CaretOpenGLInclude.h" +#include "GraphicsEngineData.h" +#include "GraphicsPrimitive.h" + +namespace caret { + + class GraphicsOpenGLBufferObject; + class GraphicsOpenGLTextureName; + class GraphicsPrimitive; + class GraphicsPrimitiveSelectionHelper; + + class GraphicsEngineDataOpenGL : public GraphicsEngineData { + + public: + GraphicsEngineDataOpenGL(); + + virtual ~GraphicsEngineDataOpenGL(); + + void deleteBuffers(); + + static void draw(GraphicsPrimitive* primitive); + + static void drawWithSelection(GraphicsPrimitive* primitive, + const int32_t pixelX, + const int32_t pixelY, + int32_t& selectedPrimitiveIndexOut, + float& selectedPrimitiveDepthOut); + + void invalidateCoordinates(); + + void invalidateColors(); + + // ADD_NEW_METHODS_HERE + + private: + enum class PrivateDrawMode { + DRAW_NORMAL, + DRAW_SELECTION, + }; + + GraphicsEngineDataOpenGL(const GraphicsEngineDataOpenGL&); + + GraphicsEngineDataOpenGL& operator=(const GraphicsEngineDataOpenGL&); + + void loadAllBuffers(GraphicsPrimitive* primitive); + + void loadCoordinateBuffer(GraphicsPrimitive* primitive); + + void loadNormalVectorBuffer(GraphicsPrimitive* primitive); + + void loadColorBuffer(GraphicsPrimitive* primitive); + + void loadTextureCoordinateBuffer(GraphicsPrimitive* primitive); + + void loadTextureImageDataBuffer(GraphicsPrimitive* primitive); + + static void drawPrivate(const PrivateDrawMode drawMode, + GraphicsPrimitive* primitive, + GraphicsPrimitiveSelectionHelper* primitiveSelectionHelper); + + static void drawWindowSpace(const PrivateDrawMode drawMode, + GraphicsPrimitive* primitive, + GraphicsPrimitiveSelectionHelper* primitiveSelectionHelper); + + static void drawPointsPrimitiveMillimeters(const GraphicsPrimitive* primitive); + + static void drawSpheresPrimitive(const GraphicsPrimitive* primitive); + + GLenum getOpenGLBufferUsageHint(const GraphicsPrimitive::UsageType primitiveUsageType) const; + + static float getLineWidthForDrawingInPixels(const GraphicsPrimitive* primitive); + + static float getPointDiameterForDrawingInPixels(const GraphicsPrimitive* primitive); + + static void saveOpenGLStateForWindowSpaceDrawing(int32_t polygonMode[2], + int32_t viewport[4]); + + static void restoreOpenGLStateForWindowSpaceDrawing(int32_t polygonMode[2], + int32_t viewport[4]); + + GLsizei m_arrayIndicesCount = 0; + + std::unique_ptr m_coordinateBufferObject; + + GLenum m_coordinateDataType = GL_FLOAT; + + GLint m_coordinatesPerVertex = 0; + + bool m_reloadCoordinatesFlag = false; + + bool m_reloadColorsFlag = false; + + std::unique_ptr m_normalVectorBufferObject; + + GLenum m_normalVectorDataType = GL_FLOAT; + + std::unique_ptr m_colorBufferObject; + + GLenum m_colorDataType = GL_FLOAT; + + GLint m_componentsPerColor = 0; + + std::unique_ptr m_textureCoordinatesBufferObject; + + GLenum m_textureCoordinatesDataType = GL_FLOAT; + + GraphicsOpenGLTextureName* m_textureImageDataName = NULL; + +// ADD_NEW_MEMBERS_HERE + + }; + +#ifdef __GRAPHICS_ENGINE_DATA_OPEN_G_L_DECLARE__ + // +#endif // __GRAPHICS_ENGINE_DATA_OPEN_G_L_DECLARE__ + +} // namespace +#endif //__GRAPHICS_ENGINE_DATA_OPEN_G_L_H__ diff --git a/src/Graphics/GraphicsOpenGLBufferObject.cxx b/src/Graphics/GraphicsOpenGLBufferObject.cxx new file mode 100644 index 0000000000000000000000000000000000000000..988259a17c93c5802147dca47f457b2b3e00498b --- /dev/null +++ b/src/Graphics/GraphicsOpenGLBufferObject.cxx @@ -0,0 +1,77 @@ + +/*LICENSE_START*/ +/* + * Copyright (C) 2017 Washington University School of Medicine + * + * 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 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ +/*LICENSE_END*/ + +#define __GRAPHICS_OPEN_G_L_BUFFER_OBJECT_DECLARE__ +#include "GraphicsOpenGLBufferObject.h" +#undef __GRAPHICS_OPEN_G_L_BUFFER_OBJECT_DECLARE__ + +#include "CaretAssert.h" +#include "EventGraphicsOpenGLDeleteBufferObject.h" +#include "EventManager.h" + +using namespace caret; + + + +/** + * \class caret::GraphicsOpenGLBufferObject + * \brief Contains the OpenGL Buffer ID and the context in which it was created. + * \ingroup Graphics + */ + +/** + * Constructor. + * + * @param openglContextPointer + * Pointer to OpenGL context in which buffer object name was created. + * @param bufferObjectName + * The OpenGL buffer object name. + */ +GraphicsOpenGLBufferObject::GraphicsOpenGLBufferObject(void* openglContextPointer, + const GLuint bufferObjectName) +: CaretObject(), +m_openglContextPointer(openglContextPointer), +m_bufferObjectName(bufferObjectName) +{ + +} + +/** + * Destructor. + */ +GraphicsOpenGLBufferObject::~GraphicsOpenGLBufferObject() +{ + if (m_bufferObjectName > 0) { + EventGraphicsOpenGLDeleteBufferObject deleteEvent(this); + EventManager::get()->sendEvent(deleteEvent.getPointer()); + } +} + +/** + * Get a description of this object's content. + * @return String describing this object's content. + */ +AString +GraphicsOpenGLBufferObject::toString() const +{ + return "GraphicsOpenGLBufferObject"; +} + diff --git a/src/Graphics/GraphicsOpenGLBufferObject.h b/src/Graphics/GraphicsOpenGLBufferObject.h new file mode 100644 index 0000000000000000000000000000000000000000..68f7f2a70a5a02dbe4c2a4279ef859f5fbb2cb04 --- /dev/null +++ b/src/Graphics/GraphicsOpenGLBufferObject.h @@ -0,0 +1,75 @@ +#ifndef __GRAPHICS_OPEN_G_L_BUFFER_OBJECT_H__ +#define __GRAPHICS_OPEN_G_L_BUFFER_OBJECT_H__ + +/*LICENSE_START*/ +/* + * Copyright (C) 2017 Washington University School of Medicine + * + * 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 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ +/*LICENSE_END*/ + +#include "CaretOpenGLInclude.h" +#include "CaretObject.h" + + + +namespace caret { + + class GraphicsOpenGLBufferObject : public CaretObject { + + private: + GraphicsOpenGLBufferObject(void* openglContextPointer, + const GLuint bufferObjectName); + + public: + virtual ~GraphicsOpenGLBufferObject(); + + /** + * @return Pointer to OpenGL context in which buffer object was created. + */ + inline void* getOpenGLContextPointer() const { return m_openglContextPointer; } + + /** + * @return The OpenGL Buffer Object Name. + * While it is an unsigned interger, OpenGL refers to it as a "name". + */ + inline GLuint getBufferObjectName() const { return m_bufferObjectName; } + + // ADD_NEW_METHODS_HERE + + virtual AString toString() const; + + private: + GraphicsOpenGLBufferObject(const GraphicsOpenGLBufferObject&); + + GraphicsOpenGLBufferObject& operator=(const GraphicsOpenGLBufferObject&); + + void* m_openglContextPointer = NULL; + + const GLuint m_bufferObjectName = 0; + + friend class BrainOpenGL; + + // ADD_NEW_MEMBERS_HERE + + }; + +#ifdef __GRAPHICS_OPEN_G_L_BUFFER_OBJECT_DECLARE__ + // +#endif // __GRAPHICS_OPEN_G_L_BUFFER_OBJECT_DECLARE__ + +} // namespace +#endif //__GRAPHICS_OPEN_G_L_BUFFER_OBJECT_H__ diff --git a/src/Graphics/GraphicsOpenGLError.cxx b/src/Graphics/GraphicsOpenGLError.cxx new file mode 100644 index 0000000000000000000000000000000000000000..ed84b72c8e32c6fd84ae4f146a86789f916e95e4 --- /dev/null +++ b/src/Graphics/GraphicsOpenGLError.cxx @@ -0,0 +1,163 @@ + +/*LICENSE_START*/ +/* + * Copyright (C) 2018 Washington University School of Medicine + * + * 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 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ +/*LICENSE_END*/ + +#define __GRAPHICS_OPEN_G_L_ERROR_DECLARE__ +#include "GraphicsOpenGLError.h" +#undef __GRAPHICS_OPEN_G_L_ERROR_DECLARE__ + +#include "CaretAssert.h" +using namespace caret; + + + +/** + * \class caret::GraphicsOpenGLError + * \brief Contains information about OpenGL Errors. + * \ingroup Graphics + */ + +/** + * Constructor. + */ +GraphicsOpenGLError::GraphicsOpenGLError(const AString& userMessage) +: CaretObject(), +m_userMessage(userMessage) +{ + glGetIntegerv(GL_PROJECTION_STACK_DEPTH, + &m_projectionStackDepth); + glGetIntegerv(GL_MODELVIEW_STACK_DEPTH, + &m_modelviewStackDepth); + glGetIntegerv(GL_NAME_STACK_DEPTH, + &m_nameStackDepth); + + SystemUtilities::getBackTrace(m_backtrace); +} + +/** + * Destructor. + */ +GraphicsOpenGLError::~GraphicsOpenGLError() +{ +} + +/** + * Add an error code. + * + * @param errorCode + */ +void +GraphicsOpenGLError::addError(const GLenum errorCode) +{ + m_errorCodes.push_back(errorCode); +} + +/** + * @return Number of errors. OpenGL may have more than one error + * available. Set the man page for glGetError(). + */ +int32_t +GraphicsOpenGLError::getNumberOfErrors() const +{ + return m_errorCodes.size(); +} + +/** + * @return Error code for the given error index. + * + * @param errorIndex + * Error index. + */ +GLenum +GraphicsOpenGLError::getErrorCode(const int32_t errorIndex) const +{ + CaretAssertVectorIndex(m_errorCodes, errorIndex); + return m_errorCodes[errorIndex]; +} + +/** + * @return Description for the given error index. + * + * @param errorIndex + * Error index. + */ +AString +GraphicsOpenGLError::getErrorDescription(const int32_t errorIndex) const +{ + CaretAssertVectorIndex(m_errorCodes, errorIndex); + AString description((char*)gluErrorString(m_errorCodes[errorIndex])); + + return description; +} + +/** + * @return String with verbose description of error information. + * An empty string is returned if there are no errors. + */ +AString +GraphicsOpenGLError::getVerboseDescription() const +{ + if (m_errorCodes.empty()) { + return ""; + } + + AString msg; + + if ( ! m_userMessage.isEmpty()) { + msg.appendWithNewLine(m_userMessage); + } + + for (auto errorCode : m_errorCodes) { + msg.appendWithNewLine("OpenGL Error: " + + AString((char*)gluErrorString(errorCode)) + + " (" + + AString::number(static_cast(errorCode)) + + ")"); + } + + msg.appendWithNewLine("OpenGL Version: " + AString((char*)glGetString(GL_VERSION))); + msg.appendWithNewLine("OpenGL Vendor: " + AString((char*)glGetString(GL_VENDOR))); + + GLint maxNameStackDepth, maxModelStackDepth, maxProjStackDepth; + glGetIntegerv(GL_MAX_PROJECTION_STACK_DEPTH, + &maxProjStackDepth); + glGetIntegerv(GL_MAX_MODELVIEW_STACK_DEPTH, + &maxModelStackDepth); + glGetIntegerv(GL_MAX_NAME_STACK_DEPTH, + &maxNameStackDepth); + + msg.appendWithNewLine("Projection Matrix Stack Depth " + + AString::number(m_projectionStackDepth) + + " Max Depth " + + AString::number(maxProjStackDepth)); + msg.appendWithNewLine("Model Matrix Stack Depth " + + AString::number(m_modelviewStackDepth) + + " Max Depth " + + AString::number(maxModelStackDepth)); + msg.appendWithNewLine("Name Matrix Stack Depth " + + AString::number(m_nameStackDepth) + + " Max Depth " + + AString::number(maxNameStackDepth)); + msg.appendWithNewLine("Backtrace: " + + m_backtrace.toSymbolString()); + + return msg; +} + diff --git a/src/Graphics/GraphicsOpenGLError.h b/src/Graphics/GraphicsOpenGLError.h new file mode 100644 index 0000000000000000000000000000000000000000..5c7e371221e3a7f02bece78b82fb0939ee65e920 --- /dev/null +++ b/src/Graphics/GraphicsOpenGLError.h @@ -0,0 +1,92 @@ +#ifndef __GRAPHICS_OPEN_G_L_ERROR_H__ +#define __GRAPHICS_OPEN_G_L_ERROR_H__ + +/*LICENSE_START*/ +/* + * Copyright (C) 2018 Washington University School of Medicine + * + * 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 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ +/*LICENSE_END*/ + + + +#include + +#include "CaretObject.h" +#include "CaretOpenGLInclude.h" + + +namespace caret { + + class GraphicsOpenGLError : public CaretObject { + + public: + GraphicsOpenGLError(const AString& userMessage); + + virtual ~GraphicsOpenGLError(); + + int32_t getNumberOfErrors() const; + + GLenum getErrorCode(const int32_t errorIndex) const; + + AString getErrorDescription(const int32_t errorIndex) const; + + // ADD_NEW_METHODS_HERE + + AString getVerboseDescription() const; + + private: +// class ErrorInfo { +// public: +// ErrorInfo(const GLenum errorCode, +// const AString errorDescription) +// : m_errorCode(errorCode), +// m_errorDescription(errorDescription) { } +// +// const GLenum m_errorCode; +// +// const AString m_errorDescription; +// }; + + GraphicsOpenGLError(const GraphicsOpenGLError& obj); + + GraphicsOpenGLError& operator=(const GraphicsOpenGLError& obj); + + const AString m_userMessage; + + void addError(const GLenum errorCode); + + std::vector m_errorCodes; + + SystemBacktrace m_backtrace; + + int32_t m_projectionStackDepth = 0; + + int32_t m_modelviewStackDepth = 0; + + int32_t m_nameStackDepth = 0; + + // ADD_NEW_MEMBERS_HERE + + friend class GraphicsUtilitiesOpenGL; + }; + +#ifdef __GRAPHICS_OPEN_G_L_ERROR_DECLARE__ + // +#endif // __GRAPHICS_OPEN_G_L_ERROR_DECLARE__ + +} // namespace +#endif //__GRAPHICS_OPEN_G_L_ERROR_H__ diff --git a/src/Graphics/GraphicsOpenGLPolylineTriangles.cxx b/src/Graphics/GraphicsOpenGLPolylineTriangles.cxx new file mode 100644 index 0000000000000000000000000000000000000000..533424393357ce46125d5d01345cce4d1686c6af --- /dev/null +++ b/src/Graphics/GraphicsOpenGLPolylineTriangles.cxx @@ -0,0 +1,1380 @@ + +/*LICENSE_START*/ +/* + * Copyright (C) 2017 Washington University School of Medicine + * + * 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 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ +/*LICENSE_END*/ + +#include + +#define __GRAPHICS_OPENGL_POLYLINE_TRIANGLES_DECLARE__ +#include "GraphicsOpenGLPolylineTriangles.h" +#undef __GRAPHICS_OPENGL_POLYLINE_TRIANGLES_DECLARE__ + +#include "CaretAssert.h" +#include "CaretLogger.h" +#include "GraphicsEngineDataOpenGL.h" +#include "CaretOpenGLInclude.h" +#include "GraphicsPrimitive.h" +#include "GraphicsPrimitiveV3fC4f.h" +#include "GraphicsPrimitiveV3fC4ub.h" +#include "EventManager.h" +#include "EventOpenGLObjectToWindowTransform.h" +#include "MathFunctions.h" + +using namespace caret; + + + +/** + * \class caret::GraphicsOpenGLPolylineTriangles + * \brief Converts lines to rectangles + * \ingroup Graphics + * + * Convert lines to polygons. + * OpenGL lines have a limited width whose maximum is often exceeded. + * Convert the lines to rectangles. All drawing is done in + * two dimensional windows coordinates. + */ + +/** + * Constructor. + * + * @param xyz + * The XYZ vertices. + * @param floatRGBA + * The Float RGBA coloring. + * @param byteRGBA + * The Byte RGBA coloring. + * @param vertexPrimitiveRestartIndices + * Contains indices at which the primitive should restart. + * The primitive will stop at the index and not connect to the next index. + * @param lineThicknessPixels + * Thickness of lines in pixels. + * @param colorType + * Type of color (solid or per-vertex) + * @param lineType + * Type of lines drawn. + * @param joinType + * Type of joining for line loop and line strip + */ +GraphicsOpenGLPolylineTriangles::GraphicsOpenGLPolylineTriangles(const std::vector& xyz, + const std::vector& floatRGBA, + const std::vector& byteRGBA, + const std::set& vertexPrimitiveRestartIndices, + const float lineThicknessPixels, + const ColorType colorType, + const LineType lineType, + const JoinType joinType) +: m_inputXYZ(xyz), +m_inputFloatRGBA(floatRGBA), +m_inputByteRGBA(byteRGBA), +m_vertexPrimitiveRestartIndices(vertexPrimitiveRestartIndices), +m_lineThicknessPixels(lineThicknessPixels), +m_colorType(colorType), +m_lineType(lineType), +m_joinType(joinType) +{ +} + +/** + * Destructor. + */ +GraphicsOpenGLPolylineTriangles::~GraphicsOpenGLPolylineTriangles() +{ +} + +/** + * Convert a Workbench Line Primitive to an OpenGL primitive. + * OpenGL has limits on the width of the lines that it draws. This method will + * convert the lines into polygons so that any line width may be drawn. + * + * @param primitive + * A graphics primitive with a primitive type that is one of the + * WORKBENCH_LINE* types. + * @param errorMessageOut + * Upon exit contains an error message if conversion failed. + * @return + * A graphics primitive with an OPENGL_TRIANGLES type that draws the lines + * using triangles. NULL if error. + */ +GraphicsPrimitive* +GraphicsOpenGLPolylineTriangles::convertWorkbenchLinePrimitiveTypeToOpenGL(const GraphicsPrimitive* primitive, + AString& errorMessageOut) +{ + CaretAssert(primitive); + errorMessageOut.clear(); + + if ( ! primitive->isValid()) { + errorMessageOut = "Primitive is not valid."; + return NULL; + } + + float lineWidthPixels = primitive->m_lineWidthValue; + + switch (primitive->m_lineWidthType) { + case GraphicsPrimitive::LineWidthType::PERCENTAGE_VIEWPORT_HEIGHT: + { + GLint viewport[4]; + glGetIntegerv(GL_VIEWPORT, viewport); + lineWidthPixels = (lineWidthPixels / 100.0) * viewport[3]; + } + break; + case GraphicsPrimitive::LineWidthType::PIXELS: + break; + } + + /* enforce a minimum line width */ + const float minimumLineWidthPixels = 0.001f; + if (lineWidthPixels < minimumLineWidthPixels) { + lineWidthPixels = minimumLineWidthPixels; + } + + ColorType colorType = ColorType::FLOAT_RGBA_PER_VERTEX; + switch (primitive->m_colorDataType) { + case GraphicsPrimitive::ColorDataType::NONE: + errorMessageOut = "INVALID color type NONE for graphics primitive"; + return NULL; + break; + case GraphicsPrimitive::ColorDataType::FLOAT_RGBA: + colorType = ColorType::FLOAT_RGBA_PER_VERTEX; + break; + case GraphicsPrimitive::ColorDataType::UNSIGNED_BYTE_RGBA: + colorType = ColorType::BYTE_RGBA_PER_VERTEX; + break; + } + + LineType lineType = LineType::LINES; + JoinType joinType = JoinType::NONE; + switch (primitive->m_primitiveType) { + case GraphicsPrimitive::PrimitiveType::OPENGL_LINE_LOOP: + errorMessageOut = "Input type is OPENGL_LINE_LOOP but must be one of the POLYGONAL_LINE* types"; + break; + case GraphicsPrimitive::PrimitiveType::OPENGL_LINE_STRIP: + errorMessageOut = "Input type is OPENGL_LINE_STRIP but must be one of the POLYGONAL_LINE* types"; + break; + case GraphicsPrimitive::PrimitiveType::OPENGL_LINES: + errorMessageOut = "Input type is OPENGL_LINES but must be one of the POLYGONAL_LINE* types"; + break; + case GraphicsPrimitive::PrimitiveType::OPENGL_POINTS: + errorMessageOut = "Input type is OPENGL_POINTS but must be one of the POLYGONAL_LINE* types"; + break; + case GraphicsPrimitive::PrimitiveType::OPENGL_TRIANGLE_FAN: + errorMessageOut = "Input type is OPENGL_TRIANGLE_FAN but must be one of the POLYGONAL_LINE* types"; + break; + case GraphicsPrimitive::PrimitiveType::OPENGL_TRIANGLE_STRIP: + errorMessageOut = "Input type is OPENGL_TRIANGLE_STRIP but must be one of the POLYGONAL_LINE* types"; + break; + case GraphicsPrimitive::PrimitiveType::OPENGL_TRIANGLES: + errorMessageOut = "Input type is OPENGL_TRIANGLES but must be one of the POLYGONAL_LINE* types"; + break; + case GraphicsPrimitive::PrimitiveType::POLYGONAL_LINE_LOOP_BEVEL_JOIN: + lineType = LineType::LINE_LOOP; + joinType = JoinType::BEVEL; + break; + case GraphicsPrimitive::PrimitiveType::POLYGONAL_LINE_LOOP_MITER_JOIN: + lineType = LineType::LINE_LOOP; + joinType = JoinType::MITER; + break; + case GraphicsPrimitive::PrimitiveType::POLYGONAL_LINE_STRIP_BEVEL_JOIN: + lineType = LineType::LINE_STRIP; + joinType = JoinType::BEVEL; + break; + case GraphicsPrimitive::PrimitiveType::POLYGONAL_LINE_STRIP_MITER_JOIN: + lineType = LineType::LINE_STRIP; + joinType = JoinType::MITER; + break; + case GraphicsPrimitive::PrimitiveType::POLYGONAL_LINES: + lineType = LineType::LINES; + break; + case GraphicsPrimitive::PrimitiveType::SPHERES: + errorMessageOut = "Input type is OPENGL_TRIANGLES but must be one of the POLYGONAL_LINE* types"; + break; + } + + if ( ! errorMessageOut.isEmpty()) { + return NULL; + } + + GraphicsOpenGLPolylineTriangles lineConversion(primitive->m_xyz, + primitive->m_floatRGBA, + primitive->m_unsignedByteRGBA, + primitive->m_polygonalLinePrimitiveRestartIndices, + lineWidthPixels, + colorType, + lineType, + joinType); + + GraphicsPrimitive* primitiveOut = lineConversion.convertLinesToPolygons(errorMessageOut); + + return primitiveOut; +} + +/** + * Convert the content of this instance to a new graphics primitive + * that draws the lines as polygons. + * + * @param errorMessageOut + * Contains error message if conversion failed. + * @return + * Pointer to new primitive or NULL if there is an error. + * Caller must destroy the primitive. + */ +GraphicsPrimitive* +GraphicsOpenGLPolylineTriangles::convertLinesToPolygons(AString& errorMessageOut) +{ + errorMessageOut.clear(); + + const int32_t numVertices = static_cast(m_inputXYZ.size() / 3); + if (numVertices < 2) { + errorMessageOut = ("Invalid number of vertices=" + + AString::number(numVertices) + + " for line drawing. Must be at least 2."); + return NULL; + } + + const int32_t numFloatRGBA = static_cast(m_inputFloatRGBA.size()); + const int32_t numByteRGBA = static_cast(m_inputByteRGBA.size()); + switch (m_colorType) { + case ColorType::BYTE_RGBA_PER_VERTEX: + { + const int32_t numRgbaVertices = numByteRGBA / 4; + if (numVertices != numRgbaVertices) { + errorMessageOut = ("Mismatch in vertices and coloring. There are " + + AString::number(numVertices) + + " vertices but have byte coloring for " + + AString::number(numRgbaVertices) + + " vertices"); + return NULL; + } + } + break; + case ColorType::BYTE_RGBA_SOLID: + if (numByteRGBA < 4) { + errorMessageOut = ("Must have at last 4 color components for solid color line drawing" + "Number of color components is " + + AString::number(numByteRGBA)); + return NULL; + } + break; + case ColorType::FLOAT_RGBA_PER_VERTEX: + { + const int32_t numRgbaVertices = numFloatRGBA / 4; + if (numVertices != numRgbaVertices) { + errorMessageOut = ("Mismatch in vertices and coloring. There are " + + AString::number(numVertices) + + " vertices but have float coloring for " + + AString::number(numRgbaVertices) + + " vertices"); + return NULL; + } + } + break; + case ColorType::FLOAT_RGBA_SOLID: + if (numFloatRGBA < 4) { + errorMessageOut = ("Must have at last 4 color components for solid color line drawing" + "Number of color components is " + + AString::number(numFloatRGBA)); + return NULL; + } + break; + } + + saveOpenGLState(); + + createTransform(); + + createWindowCoordinatesFromVertices(); + + const int32_t numWindowPoints = static_cast(m_vertexWindowXYZ.size() / 3); + if (numWindowPoints >= 2) { + convertLineSegmentsToTriangles(); + } + + restoreOpenGLState(); + + GraphicsPrimitive* primitiveOut = NULL; + + if (m_primitive != NULL) { + if (m_primitive->isValid()) { + if (m_primitiveByteColor.get() != NULL) { + primitiveOut = m_primitiveByteColor.release(); + } + else if (m_primitiveFloatColor.get() != NULL) { + primitiveOut = m_primitiveFloatColor.release(); + } + else { + CaretAssert(0); + errorMessageOut = "Both Byte and Float Primitives are invalid, should never happen"; + } + } + else { + errorMessageOut = "Converted primitive is invalid."; + } + } + else { + errorMessageOut = "Primitive invalid, all points may be coincident"; + } + + return primitiveOut; +} + +/** + * Save the OpenGL State. + */ +void +GraphicsOpenGLPolylineTriangles::saveOpenGLState() +{ + glPushAttrib(GL_COLOR_BUFFER_BIT + | GL_ENABLE_BIT + | GL_HINT_BIT + | GL_LIGHTING_BIT + | GL_POLYGON_BIT); + + /* + * For anti-aliasing requires sorting polygons + * and turning of depth testing + */ + const bool drawWithAntiAliasingFlag = false; + if (drawWithAntiAliasingFlag) { + glBlendFunc (GL_SRC_ALPHA_SATURATE, GL_ONE); + glEnable(GL_BLEND); + glHint(GL_POLYGON_SMOOTH_HINT, + GL_NICEST); + glEnable(GL_POLYGON_SMOOTH); + } + + glDisable(GL_LIGHTING); + glDisable(GL_COLOR_MATERIAL); + glGetIntegerv(GL_POLYGON_MODE, + m_savedPolygonMode); + + glGetIntegerv(GL_VIEWPORT, + m_savedViewport); + glMatrixMode(GL_PROJECTION); + glPushMatrix(); + glMatrixMode(GL_MODELVIEW); + glPushMatrix(); +} + +/** + * Restore the OpenGL State. + */ +void +GraphicsOpenGLPolylineTriangles::restoreOpenGLState() +{ + glPolygonMode(GL_FRONT, m_savedPolygonMode[0]); + glPolygonMode(GL_BACK, m_savedPolygonMode[1]); + glMatrixMode(GL_PROJECTION); + glPopMatrix(); + glMatrixMode(GL_MODELVIEW); + glPopMatrix(); + glViewport(m_savedViewport[0], m_savedViewport[1], m_savedViewport[2], m_savedViewport[3]); + + glPopAttrib(); +} + +/** + * Create the transform matrix. + */ +void +GraphicsOpenGLPolylineTriangles::createTransform() +{ + m_transformEvent.reset(new EventOpenGLObjectToWindowTransform(EventOpenGLObjectToWindowTransform::SpaceType::WINDOW)); + EventManager::get()->sendEvent(m_transformEvent.get()->getPointer()); + if ( ! m_transformEvent->isValid()) { + CaretLogSevere("Getting OpenGL Transform Failed"); + return; + } +} + + +/** + * Create window coordinates from the input vertices. + */ +void +GraphicsOpenGLPolylineTriangles::createWindowCoordinatesFromVertices() +{ + const float coincidentMaxDistSQ = (0.01f * 0.01f); + + GLdouble depthRange[2]; + glGetDoublev(GL_DEPTH_RANGE, + depthRange); + + GLint viewport[4]; + glGetIntegerv(GL_VIEWPORT, + viewport); + + bool closeLoopFlag = false; + int32_t numInputPoints = static_cast(m_inputXYZ.size() / 3); + int32_t iStep = 1; + switch (m_lineType) { + case LineType::LINES: + iStep = 2; + if (MathFunctions::isOddNumber(numInputPoints)) { + CaretLogWarning("Odd number of points for drawing line segment pairs, should be even number."); + --numInputPoints; + } + break; + case LineType::LINE_LOOP: + closeLoopFlag = true; + iStep = 1; + if (numInputPoints > 3) { + /* + * For a loop, ignore the last point if it is the + * same as the first point + */ + const int32_t lastOffset = (numInputPoints - 1) * 3; + CaretAssertVectorIndex(m_inputXYZ, lastOffset + 2); + const float distance = MathFunctions::distance3D(&m_inputXYZ[0], + &m_inputXYZ[lastOffset]); + if (distance < 0.0001f) { + --numInputPoints; + } + } + break; + case LineType::LINE_STRIP: + iStep = 1; + break; + } + + m_vertexWindowXYZ.reserve(numInputPoints * 3); + m_vertexWindowInputIndices.reserve(numInputPoints); + + /* + * Coincident points that result in zero length + * line segments are filtered out + */ + for (int32_t i = 0; i < numInputPoints; i += iStep) { + const int32_t i3 = i * 3; + + /* + * Convert model coordinate to a window coordinate + * as described by the documentation from gluProject(). + */ + float windowXYZ[3]; + CaretAssertVectorIndex(m_inputXYZ, i3 + 1); + convertFromModelToWindowCoordinate(&m_inputXYZ[i3], windowXYZ); + + if (iStep == 1) { + if (i > 0) { + const float previousPointOffset = (m_vertexWindowXYZ.size() - 3); + CaretAssertVectorIndex(m_vertexWindowXYZ, previousPointOffset + 2); + const float distSQ = MathFunctions::distanceSquared3D(windowXYZ, + &m_vertexWindowXYZ[previousPointOffset]); + if (distSQ < coincidentMaxDistSQ) { + CaretLogFine("Filtered out connected segment conincident point index=" + AString::number(i) + + " after conversion to window coordinates. " + + AString::fromNumbers(windowXYZ, 3, ",") + + " " + + AString::fromNumbers(&m_vertexWindowXYZ[previousPointOffset], 3, ",")); + continue; + } + } + + m_vertexWindowXYZ.push_back(windowXYZ[0]); + m_vertexWindowXYZ.push_back(windowXYZ[1]); + m_vertexWindowXYZ.push_back(windowXYZ[2]); + m_vertexWindowInputIndices.push_back(i); + } + else if (iStep == 2) { + const int32_t iNext3 = (i + 1) * 3; + float windowNextXYZ[3]; + CaretAssertVectorIndex(m_inputXYZ, iNext3 + 2); + convertFromModelToWindowCoordinate(&m_inputXYZ[iNext3], + windowNextXYZ); + + const float distSQ = MathFunctions::distanceSquared3D(windowXYZ, + windowNextXYZ); + if (distSQ < coincidentMaxDistSQ) { + CaretLogFine("Filtered out line pairs conincident points " + + AString::number(i) + + " and " + + AString::number(i + 1)); + continue; + } + m_vertexWindowXYZ.push_back(windowXYZ[0]); + m_vertexWindowXYZ.push_back(windowXYZ[1]); + m_vertexWindowXYZ.push_back(windowXYZ[2]); + m_vertexWindowInputIndices.push_back(i); + + m_vertexWindowXYZ.push_back(windowNextXYZ[0]); + m_vertexWindowXYZ.push_back(windowNextXYZ[1]); + m_vertexWindowXYZ.push_back(windowNextXYZ[2]); + m_vertexWindowInputIndices.push_back(i + 1); + } + else { + CaretAssertMessage(0, "Invalid step value"); + } + + if (m_debugFlag) { + GLdouble modelviewArray[16]; + glGetDoublev(GL_MODELVIEW_MATRIX, modelviewArray); + GLdouble projectionArray[16]; + glGetDoublev(GL_PROJECTION_MATRIX, projectionArray); + GLdouble winX, winY, winZ; + gluProject(m_inputXYZ[i3], m_inputXYZ[i3+1], m_inputXYZ[i3+2], + modelviewArray, projectionArray, viewport, + &winX, &winY, &winZ); + std::cout << "Input: " << m_inputXYZ[i3] << ", " << m_inputXYZ[i3+1] << ", " << m_inputXYZ[i3+2] << std::endl; + std::cout << " Pt " << i << ": " + << winX << ", " << winY << ", " << winZ << std::endl; + std::cout << " gluProject " << winX << ", " << winY << ", " << winZ << std::endl; + } + } + + if (closeLoopFlag) { + /* + * Close by adding first two points to end (duplicates them). + * Two points are necessary for the joins so that there is + * not a gap where the last and first points connect. + */ + const int32_t numXYZ = (m_vertexWindowXYZ.size() / 3); + if (numXYZ > 3) { + const int32_t closeCount = 2; + for (int32_t i = 0; i < (closeCount * 3); i++) { + m_vertexWindowXYZ.push_back(m_vertexWindowXYZ[i]); + } + for (int32_t i = 0; i < closeCount; i++) { + m_vertexWindowInputIndices.push_back(m_vertexWindowInputIndices[i]); + } + } + } + + CaretAssert(m_vertexWindowXYZ.size() == (m_vertexWindowInputIndices.size() * 3)); +} + +/** + * Convert from a model coordinate to a window coordinate. + * + * @param modelXYZ + * The coordinate in model space. + * @param windowXYZOut + * Output with coordinate in window space. + */ +void +GraphicsOpenGLPolylineTriangles::convertFromModelToWindowCoordinate(const float modelXYZ[3], + float windowXYZOut[3]) const +{ + CaretAssert(m_transformEvent); + m_transformEvent->transformPoint(modelXYZ, + windowXYZOut); +} + +/** + * Create two triangles from the two window vertices + * + * @param windowVertexOneIndex + * Index of first window vertex at start of line + * @param windowVertexTwoIndex + * Index of second window vertex at end of line + */ +void +GraphicsOpenGLPolylineTriangles::createTrianglesFromWindowVertices(const int32_t windowVertexOneIndex, + const int32_t windowVertexTwoIndex) +{ + const int32_t iOne3 = windowVertexOneIndex * 3; + CaretAssertVectorIndex(m_vertexWindowXYZ, iOne3 + 2); + const int32_t iTwo3 = windowVertexTwoIndex * 3; + CaretAssertVectorIndex(m_vertexWindowXYZ, iTwo3 + 2); + float x1 = m_vertexWindowXYZ[iOne3]; + float y1 = m_vertexWindowXYZ[iOne3 + 1]; + float z1 = m_vertexWindowXYZ[iOne3 + 2]; + float x2 = m_vertexWindowXYZ[iTwo3]; + float y2 = m_vertexWindowXYZ[iTwo3 + 1]; + float z2 = m_vertexWindowXYZ[iTwo3 + 2]; + + /* + * Normalized vector from start to end on line segment in 2D coordinates + */ + float startToEndVector[3] = { + x2 - x1, + y2 - y1, + 0.0f + }; + const float lineLength = MathFunctions::normalizeVector(startToEndVector); + if (lineLength < 0.001f) { + CaretLogWarning("This should not happen: Failure to filter coincident points with indices=" + + AString::number(windowVertexOneIndex) + + ", " + + AString::number(windowVertexTwoIndex) + + " Coordinates: " + + AString::fromNumbers(&m_vertexWindowXYZ[iOne3], 3, ",") + + " " + + AString::fromNumbers(&m_vertexWindowXYZ[iTwo3], 3, ",")); + } + + /* + * Create a perpendicualar vector to the line segment + */ + float perpendicularVector[3] = { + startToEndVector[1], + -startToEndVector[0], + 0.0f + }; + + /* + * "Width" of rectangle + */ + const float halfWidth = m_lineThicknessPixels / 2.0f; + const float halfWidthX = perpendicularVector[0] * halfWidth; + const float halfWidthY = perpendicularVector[1] * halfWidth; + + /* + * Points of the rectangle + * They should be counter-clockwise oriented + */ + float p1[3] = { + x1 - halfWidthX, + y1 - halfWidthY, + z1 + }; + float p2[3] = { + x1 + halfWidthX, + y1 + halfWidthY, + z1 + }; + float p3[3] = { + x2 + halfWidthX, + y2 + halfWidthY, + z2 + }; + float p4[3] = { + x2 - halfWidthX, + y2 - halfWidthY, + z2 + }; + + /* + * Indices to input coloring. + * Note that coincident points may be removed + */ + CaretAssertVectorIndex(m_vertexWindowInputIndices, windowVertexOneIndex); + const int32_t indexOneRgba = m_vertexWindowInputIndices[windowVertexOneIndex]; + CaretAssertVectorIndex(m_vertexWindowInputIndices, windowVertexTwoIndex); + const int32_t indexTwoRgba = m_vertexWindowInputIndices[windowVertexTwoIndex]; + + /* + * RGBA for the points P1, P2, P3, P4 + */ + int32_t rgbaOffsetP1P2 = 0; + int32_t rgbaOffsetP3P4 = 0; + switch (m_colorType) { + case ColorType::BYTE_RGBA_PER_VERTEX: + rgbaOffsetP1P2 = indexOneRgba * 4; + rgbaOffsetP3P4 = indexTwoRgba * 4; + break; + case ColorType::BYTE_RGBA_SOLID: + break; + case ColorType::FLOAT_RGBA_PER_VERTEX: + rgbaOffsetP1P2 = indexOneRgba * 4; + rgbaOffsetP3P4 = indexTwoRgba * 4; + break; + case ColorType::FLOAT_RGBA_SOLID: + break; + } + + float crossProduct[3]; + MathFunctions::crossProduct(startToEndVector, perpendicularVector, crossProduct); + if (crossProduct[2] == 0.0f) { + /* This should get caught by vector length check above */ + } + + switch (m_colorType) { + case ColorType::BYTE_RGBA_PER_VERTEX: + case ColorType::BYTE_RGBA_SOLID: + { + CaretAssertVectorIndex(m_inputByteRGBA, rgbaOffsetP1P2 + 3); + const uint8_t* rgbaP12 = &m_inputByteRGBA[rgbaOffsetP1P2]; + CaretAssertVectorIndex(m_inputByteRGBA, rgbaOffsetP3P4 + 3); + const uint8_t* rgbaP34 = &m_inputByteRGBA[rgbaOffsetP3P4]; + + CaretAssert(m_primitiveByteColor.get()); + if (crossProduct[2] <= 0.0f) { + m_primitiveByteColor->addVertex(p1, rgbaP12); + m_primitiveByteColor->addVertex(p2, rgbaP12); + m_primitiveByteColor->addVertex(p4, rgbaP34); + + m_primitiveByteColor->addVertex(p2, rgbaP12); + m_primitiveByteColor->addVertex(p3, rgbaP34); + m_primitiveByteColor->addVertex(p4, rgbaP34); + } + else { + if (m_debugFlag) std::cout << "Flipped Quad" << std::endl; + m_primitiveByteColor->addVertex(p2, rgbaP12); + m_primitiveByteColor->addVertex(p1, rgbaP12); + m_primitiveByteColor->addVertex(p3, rgbaP34); + + m_primitiveByteColor->addVertex(p1, rgbaP12); + m_primitiveByteColor->addVertex(p4, rgbaP34); + m_primitiveByteColor->addVertex(p3, rgbaP34); + } + } + break; + case ColorType::FLOAT_RGBA_PER_VERTEX: + case ColorType::FLOAT_RGBA_SOLID: + { + CaretAssertVectorIndex(m_inputFloatRGBA, rgbaOffsetP1P2 + 3); + const float* rgbaP12 = &m_inputFloatRGBA[rgbaOffsetP1P2]; + CaretAssertVectorIndex(m_inputFloatRGBA, rgbaOffsetP3P4 + 3); + const float* rgbaP34 = &m_inputFloatRGBA[rgbaOffsetP3P4]; + + CaretAssert(m_primitiveFloatColor.get()); + if (crossProduct[2] <= 0.0f) { + m_primitiveFloatColor->addVertex(p1, rgbaP12); + m_primitiveFloatColor->addVertex(p2, rgbaP12); + m_primitiveFloatColor->addVertex(p4, rgbaP34); + + m_primitiveFloatColor->addVertex(p2, rgbaP12); + m_primitiveFloatColor->addVertex(p3, rgbaP34); + m_primitiveFloatColor->addVertex(p4, rgbaP34); + + } + else { + if (m_debugFlag) std::cout << "Flipped Quad" << std::endl; + m_primitiveFloatColor->addVertex(p2, rgbaP12); + m_primitiveFloatColor->addVertex(p1, rgbaP12); + m_primitiveFloatColor->addVertex(p3, rgbaP34); + + m_primitiveFloatColor->addVertex(p1, rgbaP12); + m_primitiveFloatColor->addVertex(p4, rgbaP34); + m_primitiveFloatColor->addVertex(p3, rgbaP34); + } + } + break; + } + + const int32_t numVertices = m_primitive->getNumberOfVertices(); + const int32_t triangleOneIndices[3] = { + numVertices - 6, + numVertices - 5, + numVertices - 4 + }; + const int32_t triangleTwoIndices[3] = { + numVertices - 3, + numVertices - 2, + numVertices - 1 + }; + + PolylineInfo polyLine(windowVertexOneIndex, + windowVertexTwoIndex, + triangleOneIndices, + triangleTwoIndices); + + if (m_lastPolyLineInfo.m_valid) { + joinTwoPolyLines(m_lastPolyLineInfo, polyLine); + } + + m_lastPolyLineInfo = polyLine; +} + +/** + * Join two polylines by filling in the gaps where the + * polylines meet. + * + * @param polyOne + * The first polyline. + * @param polyTwo + * The two polyline. + */ +void +GraphicsOpenGLPolylineTriangles::joinTwoPolyLines(const PolylineInfo& polyOne, + const PolylineInfo& polyTwo) +{ + CaretAssert(m_primitive); + + switch (m_joinType) { + case JoinType::BEVEL: + break; + case JoinType::MITER: + break; + case JoinType::NONE: + return; + break; + } + + switch (m_lineType) { + case LineType::LINES: + return; + break; + case LineType::LINE_LOOP: + break; + case LineType::LINE_STRIP: + break; + } + + /* + * Does triangle "i" connect to triangle "i+1" ? + */ + if (polyOne.m_windowVertexTwoIndex == polyTwo.m_windowVertexOneIndex) { + switch (m_joinType) { + case JoinType::BEVEL: + performBevelJoin(polyOne, + polyTwo); + break; + case JoinType::MITER: + performMiterJoin(polyOne, + polyTwo); + break; + case JoinType::NONE: + break; + } + } + + addJoinTriangles(); +} + +/** + * Determine the turn direction at the connection point of two line segments. + * + * @param polyOne + * Info about first polyline + * @param polyTwo + * Info about second polyline + * @return + * The turn direction. + */ +GraphicsOpenGLPolylineTriangles::TurnDirection +GraphicsOpenGLPolylineTriangles::getTurnDirection(const PolylineInfo& polyOne, + const PolylineInfo& polyTwo) const +{ + TurnDirection turnDirection = TurnDirection::PARALLEL; + + const int32_t windowVertexIndexOne = polyOne.m_windowVertexOneIndex; + const int32_t windowVertexIndexTwo = polyOne.m_windowVertexTwoIndex; + const int32_t windowVertexIndexThree = polyTwo.m_windowVertexTwoIndex; + + if (m_debugFlag) std::cout << "Turn Direction: " << windowVertexIndexOne << ", " << windowVertexIndexTwo << ", " << windowVertexIndexThree << std::endl; + CaretAssert(m_primitive); + + /* + * Determine if the junction of the two consecutive + * line segments in window coordinates makes a left + * or right turn + */ + CaretAssertVectorIndex(m_vertexWindowXYZ, (windowVertexIndexOne * 3) + 2); + CaretAssertVectorIndex(m_vertexWindowXYZ, (windowVertexIndexTwo * 3) + 2); + CaretAssertVectorIndex(m_vertexWindowXYZ, (windowVertexIndexThree * 3) + 2); + const float signedArea = MathFunctions::triangleAreaSigned2D(&m_vertexWindowXYZ[windowVertexIndexOne * 3], + &m_vertexWindowXYZ[windowVertexIndexTwo * 3], + &m_vertexWindowXYZ[windowVertexIndexThree * 3]); + + + const float smallNumber = 0.0f; + if (signedArea > smallNumber) { + turnDirection = TurnDirection::LEFT; + } + else if (signedArea < -smallNumber) { + turnDirection = TurnDirection::RIGHT; + } + else { + turnDirection = TurnDirection::PARALLEL; + } + + return turnDirection; +} + +/** + * Join the second triangle in poly one to poly two with a bevel join. + * A triangle is added that connects the outside corners of the + * two polylines. + * + * @param polyOne + * Info about triangles in first polyline + * @param polyTwo + * Info about triangles in second polyline + */ +void +GraphicsOpenGLPolylineTriangles::performBevelJoin(const PolylineInfo& polyOne, + const PolylineInfo& polyTwo) +{ + const TurnDirection turnDirection = getTurnDirection(polyOne, + polyTwo); + + const int32_t windowVertexIndexTwo = polyOne.m_windowVertexTwoIndex; + + if (m_debugFlag) { + const int32_t windowVertexIndexOne = polyOne.m_windowVertexOneIndex; + const int32_t windowVertexIndexThree = polyTwo.m_windowVertexTwoIndex; + std::cout << "Join: " << windowVertexIndexOne << ", " << windowVertexIndexTwo << ", " << windowVertexIndexThree << std::endl; + } + CaretAssert(m_primitive); + + /* + * Determine if the junction of the two consecutive + * line segments in window coordinates makes a left + * or right turn + */ + CaretAssertVectorIndex(m_vertexWindowXYZ, (windowVertexIndexTwo * 3) + 2); + + int32_t quadOneSideEndVertexIndex = -1; + int32_t quadTwoSideBeginVertexIndex = -1; + switch (turnDirection) { + case TurnDirection::LEFT: + if (m_debugFlag) std::cout << " Left Turn" << std::endl; + + /* + * Join the right side that is first two vertices in triangle two + */ + quadOneSideEndVertexIndex = polyOne.m_triangleTwoPrimitiveIndices[1]; + quadTwoSideBeginVertexIndex = polyTwo.m_triangleTwoPrimitiveIndices[0]; + break; + case TurnDirection::PARALLEL: + if (m_debugFlag) std::cout << " Parallel" << std::endl; + return; + break; + case TurnDirection::RIGHT: + if (m_debugFlag) std::cout << " Right Turn" << std::endl; + + /* + * Join left side that is first and third vertices in triangle one + */ + quadOneSideEndVertexIndex = polyOne.m_triangleOnePrimitiveIndices[2]; + quadTwoSideBeginVertexIndex = polyTwo.m_triangleOnePrimitiveIndices[0]; + break; + } + + /* + * Add a triangle to fill in the gap + */ + m_triangleJoins.push_back(TriangleJoin(windowVertexIndexTwo, + quadOneSideEndVertexIndex, + quadTwoSideBeginVertexIndex)); +} + +/** + * Join the second triangle in poly one to poly two with a miter join. + * A miter join extends the 'outside' edges of the turn to their intersection. + * If the angle is sharp (very small) this intersection may be long way + * from the original vertex. The miter limit keeps this from happening by + * extending the intersection just a little bit and adding essentially a + * bevel-like join. + * + * @param polyOne + * Info about triangles in first polyline + * @param polyTwo + * Info about triangles in second polyline + */ +void +GraphicsOpenGLPolylineTriangles::performMiterJoin(const PolylineInfo& polyOne, + const PolylineInfo& polyTwo) +{ + const TurnDirection turnDirection = getTurnDirection(polyOne, + polyTwo); + + const int32_t windowVertexIndexTwo = polyOne.m_windowVertexTwoIndex; + + if (m_debugFlag) { + const int32_t windowVertexIndexOne = polyOne.m_windowVertexOneIndex; + const int32_t windowVertexIndexThree = polyTwo.m_windowVertexTwoIndex; + std::cout << "Miter Join: " << windowVertexIndexOne << ", " << windowVertexIndexTwo << ", " << windowVertexIndexThree << std::endl; + } + CaretAssert(m_primitive); + + /* + * Determine if the junction of the two consecutive + * line segments in window coordinates makes a left + * or right turn + */ + CaretAssertVectorIndex(m_vertexWindowXYZ, (windowVertexIndexTwo * 3) + 2); + + int32_t quadOneSideBeginVertexIndex = -1; + int32_t quadOneSideEndVertexIndex = -1; + int32_t quadTwoSideBeginVertexIndex = -1; + int32_t quadTwoSideEndVertexIndex = -1; + bool leftTurnFlag = false; + switch (turnDirection) { + case TurnDirection::LEFT: + leftTurnFlag = true; + if (m_debugFlag) std::cout << " Miter Left Turn" << std::endl; + + /* + * Join the right side that is first two vertices in triangle two + */ + quadOneSideBeginVertexIndex = polyOne.m_triangleTwoPrimitiveIndices[0]; + quadOneSideEndVertexIndex = polyOne.m_triangleTwoPrimitiveIndices[1]; + quadTwoSideBeginVertexIndex = polyTwo.m_triangleTwoPrimitiveIndices[0]; + quadTwoSideEndVertexIndex = polyTwo.m_triangleTwoPrimitiveIndices[1]; + break; + case TurnDirection::PARALLEL: + if (m_debugFlag) std::cout << " Miter Parallel" << std::endl; + return; + break; + case TurnDirection::RIGHT: + leftTurnFlag = false; + if (m_debugFlag) std::cout << " Miter Right Turn" << std::endl; + + /* + * Join left side that is first and third vertices in triangle one + */ + quadOneSideBeginVertexIndex = polyOne.m_triangleOnePrimitiveIndices[0]; + quadOneSideEndVertexIndex = polyOne.m_triangleOnePrimitiveIndices[2]; + quadTwoSideBeginVertexIndex = polyTwo.m_triangleOnePrimitiveIndices[0]; + quadTwoSideEndVertexIndex = polyTwo.m_triangleOnePrimitiveIndices[2]; + break; + } + + const std::vector& xyz = m_primitive->getFloatXYZ(); + CaretAssertVectorIndex(xyz, (quadOneSideBeginVertexIndex * 3) + 2); + CaretAssertVectorIndex(xyz, (quadOneSideEndVertexIndex * 3) + 2); + CaretAssertVectorIndex(xyz, (quadTwoSideBeginVertexIndex * 3) + 2); + CaretAssertVectorIndex(xyz, (quadTwoSideEndVertexIndex * 3) + 2); + const float* q1v1 = &xyz[quadOneSideBeginVertexIndex * 3]; + const float* q1v2 = &xyz[quadOneSideEndVertexIndex * 3]; + const float* q2v1 = &xyz[quadTwoSideBeginVertexIndex * 3]; + const float* q2v2 = &xyz[quadTwoSideEndVertexIndex * 3]; + + /* + * Miter limit is a multiple of the line thickness + */ + const float miterLimit = m_lineThicknessPixels; + const float miterLimitSquared = miterLimit * miterLimit; + + /* + * Find location of where the edges in the quads intersect + */ + float intersectionXYZ[3]; + if (MathFunctions::vectorIntersection2D(q1v1, q1v2, q2v1, q2v2, 0.0f, intersectionXYZ)) { + intersectionXYZ[2] = q1v1[2]; + if (m_debugFlag) std::cout << " Miter Intersection: " << AString::fromNumbers(intersectionXYZ, 3, ",") << std::endl; + + /* + * When the angle is very sharp, the miter point can be very long and pointy + * so limit to a multiplier of the line thickness + */ + CaretAssertVectorIndex(m_vertexWindowXYZ, (windowVertexIndexTwo * 3) + 2); + const float* joinPointXYZ = &m_vertexWindowXYZ[(windowVertexIndexTwo * 3)]; + const float distanceSquared = MathFunctions::distanceSquared3D(joinPointXYZ, + intersectionXYZ); + if (distanceSquared > miterLimitSquared) { + /* + * Extend lines by the miter limit to create a new quad + */ + float newV1[3]; + createMiterJoinVertex(q1v1, q1v2, miterLimit, newV1); + float newV2[3]; + createMiterJoinVertex(q2v2, q2v1, miterLimit, newV2); + + /* + * Extend vertices to the new miter limit + */ + int32_t primitiveIndexV1 = -1; + int32_t primitiveIndexV2 = -1; + if (leftTurnFlag) { + m_primitive->replaceVertexFloatXYZ(polyOne.m_triangleTwoPrimitiveIndices[1], newV1); + primitiveIndexV1 = polyOne.m_triangleTwoPrimitiveIndices[1]; + m_primitive->replaceVertexFloatXYZ(polyTwo.m_triangleOnePrimitiveIndices[1], newV2); + primitiveIndexV2 = polyTwo.m_triangleOnePrimitiveIndices[1]; + m_primitive->replaceVertexFloatXYZ(polyTwo.m_triangleTwoPrimitiveIndices[0], newV2); + } + else { + m_primitive->replaceVertexFloatXYZ(polyOne.m_triangleTwoPrimitiveIndices[2], newV1); + primitiveIndexV1 = polyOne.m_triangleTwoPrimitiveIndices[2]; + m_primitive->replaceVertexFloatXYZ(polyOne.m_triangleOnePrimitiveIndices[2], newV1); + m_primitive->replaceVertexFloatXYZ(polyTwo.m_triangleOnePrimitiveIndices[0], newV2); + primitiveIndexV2 = polyTwo.m_triangleOnePrimitiveIndices[0]; + } + + /* + * Add a triangle to fill in the gap + */ + m_triangleJoins.push_back(TriangleJoin(windowVertexIndexTwo, + primitiveIndexV1, + primitiveIndexV2)); + } + else { + /* + * Move the existing quad vertices to their intersection point + */ + if (leftTurnFlag) { + m_primitive->replaceVertexFloatXYZ(polyOne.m_triangleTwoPrimitiveIndices[1], intersectionXYZ); + m_primitive->replaceVertexFloatXYZ(polyTwo.m_triangleOnePrimitiveIndices[1], intersectionXYZ); + m_primitive->replaceVertexFloatXYZ(polyTwo.m_triangleTwoPrimitiveIndices[0], intersectionXYZ); + } + else { + m_primitive->replaceVertexFloatXYZ(polyOne.m_triangleTwoPrimitiveIndices[2], intersectionXYZ); + m_primitive->replaceVertexFloatXYZ(polyOne.m_triangleOnePrimitiveIndices[2], intersectionXYZ); + m_primitive->replaceVertexFloatXYZ(polyTwo.m_triangleOnePrimitiveIndices[0], intersectionXYZ); + } + } + } +} + +/** + * Create a vertex for a miter join when the length limit is exceeded + * + * @param v1 + * First vertex + * @param v2 + * Second vertex that is connected to the miter join vertex. + * @param miterLength + * Length of output vertex from v2 + * @param xyzOut + * Output with vertex used for miter join. + */ +void +GraphicsOpenGLPolylineTriangles::createMiterJoinVertex(const float v1[3], + const float v2[3], + const float miterLength, + float xyzOut[3]) +{ + float n2[3]; + MathFunctions::subtractVectors(v2, v1, n2); + MathFunctions::normalizeVector(n2); + n2[0] *= miterLength; + n2[1] *= miterLength; + n2[2] *= miterLength; + xyzOut[0] = v2[0] + n2[0]; + xyzOut[1] = v2[1] + n2[1]; + xyzOut[2] = v2[2] + n2[2]; +} + +/** + * Get the average color from two vertices. + * + * @param primitiveVertexOneIndex + * The first primitive vertex index. + * @param primitiveVertexTwoIndex + * The second primitive vertex index. + * @param rgbaOut + * Output with average RGBA. + */ +void +GraphicsOpenGLPolylineTriangles::getAverageColorRGBA(const int32_t primitiveVertexOneIndex, + const int32_t primitiveVertexTwoIndex, + float rgbaOut[4]) const +{ + CaretAssert(m_primitiveFloatColor); + + float rgbaOne[4]; + m_primitiveFloatColor->getVertexFloatRGBA(primitiveVertexOneIndex, rgbaOne); + float rgbaTwo[4]; + m_primitiveFloatColor->getVertexFloatRGBA(primitiveVertexTwoIndex, rgbaTwo); + + rgbaOut[0] = (rgbaOne[0] + rgbaTwo[0]) / 2.0f; + rgbaOut[1] = (rgbaOne[1] + rgbaTwo[1]) / 2.0f; + rgbaOut[2] = (rgbaOne[2] + rgbaTwo[2]) / 2.0f; + rgbaOut[3] = (rgbaOne[3] + rgbaTwo[3]) / 2.0f; +} + +/** + * Get the average color from two vertices. + * + * @param primitiveVertexOneIndex + * The first primitive vertex index. + * @param primitiveVertexTwoIndex + * The second primitive vertex index. + * @param rgbaOut + * Output with average RGBA. + */ +void +GraphicsOpenGLPolylineTriangles::getAverageColorRGBA(const int32_t primitiveVertexOneIndex, + const int32_t primitiveVertexTwoIndex, + uint8_t rgbaOut[4]) const +{ + CaretAssert(m_primitiveByteColor); + + /* + * Since byte colors range [0, 255], adding two bytes could + * cause an overflow of the byte. So average using floats + * and then assign to the byte output. + */ + uint8_t rgbaOneByte[4]; + m_primitiveByteColor->getVertexByteRGBA(primitiveVertexOneIndex, rgbaOneByte); + float rgbaOne[4] = { (float)rgbaOneByte[0], (float)rgbaOneByte[1], (float)rgbaOneByte[2], (float)rgbaOneByte[3] }; + + uint8_t rgbaTwoByte[4]; + m_primitiveByteColor->getVertexByteRGBA(primitiveVertexTwoIndex, rgbaTwoByte); + float rgbaTwo[4] = { (float)rgbaTwoByte[0], (float)rgbaTwoByte[1], (float)rgbaTwoByte[2], (float)rgbaTwoByte[3] }; + + rgbaOut[0] = static_cast((rgbaOne[0] + rgbaTwo[0]) / 2.0f); + rgbaOut[1] = static_cast((rgbaOne[1] + rgbaTwo[1]) / 2.0f); + rgbaOut[2] = static_cast((rgbaOne[2] + rgbaTwo[2]) / 2.0f); + rgbaOut[3] = static_cast((rgbaOne[3] + rgbaTwo[3]) / 2.0f); +} + +/** + * Add triangles creating by joins to fill gaps + * at the junctions of the line segments + */ +void +GraphicsOpenGLPolylineTriangles::addJoinTriangles() +{ + for (const auto tj : m_triangleJoins) { + CaretAssertVectorIndex(m_vertexWindowXYZ, tj.m_windowVertexIndex * 3 + 2); + const float* v1 = &m_vertexWindowXYZ[tj.m_windowVertexIndex*3]; + + const std::vector& xyz = m_primitive->getFloatXYZ(); + CaretAssertVectorIndex(xyz, tj.m_primitiveVertexOneIndex * 3 + 2); + CaretAssertVectorIndex(xyz, tj.m_primitiveVertexTwoIndex * 3 + 2); + + const float* v2 = &xyz[tj.m_primitiveVertexOneIndex * 3]; + const float* v3 = &xyz[tj.m_primitiveVertexTwoIndex * 3]; + + /* + * If signed area is positive, the triangle is correctly oriented; + * otherwise, flip the vertices + */ + const float signedArea = MathFunctions::triangleAreaSigned2D(v1, v2, v3); + const float smallNumber = 0.0f; + const bool normalOutFlag = (signedArea > smallNumber); + + const float* tv2 = (normalOutFlag ? v2 : v3); + const float* tv3 = (normalOutFlag ? v3 : v2); + + switch (m_primitive->getColorDataType()) { + case GraphicsPrimitive::ColorDataType::NONE: + CaretAssert(0); + break; + case GraphicsPrimitive::ColorDataType::FLOAT_RGBA: + { + float rgba[4]; + getAverageColorRGBA(tj.m_primitiveVertexOneIndex, tj.m_primitiveVertexTwoIndex, rgba); + m_primitiveFloatColor->addVertex(v1, rgba); + m_primitiveFloatColor->addVertex(tv2, rgba); + m_primitiveFloatColor->addVertex(tv3, rgba); + } + break; + case GraphicsPrimitive::ColorDataType::UNSIGNED_BYTE_RGBA: + { + uint8_t rgba[4]; + getAverageColorRGBA(tj.m_primitiveVertexOneIndex, tj.m_primitiveVertexTwoIndex, rgba); + m_primitiveByteColor->addVertex(v1, rgba); + m_primitiveByteColor->addVertex(tv2, rgba); + m_primitiveByteColor->addVertex(tv3, rgba); + } + break; + } + } + + m_triangleJoins.clear(); +} + +/** + * Convert each line segment into two triangles + * using the line width. "Length" of the + * two triangles is the length of the line segment + * and "width" of the two triangles is the line width. + */ +void +GraphicsOpenGLPolylineTriangles::convertLineSegmentsToTriangles() +{ + const int32_t numVertices = static_cast(m_vertexWindowXYZ.size() / 3); + + /* + * When estimating number of triangles, + * include both the triangles formed by the vertices + * and possible triangles added for joining end points + */ + int32_t estimatedNumberOfQuads = 0; + int32_t maximumNumberOfJoinTriangles = 0; + switch (m_lineType) { + case LineType::LINES: + estimatedNumberOfQuads = numVertices / 2; + maximumNumberOfJoinTriangles = 0; + break; + case LineType::LINE_LOOP: + estimatedNumberOfQuads = numVertices * 2; + maximumNumberOfJoinTriangles = numVertices; + break; + case LineType::LINE_STRIP: + estimatedNumberOfQuads = (numVertices - 1) * 2; + maximumNumberOfJoinTriangles = numVertices - 1; + break; + } + + int32_t estimatedNumberOfTriangles = ((estimatedNumberOfQuads * 2) + + maximumNumberOfJoinTriangles); + const int32_t estimatedNumberOfVertices = estimatedNumberOfTriangles * 3; + + m_primitive = NULL; + switch (m_colorType) { + case ColorType::BYTE_RGBA_PER_VERTEX: + case ColorType::BYTE_RGBA_SOLID: + m_primitiveByteColor.reset(GraphicsPrimitive::newPrimitiveV3fC4ub(GraphicsPrimitive::PrimitiveType::OPENGL_TRIANGLES)); + m_primitiveByteColor->reserveForNumberOfVertices(estimatedNumberOfVertices); + m_primitive = m_primitiveByteColor.get(); + break; + case ColorType::FLOAT_RGBA_PER_VERTEX: + case ColorType::FLOAT_RGBA_SOLID: + m_primitiveFloatColor.reset(GraphicsPrimitive::newPrimitiveV3fC4f(GraphicsPrimitive::PrimitiveType::OPENGL_TRIANGLES)); + m_primitiveFloatColor->reserveForNumberOfVertices(estimatedNumberOfVertices); + m_primitive = m_primitiveFloatColor.get(); + break; + } + CaretAssert(m_primitive); + + m_primitive->setUsageTypeAll(GraphicsPrimitive::UsageType::MODIFIED_ONCE_DRAWN_FEW_TIMES); + + const int32_t numVerticesMinusOne = numVertices - 1; + + std::vector restartVertexFlags(numVertices, false); + for (auto index : m_vertexPrimitiveRestartIndices) { + /* + * The caller may add a primtive restart at the last vertex + * which may be equal to numVertices. + */ + if (index < numVertices) { + CaretAssertVectorIndex(restartVertexFlags, index); + restartVertexFlags[index] = true; + } + } + + const bool restartEnabledFlag = true; + + switch (m_lineType) { + case LineType::LINES: + { + const int32_t numLineSegments = (numVertices / 2); + for (int32_t i = 0; i < numLineSegments; i++) { + const int32_t i2 = i * 2; + createTrianglesFromWindowVertices(i2, i2 + 1); + } + } + break; + case LineType::LINE_LOOP: + for (int32_t i = 0; i < numVerticesMinusOne; i++) { + CaretAssertVectorIndex(restartVertexFlags, i + 1); + if (restartVertexFlags[i + 1]) { + if (restartEnabledFlag) continue; + } + createTrianglesFromWindowVertices(i, i + 1); + } + createTrianglesFromWindowVertices(numVerticesMinusOne, 0); + break; + case LineType::LINE_STRIP: + for (int32_t i = 0; i < numVerticesMinusOne; i++) { + CaretAssertVectorIndex(restartVertexFlags, i + 1); + if (restartVertexFlags[i + 1]) { + if (restartEnabledFlag) continue; + } + createTrianglesFromWindowVertices(i, i + 1); + } + break; + } +} + +/** + * Get a description of this object's content. + * @return String describing this object's content. + */ +AString +GraphicsOpenGLPolylineTriangles::toString() const +{ + return "GraphicsOpenGLPolylineTriangles"; +} + diff --git a/src/Graphics/GraphicsOpenGLPolylineTriangles.h b/src/Graphics/GraphicsOpenGLPolylineTriangles.h new file mode 100644 index 0000000000000000000000000000000000000000..6916cd580f14a0593e74fe4e3ebd5f97571684a1 --- /dev/null +++ b/src/Graphics/GraphicsOpenGLPolylineTriangles.h @@ -0,0 +1,262 @@ +#ifndef __GRAPHICS_OPENGL_POLYLINE_TRIANGLES_H__ +#define __GRAPHICS_OPENGL_POLYLINE_TRIANGLES_H__ + +/*LICENSE_START*/ +/* + * Copyright (C) 2017 Washington University School of Medicine + * + * 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 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ +/*LICENSE_END*/ + + + +#include +#include + +#include "CaretObject.h" + +namespace caret { + + class EventOpenGLObjectToWindowTransform; + class GraphicsPrimitive; + class GraphicsPrimitiveV3fC4f; + class GraphicsPrimitiveV3fC4ub; + + class GraphicsOpenGLPolylineTriangles : public CaretObject { + + public: + virtual ~GraphicsOpenGLPolylineTriangles(); + + static GraphicsPrimitive* convertWorkbenchLinePrimitiveTypeToOpenGL(const GraphicsPrimitive* primitive, + AString& errorMessageOut); + + // ADD_NEW_METHODS_HERE + + virtual AString toString() const; + + private: + /** + * Type of line coloring + */ + enum class ColorType { + /** Byte RGBA color at each vertex */ + BYTE_RGBA_PER_VERTEX, + /** One Byte RGBA color for all vertices */ + BYTE_RGBA_SOLID, + /** Float RGBA color at each vertex */ + FLOAT_RGBA_PER_VERTEX, + /** One Float RGBA color for all vertices */ + FLOAT_RGBA_SOLID + }; + + /** + * Join tyupe of how connected lines are drawn to remove gaps between segments + */ + enum class JoinType { + /** None */ + NONE, + /** Bevel fills the gap by adding a triangle */ + BEVEL, + /** Miter extends 'outsides' of lines to meet forming a pointed shape */ + MITER + }; + + /** + * Type of line drawing + */ + enum class LineType { + /** Each pair of vertices is an independent line segment (GL_LINES) */ + LINES, + /** A connected set of lines forming a loop (last is automaticall connected to first) */ + LINE_LOOP, + /** A connected set of lines (last is not connected to first) */ + LINE_STRIP + }; + + /** + * Turn direction add joint vertex of two lines + */ + enum class TurnDirection { + /** Turns left */ + LEFT, + /** Does not turn (straight) or coincident */ + PARALLEL, + /** Turns right */ + RIGHT, + }; + + /** + * Indices into input vertices of line + * that formed the polyline + */ + class PolylineInfo { + public: + PolylineInfo() { } + + PolylineInfo(const int32_t windowVertexOneIndex, + const int32_t windowVertexTwoIndex, + const int32_t triangleOnePrimitiveIndices[3], + const int32_t triangleTwoPrimitiveIndices[3]) + : m_windowVertexOneIndex(windowVertexOneIndex), + m_windowVertexTwoIndex(windowVertexTwoIndex) { + m_triangleOnePrimitiveIndices[0] = triangleOnePrimitiveIndices[0]; + m_triangleOnePrimitiveIndices[1] = triangleOnePrimitiveIndices[1]; + m_triangleOnePrimitiveIndices[2] = triangleOnePrimitiveIndices[2]; + m_triangleTwoPrimitiveIndices[0] = triangleTwoPrimitiveIndices[0]; + m_triangleTwoPrimitiveIndices[1] = triangleTwoPrimitiveIndices[1]; + m_triangleTwoPrimitiveIndices[2] = triangleTwoPrimitiveIndices[2]; + m_valid = true; + } + + int32_t m_windowVertexOneIndex; + int32_t m_windowVertexTwoIndex; + int32_t m_triangleOnePrimitiveIndices[3]; + int32_t m_triangleTwoPrimitiveIndices[3]; + bool m_valid = false; + }; + + /** + * Triangles created by joining of polylines + */ + class TriangleJoin { + public: + TriangleJoin(const int32_t windowVertexIndex, + const int32_t primitiveVertexOneIndex, + const int32_t primitiveVertexTwoIndex) + : m_windowVertexIndex(windowVertexIndex), + m_primitiveVertexOneIndex(primitiveVertexOneIndex), + m_primitiveVertexTwoIndex(primitiveVertexTwoIndex) + { + + } + + const int32_t m_windowVertexIndex; + const int32_t m_primitiveVertexOneIndex; + const int32_t m_primitiveVertexTwoIndex; + }; + + GraphicsOpenGLPolylineTriangles(const std::vector& xyz, + const std::vector& floatRGBA, + const std::vector& byteRGBA, + const std::set& vertexPrimitiveRestartIndices, + const float lineThicknessPixels, + const ColorType colorType, + const LineType lineType, + const JoinType joinType); + + GraphicsOpenGLPolylineTriangles(const GraphicsOpenGLPolylineTriangles& obj); + + GraphicsOpenGLPolylineTriangles& operator=(const GraphicsOpenGLPolylineTriangles& obj) const; + + GraphicsPrimitive* convertLinesToPolygons(AString& errorMessageOut); + + void saveOpenGLState(); + + void restoreOpenGLState(); + + void createTransform(); + + void convertFromModelToWindowCoordinate(const float modelXYZ[3], + float windowXYZOut[3]) const; + + void createWindowCoordinatesFromVertices(); + + void convertLineSegmentsToTriangles(); + + void createTrianglesFromWindowVertices(const int32_t windowVertexOneIndex, + const int32_t windowVertexTwoIndex); + + void performBevelJoin(const PolylineInfo& polyOne, + const PolylineInfo& polyTwo); + + void performMiterJoin(const PolylineInfo& polyOne, + const PolylineInfo& polyTwo); + + void joinTwoPolyLines(const PolylineInfo& polyOne, + const PolylineInfo& polyTwo); + + void addJoinTriangles(); + + void createMiterJoinVertex(const float v1[3], + const float v2[3], + const float miterLength, + float xyzOut[3]); + + TurnDirection getTurnDirection(const PolylineInfo& polyOne, + const PolylineInfo& polyTwo) const; + + void getAverageColorRGBA(const int32_t primitiveVertexOneIndex, + const int32_t primitiveVertexTwoIndex, + float rgbaOut[4]) const; + + void getAverageColorRGBA(const int32_t primitiveVertexOneIndex, + const int32_t primitiveVertexTwoIndex, + uint8_t rgbaOut[4]) const; + + const std::vector& m_inputXYZ; + + const std::vector& m_inputFloatRGBA; + + const std::vector& m_inputByteRGBA; + + std::set m_vertexPrimitiveRestartIndices; + + const float m_lineThicknessPixels; + + const ColorType m_colorType; + + const LineType m_lineType; + + const JoinType m_joinType; + + std::vector m_vertexWindowXYZ; + + std::vector m_vertexWindowInputIndices; + + bool m_debugFlag = false; + + int m_savedPolygonMode[2]; + + int m_savedViewport[4]; + + std::unique_ptr m_transformEvent; + + /* + * The base class graphics primitive that points to + * either the Float or Byte Color primitive and can + * be used for non-color operations. NEVER delete + * this as it points to one the color primitive + */ + GraphicsPrimitive* m_primitive = NULL; + + std::unique_ptr m_primitiveFloatColor; + + std::unique_ptr m_primitiveByteColor; + + PolylineInfo m_lastPolyLineInfo; + + std::vector m_triangleJoins; + + // ADD_NEW_MEMBERS_HERE + + }; + +#ifdef __GRAPHICS_OPENGL_POLYLINE_TRIANGLES_DECLARE__ + // +#endif // __GRAPHICS_OPENGL_POLYLINE_TRIANGLES_DECLARE__ + +} // namespace +#endif //__GRAPHICS_OPENGL_POLYLINE_TRIANGLES_H__ diff --git a/src/Graphics/GraphicsOpenGLTextureName.cxx b/src/Graphics/GraphicsOpenGLTextureName.cxx new file mode 100644 index 0000000000000000000000000000000000000000..2240fdbef26525bf9393ba5720002ae9ab5f672f --- /dev/null +++ b/src/Graphics/GraphicsOpenGLTextureName.cxx @@ -0,0 +1,77 @@ + +/*LICENSE_START*/ +/* + * Copyright (C) 2017 Washington University School of Medicine + * + * 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 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ +/*LICENSE_END*/ + +#define __GRAPHICS_OPEN_G_L_TEXTURE_NAME_DECLARE__ +#include "GraphicsOpenGLTextureName.h" +#undef __GRAPHICS_OPEN_G_L_TEXTURE_NAME_DECLARE__ + +#include "CaretAssert.h" +#include "EventGraphicsOpenGLDeleteTextureName.h" +#include "EventManager.h" + +using namespace caret; + + + +/** + * \class caret::GraphicsOpenGLTextureName + * \brief Contains the OpenGL Texture Name and the context in which it was created. + * \ingroup Graphics + */ + +/** + * Constructor. + * + * @param openglContextPointer + * Pointer to OpenGL context in which texture name was created. + * @param textureName + * The OpenGL texture name. + */ +GraphicsOpenGLTextureName::GraphicsOpenGLTextureName(void* openglContextPointer, + const GLuint textureName) +: CaretObject(), +m_openglContextPointer(openglContextPointer), +m_textureName(textureName) +{ + +} + +/** + * Destructor. + */ +GraphicsOpenGLTextureName::~GraphicsOpenGLTextureName() +{ + if (m_textureName > 0) { + EventGraphicsOpenGLDeleteTextureName deleteEvent(this); + EventManager::get()->sendEvent(deleteEvent.getPointer()); + } +} + +/** + * Get a description of this object's content. + * @return String describing this object's content. + */ +AString +GraphicsOpenGLTextureName::toString() const +{ + return "GraphicsOpenGLTextureName"; +} + diff --git a/src/Graphics/GraphicsOpenGLTextureName.h b/src/Graphics/GraphicsOpenGLTextureName.h new file mode 100644 index 0000000000000000000000000000000000000000..b72157b309d9a4fa3df8a90c498773a279bfe3a1 --- /dev/null +++ b/src/Graphics/GraphicsOpenGLTextureName.h @@ -0,0 +1,75 @@ +#ifndef __GRAPHICS_OPEN_G_L_TEXTURE_NAME_H__ +#define __GRAPHICS_OPEN_G_L_TEXTURE_NAME_H__ + +/*LICENSE_START*/ +/* + * Copyright (C) 2017 Washington University School of Medicine + * + * 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 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ +/*LICENSE_END*/ + +#include "CaretOpenGLInclude.h" +#include "CaretObject.h" + + + +namespace caret { + + class GraphicsOpenGLTextureName : public CaretObject { + + private: + GraphicsOpenGLTextureName(void* openglContextPointer, + const GLuint textureName); + + public: + virtual ~GraphicsOpenGLTextureName(); + + /** + * @return Pointer to OpenGL context in which texture name was created. + */ + inline void* getOpenGLContextPointer() const { return m_openglContextPointer; } + + /** + * @return The OpenGL Texture Name Name. + * While it is an unsigned interger, OpenGL refers to it as a "name". + */ + inline GLuint getTextureName() const { return m_textureName; } + + // ADD_NEW_METHODS_HERE + + virtual AString toString() const; + + private: + GraphicsOpenGLTextureName(const GraphicsOpenGLTextureName&); + + GraphicsOpenGLTextureName& operator=(const GraphicsOpenGLTextureName&); + + void* m_openglContextPointer = NULL; + + const GLuint m_textureName = 0; + + friend class BrainOpenGL; + + // ADD_NEW_MEMBERS_HERE + + }; + +#ifdef __GRAPHICS_OPEN_G_L_TEXTURE_NAME_DECLARE__ + // +#endif // __GRAPHICS_OPEN_G_L_TEXTURE_NAME_DECLARE__ + +} // namespace +#endif //__GRAPHICS_OPEN_G_L_TEXTURE_NAME_H__ diff --git a/src/Graphics/GraphicsPrimitive.cxx b/src/Graphics/GraphicsPrimitive.cxx new file mode 100644 index 0000000000000000000000000000000000000000..4b9b44a7932da1d479ba8a79811a39dd7442e467 --- /dev/null +++ b/src/Graphics/GraphicsPrimitive.cxx @@ -0,0 +1,1593 @@ + +/*LICENSE_START*/ +/* + * Copyright (C) 2017 Washington University School of Medicine + * + * 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 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ +/*LICENSE_END*/ + +#define __GRAPHICS_PRIMITIVE_DECLARE__ +#include "GraphicsPrimitive.h" +#undef __GRAPHICS_PRIMITIVE_DECLARE__ + +#include "BoundingBox.h" +#include "CaretAssert.h" +#include "CaretLogger.h" +#include "EventManager.h" +#include "GraphicsEngineDataOpenGL.h" +#include "GraphicsPrimitiveV3f.h" +#include "GraphicsPrimitiveV3fC4f.h" +#include "GraphicsPrimitiveV3fC4ub.h" +#include "GraphicsPrimitiveV3fN3f.h" +#include "GraphicsPrimitiveV3fT3f.h" + +using namespace caret; + + + +/** + * \class caret::GraphicsPrimitive + * \brief Abstract class for a graphics primitive. + * \ingroup Graphics + * + */ + +/** + * Constructs an instance with the given vertex, normal vector, and color type. + * + * @param vertexDataType + * Data type of the vertices. + * @param normalVectorDataType + * Data type of the normal vectors. + * @param colorDataType + * Data type of the colors. + * @param vertexColorType + * Type of vertex coloring + * @param textureDataType + * Data type of texture coordinates. + * @param primitiveType + * Type of primitive drawn (triangles, lines, etc.) + */ +GraphicsPrimitive::GraphicsPrimitive(const VertexDataType vertexDataType, + const NormalVectorDataType normalVectorDataType, + const ColorDataType colorDataType, + const VertexColorType vertexColorType, + const TextureDataType textureDataType, + const PrimitiveType primitiveType) +: CaretObject(), + EventListenerInterface(), + m_vertexDataType(vertexDataType), + m_normalVectorDataType(normalVectorDataType), + m_colorDataType(colorDataType), + m_vertexColorType(vertexColorType), + m_textureDataType(textureDataType), + m_primitiveType(primitiveType), + m_boundingBoxValid(false) +{ + +} + +/** + * Destructor. + */ +GraphicsPrimitive::~GraphicsPrimitive() +{ + EventManager::get()->removeAllEventsFromListener(this); +} + +/** + * Copy constructor for graphics primitive. + * @param obj + * Object that is copied. + */ +GraphicsPrimitive::GraphicsPrimitive(const GraphicsPrimitive& obj) +: CaretObject(obj), + EventListenerInterface(), + m_vertexDataType(obj.m_vertexDataType), + m_normalVectorDataType(obj.m_normalVectorDataType), + m_colorDataType(obj.m_colorDataType), + m_vertexColorType(obj.m_vertexColorType), + m_textureDataType(obj.m_textureDataType), + m_primitiveType(obj.m_primitiveType), + m_boundingBoxValid(false) +{ + this->copyHelperGraphicsPrimitive(obj); +} + +/** + * Helps with copying an object of this type. + * @param obj + * Object that is copied. + */ +void +GraphicsPrimitive::copyHelperGraphicsPrimitive(const GraphicsPrimitive& obj) +{ + m_boundingBoxValid = false; + m_xyz = obj.m_xyz; + m_floatNormalVectorXYZ = obj.m_floatNormalVectorXYZ; + m_floatRGBA = obj.m_floatRGBA; + m_unsignedByteRGBA = obj.m_unsignedByteRGBA; + m_floatTextureSTR = obj.m_floatTextureSTR; + m_lineWidthType = obj.m_lineWidthType; + m_lineWidthValue = obj.m_lineWidthValue; + m_pointSizeType = obj.m_pointSizeType; + m_pointDiameterValue = obj.m_pointDiameterValue; + m_polygonalLinePrimitiveRestartIndices = obj.m_polygonalLinePrimitiveRestartIndices; + m_sphereSizeType = obj.m_sphereSizeType; + m_sphereDiameterValue = obj.m_sphereDiameterValue; + m_textureImageBytesRGBA = obj.m_textureImageBytesRGBA; + m_textureImageWidth = obj.m_textureImageWidth; + m_textureImageHeight = obj.m_textureImageHeight; + + m_graphicsEngineDataForOpenGL.reset(); +} + +/** + * Request the capacity of the primitive for the given number of vertices. + * Using this method can help reduce reallocations as data is added + * to the primitive. Note: the primitive data is stored in + * std::vector's and this method functions identically to + * std::vector::reserve(). + * + * @param numberOfVertices + * Number of vertices + */ +void +GraphicsPrimitive::reserveForNumberOfVertices(const int32_t numberOfVertices) +{ + switch (m_vertexDataType) { + case VertexDataType::FLOAT_XYZ: + m_xyz.reserve(numberOfVertices * 3); + break; + } + + switch (m_normalVectorDataType) { + case NormalVectorDataType::FLOAT_XYZ: + m_floatNormalVectorXYZ.reserve(numberOfVertices * 3); + break; + case NormalVectorDataType::NONE: + break; + } + + switch (m_colorDataType) { + case ColorDataType::NONE: + break; + case ColorDataType::FLOAT_RGBA: + m_floatRGBA.reserve(numberOfVertices * 4); + break; + case ColorDataType::UNSIGNED_BYTE_RGBA: + m_unsignedByteRGBA.reserve(numberOfVertices * 4); + break; + } + + switch (m_textureDataType) { + case GraphicsPrimitive::TextureDataType::NONE: + break; + case GraphicsPrimitive::TextureDataType::FLOAT_STR: + m_floatTextureSTR.reserve(numberOfVertices * 3); + break; + } + + m_boundingBoxValid = false; +} + +/** + * @return The coordinates usage type hint for graphics system. + */ +GraphicsPrimitive::UsageType +GraphicsPrimitive::getUsageTypeCoordinates() const +{ + return m_usageTypeCoordinates; +} +/** + * @return The normals usage type hint for graphics system. + */ +GraphicsPrimitive::UsageType +GraphicsPrimitive::getUsageTypeNormals() const +{ + return m_usageTypeNormals; +} + +/** + * @return The colors usage type hint for graphics system. + */ +GraphicsPrimitive::UsageType +GraphicsPrimitive::getUsageTypeColors() const +{ + return m_usageTypeColors; +} + +/** + * @return The texture coordinates usage type hint for graphics system. + */ +GraphicsPrimitive::UsageType +GraphicsPrimitive::getUsageTypeTextureCoordinates() const +{ + return m_usageTypeTextureCoordinates; +} + +/** + * Set the usage type hint for all data types for graphics system. + * This will override any of the individual (vertex, normal, etc) + * usage types so they should be called after calling this method. + * + * @param usageType + * New value for usage type. + */ +void +GraphicsPrimitive::setUsageTypeAll(const UsageType usageType) +{ + m_usageTypeCoordinates = usageType; + m_usageTypeNormals = usageType; + m_usageTypeColors = usageType; + m_usageTypeTextureCoordinates = usageType; +} +/** + * Set the coordinates usage type hint for graphics system. + * + * @param usageType + * New value for usage type. + */ +void +GraphicsPrimitive::setUsageTypeCoordinates(const UsageType usageType) +{ + m_usageTypeCoordinates = usageType; +} + +/** + * Set the normals usage type hint for graphics system. + * + * @param usageType + * New value for usage type. + */ +void +GraphicsPrimitive::setUsageTypeNormals(const UsageType usageType) +{ + m_usageTypeNormals = usageType; +} + +/** + * Set the colors usage type hint for graphics system. + * + * @param usageType + * New value for usage type. + */ +void +GraphicsPrimitive::setUsageTypeColors(const UsageType usageType) +{ + m_usageTypeColors = usageType; +} + +/** + * Set the texture coordinates usage type hint for graphics system. + * + * @param usageType + * New value for usage type. + */ +void +GraphicsPrimitive::setUsageTypeTextureCoordinates(const UsageType usageType) +{ + m_usageTypeTextureCoordinates = usageType; +} + +/** + * @return Is this graphics primitive valid. + * Primitive is valid if all of these conditions are met + * (A) ((count(xyz) * 3) == (count(rgba) * 4)) + * (B) (count(xyz) > 0) + * (C) ((count(xyz) == count(normals)) OR (count(normals) == 0) + */ +bool +GraphicsPrimitive::isValid() const +{ + switch (m_vertexDataType) { + case VertexDataType::FLOAT_XYZ: + break; + } + + const uint32_t numXYZ = m_xyz.size(); + if (numXYZ > 0) { + switch (m_normalVectorDataType) { + case NormalVectorDataType::NONE: + break; + case NormalVectorDataType::FLOAT_XYZ: + { + const uint32_t numNormalXYZ = m_floatNormalVectorXYZ.size(); + if (numNormalXYZ > 0) { + if (numNormalXYZ != numXYZ) { + CaretLogWarning("ERROR: GraphicsPrimitive XYZ size not matching surface normal size."); + return false; + } + } + } + break; + } + + bool haveRgbaFlag = false; + uint32_t numColorRGBA = 0; + switch (m_colorDataType) { + case ColorDataType::NONE: + haveRgbaFlag = false; + break; + case ColorDataType::FLOAT_RGBA: + numColorRGBA = m_floatRGBA.size(); + haveRgbaFlag = true; + break; + case ColorDataType::UNSIGNED_BYTE_RGBA: + numColorRGBA = m_unsignedByteRGBA.size(); + haveRgbaFlag = true; + break; + } + if (haveRgbaFlag) { + if ((numXYZ / 3) != (numColorRGBA / 4)) { + CaretLogWarning("ERROR: GraphicsPrimitive XYZ size does not match RGBA size"); + return false; + } + } + + switch (m_vertexColorType) { + case VertexColorType::NONE: + if (haveRgbaFlag) { + CaretLogWarning("ERROR: GraphicsPrimitive VertexColorType is NONE but have RGBA ColorDataType"); + } + break; + case VertexColorType::PER_VERTEX_RGBA: + case VertexColorType::SOLID_RGBA: + if ( ! haveRgbaFlag) { + CaretLogWarning("ERROR: GraphicsPrimitive VertexColorType is RGBA but have NONE ColorDataType"); + } + break; + } + + bool haveTextureFlag = false; + uint32_t numTextureSTR = 0; + switch (m_textureDataType) { + case TextureDataType::NONE: + break; + case TextureDataType::FLOAT_STR: + numTextureSTR = m_floatTextureSTR.size(); + haveTextureFlag = true; + break; + } + if (haveTextureFlag) { + if (numXYZ != numTextureSTR) { + CaretLogWarning("ERROR: GraphicsPrimitive XYZ size does not match Texture STR size"); + return false; + } + + if (m_textureImageBytesRGBA.empty()) { + CaretLogWarning("ERROR: GraphicsPrimitive has invalid texture data"); + } + } + + switch (m_primitiveType) { + case PrimitiveType::OPENGL_LINE_LOOP: + case PrimitiveType::POLYGONAL_LINE_LOOP_BEVEL_JOIN: + case PrimitiveType::POLYGONAL_LINE_LOOP_MITER_JOIN: + if (numXYZ < 3) { + CaretLogWarning("Line loop must have at least 3 vertices."); + } + break; + case PrimitiveType::OPENGL_LINE_STRIP: + case PrimitiveType::POLYGONAL_LINE_STRIP_BEVEL_JOIN: + case PrimitiveType::POLYGONAL_LINE_STRIP_MITER_JOIN: + if (numXYZ < 2) { + CaretLogWarning("Line strip must have at least 2 vertices."); + } + break; + case PrimitiveType::OPENGL_LINES: + case PrimitiveType::POLYGONAL_LINES: + if (numXYZ < 2) { + CaretLogWarning("Lines must have at least 2 vertices."); + } + else { + const uint32_t extraVertices = numXYZ % 2; + if (extraVertices > 0) { + CaretLogWarning("Extra vertices for drawing lines ignored."); + } + } + break; + case PrimitiveType::OPENGL_POINTS: + break; + case PrimitiveType::OPENGL_TRIANGLE_FAN: + if (numXYZ < 3) { + CaretLogWarning("Triangle fan must have at least 3 vertices."); + } + break; + case PrimitiveType::OPENGL_TRIANGLE_STRIP: + if (numXYZ < 3) { + CaretLogWarning("Triangle strip must have at least 3 vertices."); + } + break; + case PrimitiveType::OPENGL_TRIANGLES: + if (numXYZ < 3) { + CaretLogWarning("Triangles must have at least 3 vertices."); + } + else { + const uint32_t extraVertices = numXYZ % 3; + if (extraVertices > 0) { + CaretLogWarning("Extra vertices for drawing triangles ignored."); + } + } + break; + case PrimitiveType::SPHERES: + if (numXYZ < 1) { + CaretLogWarning("Spheres must have at least 1 vertices."); + } + break; + } + + return true; + } + + return false; +} + +/** + * Print the primitive's data, data may be long! + */ +void +GraphicsPrimitive::print() const +{ + std::cout << toStringPrivate(true) << std::endl; +} + + +/** + * Get a description of this object's content. + * @return String describing this object's content. + */ +AString +GraphicsPrimitive::toString() const +{ + return toStringPrivate(false); +} + +/** + * @return Type of primitive as text. + */ +AString +GraphicsPrimitive::getPrimitiveTypeAsText() const +{ + AString s; + + switch (m_primitiveType) { + case PrimitiveType::OPENGL_LINE_LOOP: + s = "OpenGL Line Loop"; + break; + case PrimitiveType::OPENGL_LINE_STRIP: + s = "OpenGL Line Strip"; + break; + case PrimitiveType::OPENGL_LINES: + s = "OpenGL Lines"; + break; + case PrimitiveType::OPENGL_POINTS: + s = "OpenGL Points"; + break; + case PrimitiveType::OPENGL_TRIANGLE_FAN: + s = "OpenGL Triangle Fan"; + break; + case PrimitiveType::OPENGL_TRIANGLE_STRIP: + s = "OpenGL Triangle Strip"; + break; + case PrimitiveType::OPENGL_TRIANGLES: + s = "OpenGL Triangles"; + break; + case PrimitiveType::POLYGONAL_LINE_LOOP_BEVEL_JOIN: + s = "Polygonal Line Loop Bevel Join"; + break; + case PrimitiveType::POLYGONAL_LINE_LOOP_MITER_JOIN: + s = "Polygonal Line Loop Miter Join"; + break; + case PrimitiveType::POLYGONAL_LINE_STRIP_BEVEL_JOIN: + s = "Polygonal Line Strip Bevel Join"; + break; + case PrimitiveType::POLYGONAL_LINE_STRIP_MITER_JOIN: + s = "Polygonal Line Strip Miter Join"; + break; + case PrimitiveType::POLYGONAL_LINES: + s = "Polygonal Lines"; + break; + case PrimitiveType::SPHERES: + s = "Spheres"; + break; + } + + return s; +} + +/** + * @return Sphere size type as text for the given size type. + * + * @param sizeType + * The size type. + */ +AString +GraphicsPrimitive::getSphereSizeTypeAsText(const SphereSizeType sizeType) const +{ + AString s; + + switch (sizeType) { + case SphereSizeType::MILLIMETERS: + s = "Millimeters"; + break; + } + return s; +} + +/** + * @return Vertex color type as text for the given vertex color type. + * + * @param vertexColorType + * The vertex color type. + */ +AString +GraphicsPrimitive::getVertexColorTypeAsText(const VertexColorType vertexColorType) const +{ + AString s; + + switch (vertexColorType) { + case VertexColorType::NONE: + s = "None"; + break; + case VertexColorType::PER_VERTEX_RGBA: + s = "Per Vertex RGBA"; + break; + case VertexColorType::SOLID_RGBA: + s = "Solid RGBA"; + break; + } + + return s; +} + +/** + * @return Point size type as text for the given size type. + * + * @param sizeType + * The size type. + */ +AString +GraphicsPrimitive::getPointSizeTypeAsText(const PointSizeType sizeType) const +{ + AString s; + + switch (sizeType) { + case PointSizeType::MILLIMETERS: + s = "Millimeters"; + break; + case PointSizeType::PERCENTAGE_VIEWPORT_HEIGHT: + s = "Percentage Viewport Height"; + break; + case PointSizeType::PIXELS: + s = "Pixels"; + break; + } + return s; +} + + +/** + * @return Line width type as text for the given line width type. + * + * @param lineWidthType + * The line width type. + */ +AString +GraphicsPrimitive::getLineWidthTypeAsText(const LineWidthType lineWidthType) const +{ + AString s; + + switch (lineWidthType) { + case LineWidthType::PERCENTAGE_VIEWPORT_HEIGHT: + s = "Percentage Viewport Height"; + break; + case LineWidthType::PIXELS: + s = "Pixels"; + break; + } + return s; +} + +/** + * Convert to string. + * + * @param includeAllDataFlag + * If true, numerical values are included. Else, only type of data. + */ +AString +GraphicsPrimitive::toStringPrivate(const bool includeAllDataFlag) const +{ + AString s("Primitive: " + getPrimitiveTypeAsText()); + + const int32_t numVertices = getNumberOfVertices(); + s.appendWithNewLine("Number of Vertices: " + AString::number(numVertices) + "\n"); + + switch (m_textureDataType) { + case TextureDataType::NONE: + break; + case TextureDataType::FLOAT_STR: + s.appendWithNewLine("Texture: " + AString::number(m_floatTextureSTR.size()) + " Float Texture 0.0 to 1.0. "); + s.appendWithNewLine(" Width: " + AString::number(m_textureImageWidth) + + " Height: " + AString::number(m_textureImageHeight)); + break; + } + + bool addLineWidthFlag = false; + bool addPointSizeFlag = false; + bool addSphereSizeFlag = false; + + switch (m_primitiveType) { + case PrimitiveType::OPENGL_LINE_LOOP: + addLineWidthFlag = true; + break; + case PrimitiveType::OPENGL_LINE_STRIP: + addLineWidthFlag = true; + break; + case PrimitiveType::OPENGL_LINES: + addLineWidthFlag = true; + break; + case PrimitiveType::OPENGL_POINTS: + addPointSizeFlag = true; + break; + case PrimitiveType::OPENGL_TRIANGLE_FAN: + break; + case PrimitiveType::OPENGL_TRIANGLE_STRIP: + break; + case PrimitiveType::OPENGL_TRIANGLES: + break; + case PrimitiveType::POLYGONAL_LINE_LOOP_BEVEL_JOIN: + case PrimitiveType::POLYGONAL_LINE_LOOP_MITER_JOIN: + addLineWidthFlag = true; + break; + case PrimitiveType::POLYGONAL_LINE_STRIP_BEVEL_JOIN: + case PrimitiveType::POLYGONAL_LINE_STRIP_MITER_JOIN: + addLineWidthFlag = true; + break; + case PrimitiveType::POLYGONAL_LINES: + addLineWidthFlag = true; + break; + case PrimitiveType::SPHERES: + addSphereSizeFlag = true; + break; + } + + if ( ! m_polygonalLinePrimitiveRestartIndices.empty()) { + s.appendWithNewLine("Polygonal Line Primitive Restart Indices Count: " + AString::number(m_polygonalLinePrimitiveRestartIndices.size())); + if (includeAllDataFlag) { + std::vector temp(m_polygonalLinePrimitiveRestartIndices.begin(), + m_polygonalLinePrimitiveRestartIndices.end()); + s.appendWithNewLine(" " + AString::fromNumbers(temp, ", ")); + } + } + + if (addLineWidthFlag) { + s.appendWithNewLine("Line Width Type: " + + getLineWidthTypeAsText(m_lineWidthType) + + "; Value: " + + AString::number(m_lineWidthValue, 'f', 3)); + } + + if (addPointSizeFlag) { + s.appendWithNewLine("Point Diameter Type: " + + getPointSizeTypeAsText(m_pointSizeType) + + "; Value: " + + AString::number(m_pointDiameterValue, 'f', 3)); + } + + if (addSphereSizeFlag) { + s.appendWithNewLine("Sphere Diameter Type: " + + getSphereSizeTypeAsText(m_sphereSizeType) + + "; Value: " + + AString::number(m_sphereDiameterValue, 'f', 3)); + } + + s.appendWithNewLine("Vertex Color Type: " + + getVertexColorTypeAsText(m_vertexColorType)); + + s.append("\n"); + if (includeAllDataFlag) { + for (int32_t i = 0; i < numVertices; i++) { + s.append(AString("%1: ").arg(i, 5)); + + switch (m_vertexDataType) { + case VertexDataType::FLOAT_XYZ: + { + CaretAssertVectorIndex(m_xyz, i*3 + 2); + const float* xyz = &m_xyz[i * 3]; + s.append(AString("%1, %2, %3").arg(xyz[0], 10, 'f', 3).arg(xyz[1], 10, 'f', 3).arg(xyz[2], 10, 'f', 3)); + } + break; + } + + switch (m_normalVectorDataType) { + case NormalVectorDataType::FLOAT_XYZ: + { + CaretAssertVectorIndex(m_floatNormalVectorXYZ, i*3 + 2); + const float* xyz = &m_floatNormalVectorXYZ[i * 3]; + s.append(AString(" N:%1, %2, %3").arg(xyz[0], 7, 'f', 5).arg(xyz[1], 7, 'f', 5).arg(xyz[2], 7, 'f', 5)); + } + case NormalVectorDataType::NONE: + break; + } + + switch (m_colorDataType) { + case ColorDataType::NONE: + break; + case ColorDataType::FLOAT_RGBA: + { + CaretAssertVectorIndex(m_floatRGBA, i*4 + 3); + const float* rgba = &m_floatRGBA[i * 4]; + s.append(AString(" RGBAf: %1, %2, %3, %4").arg(rgba[0], 5, 'f', 3).arg(rgba[1], 5, 'f', 3).arg(rgba[2], 5, 'f', 3).arg(rgba[3], 5, 'f', 3)); + } + break; + case ColorDataType::UNSIGNED_BYTE_RGBA: + { + CaretAssertVectorIndex(m_unsignedByteRGBA, i*4 + 3); + const uint8_t* rgba = &m_unsignedByteRGBA[i * 4]; + s.append(AString(" RGBAb: %1, %2, %3, %4").arg(rgba[0], 3).arg(rgba[1], 3).arg(rgba[2], 3).arg(rgba[3], 3)); + } + } + + switch (m_textureDataType) { + case TextureDataType::NONE: + break; + case TextureDataType::FLOAT_STR: + { + CaretAssertVectorIndex(m_floatTextureSTR, i*3 + 2); + const float* str = &m_floatTextureSTR[i * 3]; + s.append(AString(" T:%1, %2, %3").arg(str[0], 7, 'f', 5).arg(str[1], 7, 'f', 5).arg(str[2], 7, 'f', 5)); + } + break; + } + s.append("\n"); + } + } + + return s; +} + +/** + * Add a vertex and its normal vector + * + * @param xyz + * Coordinate of vertex. + * @param normalVector + * Normal vector for the vertex. + * @param rgbaFloat + * Float RGBA color components. + * @param rgbaByte + * Byte RGBA color components. + */ +void +GraphicsPrimitive::addVertexProtected(const float xyz[3], + const float normalVector[3], + const float rgbaFloat[4], + const uint8_t rgbaByte[4], + const float textureSTR[3]) +{ + m_xyz.insert(m_xyz.end(), + xyz, xyz + 3); + + switch (m_normalVectorDataType) { + case NormalVectorDataType::FLOAT_XYZ: + CaretAssert(normalVector); + m_floatNormalVectorXYZ.insert(m_floatNormalVectorXYZ.end(), + normalVector, + normalVector + 3); + break; + case NormalVectorDataType::NONE: + break; + } + + switch (m_colorDataType) { + case ColorDataType::NONE: + break; + case ColorDataType::FLOAT_RGBA: + CaretAssert(rgbaFloat); + m_floatRGBA.insert(m_floatRGBA.end(), + rgbaFloat, + rgbaFloat + 4); + break; + case ColorDataType::UNSIGNED_BYTE_RGBA: + CaretAssert(rgbaByte); + m_unsignedByteRGBA.insert(m_unsignedByteRGBA.end(), + rgbaByte, + rgbaByte + 4); + break; + } + + switch (m_textureDataType) { + case TextureDataType::NONE: + break; + case TextureDataType::FLOAT_STR: + CaretAssert(textureSTR); + m_floatTextureSTR.insert(m_floatTextureSTR.end(), + textureSTR, + textureSTR + 3); + break; + } + + m_boundingBoxValid = false; + + /* + * The triangle strip primitve vertces are filled after two + * vertices are added after requesting a restart + */ + if (m_triangleStripPrimitiveRestartIndex == getNumberOfVertices()) { + m_triangleStripPrimitiveRestartIndex = -1; + fillTriangleStripPrimitiveRestartVertices(); + } +} + +/** + * Replace the existing XYZ coordinates with the given + * XYZ coordinates. The existing and new coordinates + * MUST BE the same size. + * + * @param xyz + * The new XYZ coordinates. + */ +void +GraphicsPrimitive::replaceFloatXYZ(const std::vector& xyz) +{ + if (xyz.size() == m_xyz.size()) { + m_xyz = xyz; + + if (m_graphicsEngineDataForOpenGL != NULL) { + m_graphicsEngineDataForOpenGL->invalidateCoordinates(); + } + } + else { + const AString msg("Replacement XYZ must be same size as existing xyz"); + CaretLogWarning(msg); + CaretAssertMessage(0, msg); + } + + m_boundingBoxValid = false; +} + +/** + * Replace the XYZ coordinate at the given index + * + * @param vertexIndex + * Index of the vertex + * @param xyz + * The new XYZ coordinate + */ +void +GraphicsPrimitive::replaceVertexFloatXYZ(const int32_t vertexIndex, + const float xyz[3]) +{ + const int32_t offset = vertexIndex * 3; + CaretAssertVectorIndex(m_xyz, offset + 2); + m_xyz[offset] = xyz[0]; + m_xyz[offset + 1] = xyz[1]; + m_xyz[offset + 2] = xyz[2]; + + if (m_graphicsEngineDataForOpenGL != NULL) { + m_graphicsEngineDataForOpenGL->invalidateCoordinates(); + } +} + +/** + * Get the float RGBA coloring for a vertex. + * + * @param vertexIndex + * Index of the vertex. + * @param rgbaOut + * Output with RGBA. + */ +void +GraphicsPrimitive::getVertexFloatRGBA(const int32_t vertexIndex, + float rgbaOut[4]) const +{ + const int32_t i4 = vertexIndex * 4; + switch (m_colorDataType) { + case ColorDataType::NONE: + CaretAssert(0); + break; + case ColorDataType::FLOAT_RGBA: + CaretAssertVectorIndex(m_floatRGBA, i4 + 3); + rgbaOut[0] = m_floatRGBA[i4]; + rgbaOut[1] = m_floatRGBA[i4 + 1]; + rgbaOut[2] = m_floatRGBA[i4 + 2]; + rgbaOut[3] = m_floatRGBA[i4 + 3]; + break; + case ColorDataType::UNSIGNED_BYTE_RGBA: + CaretAssertMessage(0, "Getting float RGBA from primitive but coloring type is Byte"); + CaretLogWarning("Getting float RGBA from primitive but coloring type is Byte"); + break; + } +} + +/** + * Replace the float RGBA coloring for a vertex. + * + * @param vertexIndex + * Index of the vertex. + * @param rgba + * New RGBA for vertex. + */ +void +GraphicsPrimitive::replaceVertexFloatRGBA(const int32_t vertexIndex, + const float rgba[4]) +{ + const int32_t i4 = vertexIndex * 4; + switch (m_colorDataType) { + case ColorDataType::NONE: + CaretAssert(0); + break; + case ColorDataType::FLOAT_RGBA: + CaretAssertVectorIndex(m_floatRGBA, i4 + 3); + m_floatRGBA[i4] = rgba[0]; + m_floatRGBA[i4 + 1] = rgba[1]; + m_floatRGBA[i4 + 2] = rgba[2]; + m_floatRGBA[i4 + 3] = rgba[3]; + break; + case ColorDataType::UNSIGNED_BYTE_RGBA: + CaretAssertMessage(0, "Replacing float RGBA in primitive but coloring type is Byte"); + CaretLogWarning("Replacing float RGBA in primitive but coloring type is Byte"); + break; + } + + if (m_graphicsEngineDataForOpenGL != NULL) { + m_graphicsEngineDataForOpenGL->invalidateColors(); + } +} + +/** + * Get the byte RGBA coloring for a vertex. + * + * @param vertexIndex + * Index of the vertex. + * @param rgbaOut + * Output with RGBA. + */ +void +GraphicsPrimitive::getVertexByteRGBA(const int32_t vertexIndex, + uint8_t rgbaOut[4]) const +{ + const int32_t i4 = vertexIndex * 4; + switch (m_colorDataType) { + case ColorDataType::NONE: + CaretAssert(0); + break; + case ColorDataType::FLOAT_RGBA: + CaretAssertMessage(0, "Getting Byte RGBA in primitive but coloring type is Float"); + CaretLogWarning("Getting Byte RGBA in primitive but coloring type is Float"); + break; + case ColorDataType::UNSIGNED_BYTE_RGBA: + CaretAssertVectorIndex(m_unsignedByteRGBA, i4 + 3); + rgbaOut[0] = m_unsignedByteRGBA[i4]; + rgbaOut[1] = m_unsignedByteRGBA[i4 + 1]; + rgbaOut[2] = m_unsignedByteRGBA[i4 + 2]; + rgbaOut[3] = m_unsignedByteRGBA[i4 + 3]; + break; + } +} + +/** + * Replace the float RGBA coloring for a vertex. + * + * @param vertexIndex + * Index of the vertex. + * @param rgba + * New RGBA for vertex. + */ +void +GraphicsPrimitive::replaceVertexByteRGBA(const int32_t vertexIndex, + const uint8_t rgba[4]) +{ + const int32_t i4 = vertexIndex * 4; + switch (m_colorDataType) { + case ColorDataType::NONE: + CaretAssert(0); + break; + case ColorDataType::FLOAT_RGBA: + CaretAssertMessage(0, "Replacing Byte RGBA in primitive but coloring type is Float"); + CaretLogWarning("Replacing Byte RGBA in primitive but coloring type is Float"); + break; + case ColorDataType::UNSIGNED_BYTE_RGBA: + CaretAssertVectorIndex(m_unsignedByteRGBA, i4 + 3); + m_unsignedByteRGBA[i4] = rgba[0]; + m_unsignedByteRGBA[i4 + 1] = rgba[1]; + m_unsignedByteRGBA[i4 + 2] = rgba[2]; + m_unsignedByteRGBA[i4 + 3] = rgba[3]; + break; + } + + if (m_graphicsEngineDataForOpenGL != NULL) { + m_graphicsEngineDataForOpenGL->invalidateColors(); + } +} + +/** + * Replace all vertex RGBA with the given solid color RGBA. + * + * @param rgba + * RGBA values for all vertices + */ +void +GraphicsPrimitive::replaceAllVertexSolidByteRGBA(const uint8_t rgba[4]) +{ + switch (m_colorDataType) { + case ColorDataType::NONE: + CaretAssert(0); + break; + case ColorDataType::FLOAT_RGBA: + CaretAssertMessage(0, "Replacing Byte RGBA in primitive but coloring type is Float"); + CaretLogWarning("Replacing Byte RGBA in primitive but coloring type is Float"); + break; + case ColorDataType::UNSIGNED_BYTE_RGBA: + { + const int32_t numRGBA = static_cast(m_unsignedByteRGBA.size() / 4); + for (int32_t i = 0; i < numRGBA; i++) { + const int32_t i4 = i * 4; + CaretAssertVectorIndex(m_unsignedByteRGBA, i4 + 3); + m_unsignedByteRGBA[i4] = rgba[0]; + m_unsignedByteRGBA[i4 + 1] = rgba[1]; + m_unsignedByteRGBA[i4 + 2] = rgba[2]; + m_unsignedByteRGBA[i4 + 3] = rgba[3]; + } + } + } + + if (m_graphicsEngineDataForOpenGL != NULL) { + m_graphicsEngineDataForOpenGL->invalidateColors(); + } +} + +/** + * Replace all vertex RGBA with the given solid color RGBA. + * + * @param rgba + * RGBA values for all vertices + */ +void +GraphicsPrimitive::replaceAllVertexSolidFloatRGBA(const float rgba[4]) +{ + switch (m_colorDataType) { + case ColorDataType::NONE: + CaretAssert(0); + break; + case ColorDataType::FLOAT_RGBA: + { + const int32_t numRGBA = static_cast(m_floatRGBA.size() / 4); + for (int32_t i = 0; i < numRGBA; i++) { + const int32_t i4 = i * 4; + CaretAssertVectorIndex(m_floatRGBA, i4 + 3); + m_floatRGBA[i4] = rgba[0]; + m_floatRGBA[i4 + 1] = rgba[1]; + m_floatRGBA[i4 + 2] = rgba[2]; + m_floatRGBA[i4 + 3] = rgba[3]; + } + } + break; + case ColorDataType::UNSIGNED_BYTE_RGBA: + CaretAssertMessage(0, "Replacing Byte RGBA in primitive but coloring type is Float"); + CaretLogWarning("Replacing Byte RGBA in primitive but coloring type is Float"); + break; + } + + if (m_graphicsEngineDataForOpenGL != NULL) { + m_graphicsEngineDataForOpenGL->invalidateColors(); + } +} + + + + +/** + * Get a bounds box for the vertex coordinates. + * + * @param boundingBoxOut + * Output bounding box. + * @return + * True if bounding box is valid, else false. + */ +bool +GraphicsPrimitive::getVertexBounds(BoundingBox& boundingBoxOut) const +{ + if ( ! m_boundingBoxValid) { + if (m_boundingBox == NULL) { + m_boundingBox.reset(new BoundingBox()); + } + m_boundingBox->resetForUpdate(); + + const int32_t numberOfVertices = static_cast(m_xyz.size() / 3); + for (int32_t i = 0; i < numberOfVertices; i++) { + const int32_t i3 = i * 3; + CaretAssertVectorIndex(m_xyz, i3 + 2); + m_boundingBox->update(&m_xyz[i3]); + } + + m_boundingBoxValid = true; + } + + boundingBoxOut = *m_boundingBox; + return m_boundingBoxValid; +} + +/** + * There may be instances where a primitive should stop and restart at + * non-consecutive vertices (such as a gap in connected line segment). + * Typically, this requires creating separate primitives around + * the gap. + * + * When this method is called, it indicates that any previously + * added points will not connect to any points after this method + * is called. + * + * This functionality is similar to that of glPrimitiveRestartIndex() + * that is not available in versions of OpenGL prior to 3.1. + * + * At this time, this functionality if available only for + * line strip and line loop. + */ +void +GraphicsPrimitive::addPrimitiveRestart() +{ + bool polygonalLineFlag = false; + bool triangleStripFlag = false; + + switch (m_primitiveType) { + case PrimitiveType::OPENGL_LINE_LOOP: + break; + case PrimitiveType::OPENGL_LINE_STRIP: + break; + case PrimitiveType::OPENGL_LINES: + break; + case PrimitiveType::OPENGL_POINTS: + break; + case PrimitiveType::OPENGL_TRIANGLE_FAN: + break; + case PrimitiveType::OPENGL_TRIANGLE_STRIP: + triangleStripFlag = true; + break; + case PrimitiveType::OPENGL_TRIANGLES: + break; + case PrimitiveType::POLYGONAL_LINE_LOOP_BEVEL_JOIN: + case PrimitiveType::POLYGONAL_LINE_LOOP_MITER_JOIN: + polygonalLineFlag = true; + break; + case PrimitiveType::POLYGONAL_LINE_STRIP_BEVEL_JOIN: + case PrimitiveType::POLYGONAL_LINE_STRIP_MITER_JOIN: + polygonalLineFlag = true; + break; + case PrimitiveType::POLYGONAL_LINES: + break; + case PrimitiveType::SPHERES: + break; + } + + if (polygonalLineFlag) { + /* + * A set is used because a vertex could be added more than one time + */ + m_polygonalLinePrimitiveRestartIndices.insert(getNumberOfVertices()); + } + else if (triangleStripFlag) { + const int32_t numVertices = getNumberOfVertices(); + if (numVertices > 3) { + /* + * Add 18 dummy vertices that will be used for the + * 6 degenerate triangles at a later time + */ + float float4[4] = { 0.0f, 0.0f, 0.0f, 0.0f }; + uint8_t byte4[4] = { 0, 0, 0, 0 }; + for (int32_t i = 0; i < 18; i++) { + addVertexProtected(float4, float4, float4, byte4, float4); + } + + /* + * When the number of vertices becomes the index below, + * the degenerate triangles will be added. + */ + m_triangleStripPrimitiveRestartIndex = getNumberOfVertices() + 2; + } + else { + CaretLogSevere("Must have at least three vertices before requesting a triangle strip primitive restart."); + } + } + else { + CaretLogSevere("Requested primitive restart on not yet supported primitive type: " + + getPrimitiveTypeAsText()); + } +} + +/** + * Copy a vertex to another vertex. + * + * @param copyFromIndex + * Index of the 'copied' vertex. + * @param copyToIndex + */ +void +GraphicsPrimitive::copyVertex(const int32_t copyFromIndex, + const int32_t copyToIndex) +{ + const int32_t from3 = copyFromIndex * 3; + const int32_t to3 = copyToIndex * 3; + + for (int32_t i = 0; i < 3; i++) { + CaretAssertVectorIndex(m_xyz, from3 + i); + CaretAssertVectorIndex(m_xyz, to3 + i); + m_xyz[to3 + i] = m_xyz[from3 + i]; + + switch (m_normalVectorDataType) { + case NormalVectorDataType::FLOAT_XYZ: + CaretAssertVectorIndex(m_floatNormalVectorXYZ, from3 + i); + CaretAssertVectorIndex(m_floatNormalVectorXYZ, to3 + i); + m_floatNormalVectorXYZ[to3 + i] = m_floatNormalVectorXYZ[from3 + i]; + break; + case NormalVectorDataType::NONE: + break; + } + + switch (m_textureDataType) { + case TextureDataType::NONE: + break; + case TextureDataType::FLOAT_STR: + CaretAssertVectorIndex(m_floatTextureSTR, from3 + i); + CaretAssertVectorIndex(m_floatTextureSTR, to3 + i); + m_floatTextureSTR[to3 + i] = m_floatTextureSTR[from3 + i]; + break; + } + } + + const int32_t from4 = copyFromIndex * 4; + const int32_t to4 = copyToIndex * 4; + + for (int32_t i = 0; i < 4; i++) { + switch (m_colorDataType) { + case ColorDataType::NONE: + break; + case ColorDataType::FLOAT_RGBA: + CaretAssertVectorIndex(m_floatRGBA, from4 + i); + CaretAssertVectorIndex(m_floatRGBA, to4 + i); + m_floatRGBA[to4 + i] = m_floatRGBA[from4 + i]; + break; + case ColorDataType::UNSIGNED_BYTE_RGBA: + CaretAssertVectorIndex(m_unsignedByteRGBA, from4 + i); + CaretAssertVectorIndex(m_unsignedByteRGBA, to4 + i); + m_unsignedByteRGBA[to4 + i] = m_unsignedByteRGBA[from4 + i]; + break; + } + } + + m_boundingBoxValid = false; +} + +/** + * Fill the degenerate vertices for the triangle strip. + * Since OpenGL's primitive restart (glPrimitiveRestartIndices()) + * function is not available prior to OpenGL 3.1, add + * degenerate triangles as explained in the following links. + * + * @param newStripVertexOneXYZ + * First vertex in new strip that will be added after calling this method. + * @param newStripVertexTwoXYZ + * Second vertex in new strip that will be added after calling this method. + * + * @see http://www.gamedev.net/page/resources/_/technical/graphics-programming-and-theory/concatenating-triangle-strips-r1871 + * @see http://en.wikipedia.org/wiki/Triangle_strip + */ +void +GraphicsPrimitive::fillTriangleStripPrimitiveRestartVertices() +{ + const int32_t numVertices = getNumberOfVertices(); + + /* + * Vertices "F" and "F" were added just before the call to addPrimitiveRestart() + * Vertices "G" and "H" were added after the call to addPrimitiveRestart() and + * this method was called immediately after "H" was added + * addPrimitiveRestart() added 18 vertices: + * DEF + EFF + FFG + FGG + GGH + GHG + GGH + GHI + * + * From: https://www.gamedev.net/articles/programming/graphics/concatenating-triangle-strips-r1871 + */ + + const int32_t vertexH = numVertices - 1; + const int32_t vertexG = numVertices - 2; + const int32_t vertexF = numVertices - 21; + const int32_t vertexE = numVertices - 22; + int32_t copyToIndex = vertexF + 1; + + copyVertex(vertexE, copyToIndex++); + copyVertex(vertexF, copyToIndex++); + copyVertex(vertexF, copyToIndex++); + + copyVertex(vertexF, copyToIndex++); + copyVertex(vertexF, copyToIndex++); + copyVertex(vertexG, copyToIndex++); + + copyVertex(vertexF, copyToIndex++); + copyVertex(vertexG, copyToIndex++); + copyVertex(vertexG, copyToIndex++); + + copyVertex(vertexG, copyToIndex++); + copyVertex(vertexG, copyToIndex++); + copyVertex(vertexH, copyToIndex++); + + copyVertex(vertexG, copyToIndex++); + copyVertex(vertexH, copyToIndex++); + copyVertex(vertexG, copyToIndex++); + + copyVertex(vertexG, copyToIndex++); + copyVertex(vertexG, copyToIndex++); + copyVertex(vertexH, copyToIndex++); + + CaretAssert(copyToIndex == vertexG); +} + +/** + * Get the point diameter. + * + * @param sizeTypeOut + * Type of sizing. + * @param pointDiameterOut + * Diameter of point. + */ +void +GraphicsPrimitive::getPointDiameter(PointSizeType& sizeTypeOut, + float& pointDiameterOut) const +{ + sizeTypeOut = m_pointSizeType; + pointDiameterOut = m_pointDiameterValue; +} + +/** + * Set the point diameter. + * + * @param sizeType + * Type of sizing. + * @param pointDiameter + * Diameter of point. + */ +void +GraphicsPrimitive::setPointDiameter(const PointSizeType sizeType, + const float pointDiameter) const +{ + m_pointSizeType = sizeType; + m_pointDiameterValue = pointDiameter; +} + +/** + * Get the line width. + * + * @param lineWidthTypeOut + * Type of width. + * @param lineWidthOut + * Width of line. + */ +void +GraphicsPrimitive::getLineWidth(LineWidthType& widthTypeOut, + float& lineWidthOut) const +{ + widthTypeOut = m_lineWidthType; + lineWidthOut = m_lineWidthValue; +} + +/** + * Set the line width. + * + * @param lineWidthType + * Type of width. + * @param lineWidth + * Width of line. + */ +void +GraphicsPrimitive::setLineWidth(const LineWidthType lineWidthType, + const float lineWidth) const +{ + m_lineWidthType = lineWidthType; + m_lineWidthValue = lineWidth; +} + +/** + * Get the sphere diameter. + * + * @param sizeTypeOut + * Type of sizing. + * @param pointDiameterOut + * Diameter of sphere. + */ +void +GraphicsPrimitive::getSphereDiameter(SphereSizeType& sizeTypeOut, + float& sphereDiameterOut) const +{ + sizeTypeOut = m_sphereSizeType; + sphereDiameterOut = m_sphereDiameterValue; +} + +/** + * Set the sphere diameter. + * + * @param sizeType + * Type of sizing. + * @param pointDiameter + * Diameter of sphere. + */ +void +GraphicsPrimitive::setSphereDiameter(const SphereSizeType sizeType, + const float sphereDiameter) const +{ + m_sphereSizeType = sizeType; + m_sphereDiameterValue = sphereDiameter; +} + +/** + * Set the image for the texture. + * + * @param imageBytesRGBA + * Bytes containing the image data. 4 bytes per pixel. + * @param imageWidth + * Width of the actual image. + * @param imageHeight + * Height of the image. + */ +void +GraphicsPrimitive::setTextureImage(const uint8_t* imageBytesRGBA, + const int32_t imageWidth, + const int32_t imageHeight) +{ + m_textureImageBytesRGBA.clear(); + m_textureImageWidth = -1; + m_textureImageHeight = -1; + + const int32_t numBytes = imageWidth * imageHeight * 4; + if (numBytes > 0) { + m_textureImageBytesRGBA.reserve(numBytes); + m_textureImageWidth = imageWidth; + m_textureImageHeight = imageHeight; + m_textureImageBytesRGBA.insert(m_textureImageBytesRGBA.end(), + imageBytesRGBA, imageBytesRGBA + numBytes); + } +} + +/** + * Get the OpenGL graphics engine data in this instance. + * + * @return + * OpenGL graphics engine data or NULL if not found. + */ +GraphicsEngineDataOpenGL* +GraphicsPrimitive::getGraphicsEngineDataForOpenGL() +{ + return m_graphicsEngineDataForOpenGL.get(); +} + +/** + * Set the OpenGL graphics engine data in this instance. + * + * @param graphicsEngineForOpenGL + * OpenGLgraphics engine for which graphics engine data is desired. This value may + * NULL to remove graphics engine data for the given graphics engine. + * This instance will take ownership of this data and handle deletion of it. + */ +void +GraphicsPrimitive::setGraphicsEngineDataForOpenGL(GraphicsEngineDataOpenGL* graphicsEngineDataForOpenGL) +{ + m_graphicsEngineDataForOpenGL.reset(graphicsEngineDataForOpenGL); +} + +/** + * Receive an event. + * + * @param event + * An event for which this instance is listening. + */ +void +GraphicsPrimitive::receiveEvent(Event* /*event*/) +{ +// if (event->getEventType() == EventTypeEnum::) { +// eventName = dynamic_cast(event); +// CaretAssert(eventName); +// +// event->setEventProcessed(); +// } +} + +/** + * @return A new primitive for XYZ with solid color float RGBA. Caller is responsible + * for deleting the returned pointer. + * + * @param primitiveType + * Type of primitive drawn (triangles, lines, etc.) + */ +GraphicsPrimitiveV3f* +GraphicsPrimitive::newPrimitiveV3f(const GraphicsPrimitive::PrimitiveType primitiveType, + const float floatRGBA[4]) +{ + GraphicsPrimitiveV3f* primitive = new GraphicsPrimitiveV3f(primitiveType, + floatRGBA); + return primitive; +} + +/** + * @return A new primitive for XYZ with solid color float RGBA. Caller is responsible + * for deleting the returned pointer. + * + * @param primitiveType + * Type of primitive drawn (triangles, lines, etc.) + */ +GraphicsPrimitiveV3fN3f* +GraphicsPrimitive::newPrimitiveV3fN3f(const GraphicsPrimitive::PrimitiveType primitiveType, + const uint8_t unsignedByteRGBA[4]) +{ + GraphicsPrimitiveV3fN3f* primitive = new GraphicsPrimitiveV3fN3f(primitiveType, + unsignedByteRGBA); + return primitive; +} + +/** + * @return A new primitive for XYZ with solid color unsigned byte RGBA. Caller is responsible + * for deleting the returned pointer. + * + * @param primitiveType + * Type of primitive drawn (triangles, lines, etc.) + */ +GraphicsPrimitiveV3f* +GraphicsPrimitive::newPrimitiveV3f(const GraphicsPrimitive::PrimitiveType primitiveType, + const uint8_t unsignedByteRGBA[4]) +{ + GraphicsPrimitiveV3f* primitive = new GraphicsPrimitiveV3f(primitiveType, + unsignedByteRGBA); + return primitive; +} + +/** + * @return A new primitive for XYZ with float RGBA. Caller is responsible + * for deleting the returned pointer. + * + * @param primitiveType + * Type of primitive drawn (triangles, lines, etc.) + */ +GraphicsPrimitiveV3fC4f* +GraphicsPrimitive::newPrimitiveV3fC4f(const GraphicsPrimitive::PrimitiveType primitiveType) +{ + GraphicsPrimitiveV3fC4f* primitive = new GraphicsPrimitiveV3fC4f(primitiveType); + return primitive; +} + +/** + * @return A new primitive for XYZ with unsigned byte RGBA. Caller is responsible + * for deleting the returned pointer. + * + * @param primitiveType + * Type of primitive drawn (triangles, lines, etc.) + */ +GraphicsPrimitiveV3fC4ub* +GraphicsPrimitive::newPrimitiveV3fC4ub(const GraphicsPrimitive::PrimitiveType primitiveType) +{ + GraphicsPrimitiveV3fC4ub* primitive = new GraphicsPrimitiveV3fC4ub(primitiveType); + return primitive; +} + +GraphicsPrimitiveV3fT3f* +GraphicsPrimitive::newPrimitiveV3fT3f(const GraphicsPrimitive::PrimitiveType primitiveType, + const uint8_t* imageBytesRGBA, + const int32_t imageWidth, + const int32_t imageHeight) +{ + GraphicsPrimitiveV3fT3f* primitive = new GraphicsPrimitiveV3fT3f(primitiveType, + imageBytesRGBA, + imageWidth, + imageHeight); + return primitive; +} + diff --git a/src/Graphics/GraphicsPrimitive.h b/src/Graphics/GraphicsPrimitive.h new file mode 100644 index 0000000000000000000000000000000000000000..b8374dcc22e4255ade52274165d4713f9d4fc179 --- /dev/null +++ b/src/Graphics/GraphicsPrimitive.h @@ -0,0 +1,505 @@ +#ifndef __GRAPHICS_PRIMITIVE_H__ +#define __GRAPHICS_PRIMITIVE_H__ + +/*LICENSE_START*/ +/* + * Copyright (C) 2017 Washington University School of Medicine + * + * 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 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ +/*LICENSE_END*/ + + +#include +#include +#include + +#include "CaretObject.h" +#include "EventListenerInterface.h" + + +namespace caret { + + class BoundingBox; + class GraphicsEngineDataOpenGL; + class GraphicsPrimitiveV3f; + class GraphicsPrimitiveV3fC4f; + class GraphicsPrimitiveV3fC4ub; + class GraphicsPrimitiveV3fN3f; + class GraphicsPrimitiveV3fT3f; + + class GraphicsPrimitive : public CaretObject, public EventListenerInterface { + + public: + /** + * Data type of vertices + */ + enum class VertexDataType { + /** Vertices are three float values per vertex containing X, Y, Z */ + FLOAT_XYZ + }; + + /** + * Data type of normal vectors + */ + enum class NormalVectorDataType { + /** No normal vectors */ + NONE, + /** Normal vectors are three float values per vertex contain X, Y, Z ranging 0.0 to 1.0 */ + FLOAT_XYZ + }; + + /** + * Data type of colors + */ + enum class ColorDataType { + /** No color components */ + NONE, + /** Four float values per vertex containing Red, Green, Blue, Alpha ranging 0.0 to 1.0 */ + FLOAT_RGBA, + /** Four unsigned byte values per vertex containing Red, Green, Blue, Alpha ranging 0 to 255 */ + UNSIGNED_BYTE_RGBA + }; + + /** + * Coloring for each vertex thype + */ + enum class VertexColorType { + /** No color components */ + NONE, + /** One RGBA for all vertices */ + SOLID_RGBA, + /** Unique RGBA for each vertex */ + PER_VERTEX_RGBA + }; + + /** + * Data type of texture components + */ + enum class TextureDataType { + /** No texture coordinates */ + NONE, + /** Three float values per vertex contains S, T, and R texture coordinates */ + FLOAT_STR + }; + + /** + * Type of primitives for drawing. There are NO primitives equivalent to + * OpenGL's GL_QUAD_STRIP and GL_POLYGON. The reason is that these + * primitive types are removed in 3.x (probably 3.2). + * + * Descriptions are copied from the glVertex man page. + */ + enum class PrimitiveType { + /** + * Draws a connected group of line segments from the first vertex to the last. + * Vertices n and n+1 define line n. n-1 lines are drawn. + * Note: OpenGL supports a limited range of line widths, usually [0.5, 10] pixels + */ + OPENGL_LINE_LOOP, + /** + * Treats each pair of vertices as an independent line segment. + * Vertices 2n-1 and 2n define line n. n/2 lines are drawn. + * Note: OpenGL supports a limited range of line widths, usually [0.5, 10] pixels + */ + OPENGL_LINE_STRIP, + /** + * Draws a connected group of line segments from the first vertex to the last, then back + * to the first. Vertices n and n+1 define line n. The last line, however, is defined by + * vertices n and 1. n lines are drawn. + * Note: OpenGL supports a limited range of line widths, usually [0.5, 10] pixels + */ + OPENGL_LINES, + /** + * Treats each vertex as a single point. Vertex n defines point n. n points are drawn. + */ + OPENGL_POINTS, + /** + * Draws a connected group of triangles. One triangle is defined for each vertex + * presented after the first two vertices. Vertices 1, n+1, and n+2 define triangle n. n-2 + * triangles are drawn. + */ + OPENGL_TRIANGLE_FAN, + /** + * Draws a connected group of triangles. One triangle is defined for each vertex + * presented after the first two vertices. For odd n, vertices n, n+1, and n+2 define + * triangle n. For even n, vertices n+1, n, and n+2 define triangle n. n-2 triangles are + * drawn. + */ + OPENGL_TRIANGLE_STRIP, + /** + * Treats each triplet of vertices as an independent triangle. Vertices 3n-2, 3n-1, and + * 3n define triangle n. n/3 triangles are drawn. + */ + OPENGL_TRIANGLES, + /** + * Like OPENGL_LINE_LOOP but there is no limit on line width as it draws the lines using polygons + * and polygons that form the line use a BEVEL join at vertices + */ + POLYGONAL_LINE_LOOP_BEVEL_JOIN, + /** + * Like OPENGL_LINE_LOOP but there is no limit on line width as it draws the lines using polygons + * and polygons that form the line use a MITER join at vertices + */ + POLYGONAL_LINE_LOOP_MITER_JOIN, + /** + * Like OPENGL_LINE_STRIP but there is no limit on line width as it draws the lines using polygons + * and polygons that form the line use a BEVEL join at vertices + */ + POLYGONAL_LINE_STRIP_BEVEL_JOIN, + /** + * Like OPENGL_LINE_STRIP but there is no limit on line width as it draws the lines using polygons + * and polygons that form the line use a MITER join at vertices + */ + POLYGONAL_LINE_STRIP_MITER_JOIN, + /** + * Like OPENGL_LINES but there is no limit on line width as it draws the lines using polygons. + */ + POLYGONAL_LINES, + /* + * Draws sphere at each vertex + */ + SPHERES + }; + + /** + * Type for point size + */ + enum class PointSizeType { + /** + * Millimeters (all models surface, volume, etc. are drawn + * in millimeters). + */ + MILLIMETERS, + /** + * Size of point or width of line is a percentage of viewport height. + * Ranges [0.0, 100.0] + */ + PERCENTAGE_VIEWPORT_HEIGHT, + /** + * Size of point or width of line is in pixels + */ + PIXELS + }; + + /** + * Type for line width + */ + enum class LineWidthType { + /** + * Size of point or width of line is a percentage of viewport height. + * Ranges [0.0, 100.0] + */ + PERCENTAGE_VIEWPORT_HEIGHT, + /** + * Size of point or width of line is in pixels + */ + PIXELS + }; + + /** + * Type for sphere size + */ + enum class SphereSizeType { + /** + * Millimeters (all models surface, volume, etc. are drawn + * in millimeters). + */ + MILLIMETERS + }; + + /** + * Hint to graphics engine on how the primitive is used. + * The hint must be set prior to loading of buffers. + */ + enum class UsageType { + /** + * Buffer is modified once and used a few times + * Like OpenGL STREAM_DRAW + */ + MODIFIED_ONCE_DRAWN_FEW_TIMES, + /** + * Buffer is modified once and used many times + * Like OpenGL STATIC_DRAW + */ + MODIFIED_ONCE_DRAWN_MANY_TIMES, + /** + * Buffer is modified many times and used many times + * Like OpenGL DYNAMIC_DRAW + */ + MODIFIED_MANY_DRAWN_MANY_TIMES + }; + + protected: + GraphicsPrimitive(const VertexDataType vertexDataType, + const NormalVectorDataType normalVectorDataType, + const ColorDataType colorDataType, + const VertexColorType vertexColorType, + const TextureDataType textureDataType, + const PrimitiveType primitiveType); + + GraphicsPrimitive(const GraphicsPrimitive& obj); + + private: + GraphicsPrimitive& operator=(const GraphicsPrimitive& obj); + + public: + static GraphicsPrimitiveV3f* newPrimitiveV3f(const GraphicsPrimitive::PrimitiveType primitiveType, + const float floatRGBA[4]); + + static GraphicsPrimitiveV3f* newPrimitiveV3f(const GraphicsPrimitive::PrimitiveType primitiveType, + const uint8_t unsignedByteRGBA[4]); + + static GraphicsPrimitiveV3fN3f* newPrimitiveV3fN3f(const GraphicsPrimitive::PrimitiveType primitiveType, + const uint8_t unsignedByteRGBA[4]); + + static GraphicsPrimitiveV3fC4f* newPrimitiveV3fC4f(const GraphicsPrimitive::PrimitiveType primitiveType); + + static GraphicsPrimitiveV3fC4ub* newPrimitiveV3fC4ub(const GraphicsPrimitive::PrimitiveType primitiveType); + + static GraphicsPrimitiveV3fT3f* newPrimitiveV3fT3f(const GraphicsPrimitive::PrimitiveType primitiveType, + const uint8_t* imageBytesRGBA, + const int32_t imageWidth, + const int32_t imageHeight); + + virtual ~GraphicsPrimitive(); + + void reserveForNumberOfVertices(const int32_t numberOfVertices); + + UsageType getUsageTypeCoordinates() const; + + UsageType getUsageTypeNormals() const; + + UsageType getUsageTypeColors() const; + + UsageType getUsageTypeTextureCoordinates() const; + + void setUsageTypeAll(const UsageType usage); + + void setUsageTypeCoordinates(const UsageType usage); + + void setUsageTypeNormals(const UsageType usage); + + void setUsageTypeColors(const UsageType usage); + + void setUsageTypeTextureCoordinates(const UsageType usage); + + virtual void receiveEvent(Event* event); + + bool isValid() const; + + /** + * @return Data type of the vertices. + */ + inline VertexDataType getVertexDataType() const { return m_vertexDataType; } + + /** + * @return Data type of the normal vectors. + */ + inline NormalVectorDataType getNormalVectorDataType () const { return m_normalVectorDataType; } + + /** + * @return Data type of the colors. + */ + inline ColorDataType getColorDataType() const { return m_colorDataType; } + + /** + * @return Type of vertex colors + */ + inline VertexColorType getVertexColorType() const { return m_vertexColorType; } + + /** + * @return Type of primitive. + */ + inline PrimitiveType getPrimitiveType() const { return m_primitiveType; } + + /** + * @return Data type of texture. + */ + inline TextureDataType getTextureDataType() const { return m_textureDataType; } + + /** + * @return The float coordinates. + */ + const std::vector& getFloatXYZ() const { return m_xyz; } + + void replaceFloatXYZ(const std::vector& xyz); + + /** + * @return The number of vertices + */ + inline int32_t getNumberOfVertices() const { return (m_xyz.size() / 3); } + + void replaceVertexFloatXYZ(const int32_t vertexIndex, + const float xyz[3]); + + void getVertexFloatRGBA(const int32_t vertexIndex, + float rgbaOut[4]) const; + + void replaceVertexFloatRGBA(const int32_t vertexIndex, + const float rgba[4]); + + void getVertexByteRGBA(const int32_t vertexIndex, + uint8_t rgbaOut[4]) const; + + void replaceVertexByteRGBA(const int32_t vertexIndex, + const uint8_t rgba[4]); + + void replaceAllVertexSolidByteRGBA(const uint8_t rgba[4]); + + void replaceAllVertexSolidFloatRGBA(const float rgba[4]); + + bool getVertexBounds(BoundingBox& boundingBoxOut) const; + + void addPrimitiveRestart(); + + void getPointDiameter(PointSizeType& sizeTypeOut, + float& pointDiameterOut) const; + + void setPointDiameter(const PointSizeType sizeType, + const float pointDiameter) const; + + void getLineWidth(LineWidthType& widthTypeOut, + float& lineWidthOut) const; + + void setLineWidth(const LineWidthType widthType, + const float lineWidth) const; + + void getSphereDiameter(SphereSizeType& sizeTypeOut, + float& sphereDiameterOut) const; + + void setSphereDiameter(const SphereSizeType sizeType, + const float sphereDiameter) const; + + GraphicsEngineDataOpenGL* getGraphicsEngineDataForOpenGL(); + + void setGraphicsEngineDataForOpenGL(GraphicsEngineDataOpenGL* graphicsEngineDataForOpenGL); + + virtual AString toString() const override; + + virtual void print() const; + + /** + * Clone this primitive. + */ + virtual GraphicsPrimitive* clone() const = 0; + + protected: + AString toStringPrivate(const bool includeAllDataFlag) const; + + void setTextureImage(const uint8_t* imageBytesRGBA, + const int32_t imageWidth, + const int32_t imageHeight); + + void addVertexProtected(const float xyz[3], + const float normalVector[3], + const float rgbaFloat[4], + const uint8_t rgbaByte[4], + const float textureSTR[3]); + + AString getPrimitiveTypeAsText() const; + + AString getLineWidthTypeAsText(const LineWidthType lineWidthType) const; + + AString getPointSizeTypeAsText(const PointSizeType sizeType) const; + + AString getSphereSizeTypeAsText(const SphereSizeType sizeType) const; + + AString getVertexColorTypeAsText(const VertexColorType vertexColorType) const; + + const VertexDataType m_vertexDataType; + + const NormalVectorDataType m_normalVectorDataType; + + const ColorDataType m_colorDataType; + + const VertexColorType m_vertexColorType; + + const TextureDataType m_textureDataType; + + const PrimitiveType m_primitiveType; + + mutable bool m_boundingBoxValid = false; + + UsageType m_usageTypeCoordinates = UsageType::MODIFIED_ONCE_DRAWN_FEW_TIMES; + + UsageType m_usageTypeNormals = UsageType::MODIFIED_ONCE_DRAWN_FEW_TIMES; + + UsageType m_usageTypeColors = UsageType::MODIFIED_ONCE_DRAWN_FEW_TIMES; + + UsageType m_usageTypeTextureCoordinates = UsageType::MODIFIED_ONCE_DRAWN_FEW_TIMES; + + std::unique_ptr m_graphicsEngineDataForOpenGL; + + int32_t m_textureImageWidth = -1; + + int32_t m_textureImageHeight = -1; + + mutable PointSizeType m_pointSizeType = PointSizeType::PIXELS; + + mutable float m_pointDiameterValue = 1.0f; + + mutable LineWidthType m_lineWidthType = LineWidthType::PIXELS; + + mutable float m_lineWidthValue = 1.0f; + + mutable SphereSizeType m_sphereSizeType = SphereSizeType::MILLIMETERS; + + mutable float m_sphereDiameterValue = 1.0f; + + mutable std::unique_ptr m_boundingBox; + + std::set m_polygonalLinePrimitiveRestartIndices; + + int32_t m_triangleStripPrimitiveRestartIndex = -1; + + private: + + void copyHelperGraphicsPrimitive(const GraphicsPrimitive& obj); + + void copyVertex(const int32_t copyFromIndex, + const int32_t copyToIndex); + + void fillTriangleStripPrimitiveRestartVertices(); + + std::vector m_xyz; + + std::vector m_floatNormalVectorXYZ; + + std::vector m_floatRGBA; + + std::vector m_unsignedByteRGBA; + + std::vector m_floatTextureSTR; + + std::vector m_textureImageBytesRGBA; + friend class GraphicsEngineDataOpenGL; + friend class GraphicsOpenGLPolylineTriangles; + friend class GraphicsPrimitiveSelectionHelper; + + std::vector m_dummyFloatRGBAVector; + + std::vector m_dummyUnsignedByteRGBAVector; + + // ADD_NEW_MEMBERS_HERE + + }; + +#ifdef __GRAPHICS_PRIMITIVE_DECLARE__ + // +#endif // __GRAPHICS_PRIMITIVE_DECLARE__ + +} // namespace +#endif //__GRAPHICS_PRIMITIVE_H__ diff --git a/src/Graphics/GraphicsPrimitiveSelectionHelper.cxx b/src/Graphics/GraphicsPrimitiveSelectionHelper.cxx new file mode 100644 index 0000000000000000000000000000000000000000..e1f2a2d06b86d31f74ec7a8e568c2de2c5cda3c8 --- /dev/null +++ b/src/Graphics/GraphicsPrimitiveSelectionHelper.cxx @@ -0,0 +1,262 @@ + +/*LICENSE_START*/ +/* + * Copyright (C) 2017 Washington University School of Medicine + * + * 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 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ +/*LICENSE_END*/ + +#define __GRAPHICS_PRIMITIVE_SELECTION_HELPER_DECLARE__ +#include "GraphicsPrimitiveSelectionHelper.h" +#undef __GRAPHICS_PRIMITIVE_SELECTION_HELPER_DECLARE__ + +#include "CaretAssert.h" +#include "CaretLogger.h" +#include "GraphicsPrimitive.h" + +using namespace caret; + + + +/** + * \class caret::GraphicsPrimitiveSelectionHelper + * \brief Helps with identification of graphics primitives + * \ingroup Graphics + */ + +/** + * Constructor. + */ +GraphicsPrimitiveSelectionHelper::GraphicsPrimitiveSelectionHelper(GraphicsPrimitive* parentGraphicsPrimitive) +: CaretObject(), +m_parentGraphicsPrimitive(parentGraphicsPrimitive) +{ + CaretAssert(m_parentGraphicsPrimitive); +} + +/** + * Destructor. + */ +GraphicsPrimitiveSelectionHelper::~GraphicsPrimitiveSelectionHelper() +{ + +} + +/** + * Setup selection data just before drawing. + */ +void +GraphicsPrimitiveSelectionHelper::setupSelectionBeforeDrawing() +{ + int32_t numberOfVertices = 0; + + switch (m_parentGraphicsPrimitive->getVertexDataType()) { + case GraphicsPrimitive::VertexDataType::FLOAT_XYZ: + numberOfVertices = m_parentGraphicsPrimitive->getFloatXYZ().size() / 3; + break; + } + + m_numberOfVerticesPerPrimitive = 0; + m_vertexOffsetForPrimitive = 0; + + switch (m_parentGraphicsPrimitive->getPrimitiveType()) { + case GraphicsPrimitive::PrimitiveType::OPENGL_LINE_LOOP: + case GraphicsPrimitive::PrimitiveType::POLYGONAL_LINE_LOOP_BEVEL_JOIN: + case GraphicsPrimitive::PrimitiveType::POLYGONAL_LINE_LOOP_MITER_JOIN: + m_numberOfVerticesPerPrimitive = numberOfVertices; + break; + case GraphicsPrimitive::PrimitiveType::OPENGL_LINE_STRIP: + case GraphicsPrimitive::PrimitiveType::POLYGONAL_LINE_STRIP_BEVEL_JOIN: + case GraphicsPrimitive::PrimitiveType::POLYGONAL_LINE_STRIP_MITER_JOIN: + m_numberOfVerticesPerPrimitive = numberOfVertices; + break; + case GraphicsPrimitive::PrimitiveType::OPENGL_LINES: + case GraphicsPrimitive::PrimitiveType::POLYGONAL_LINES: + m_numberOfVerticesPerPrimitive = 2; + break; + case GraphicsPrimitive::PrimitiveType::OPENGL_POINTS: + m_numberOfVerticesPerPrimitive = 1; + break; + case GraphicsPrimitive::PrimitiveType::OPENGL_TRIANGLE_FAN: + /* + * Assuming vertices start at ZERO + * Color is at vertex (i + 2): + * Vertex[2] is color for first triangle, + * Vertex[3] is color for second triangle... + */ + m_numberOfVerticesPerPrimitive = 1; + m_vertexOffsetForPrimitive = 2; + break; + case GraphicsPrimitive::PrimitiveType::OPENGL_TRIANGLE_STRIP: + /* + * Assuming vertices start at ZERO + * Color is at vertex (i + 2): + * Vertex[2] is color for first triangle, + * Vertex[3] is color for second triangle... + */ + m_numberOfVerticesPerPrimitive = 1; + m_vertexOffsetForPrimitive = 2; + break; + case GraphicsPrimitive::PrimitiveType::OPENGL_TRIANGLES: + m_numberOfVerticesPerPrimitive = 3; + break; + case GraphicsPrimitive::PrimitiveType::SPHERES: + m_numberOfVerticesPerPrimitive = numberOfVertices; + break; + } + + if (numberOfVertices > 0) { + const int32_t numberOfPrimitives = (numberOfVertices / m_numberOfVerticesPerPrimitive) - m_vertexOffsetForPrimitive; + const int32_t maxPrimitivesSupported = 255 * 255 * 255; + if (numberOfPrimitives > maxPrimitivesSupported) { + const AString msg("Number of primitives for selection=" + + AString::number(numberOfPrimitives) + + " exceeds maximum allowed=" + + AString::number(maxPrimitivesSupported)); + CaretAssertMessage(0, msg); + CaretLogSevere(msg); + } + else { + const int32_t selectionRgbaSize = numberOfVertices * 4; + + if (selectionRgbaSize != static_cast(m_selectionEncodedRGBA.size())) { + const GraphicsPrimitive::ColorDataType colorType = m_parentGraphicsPrimitive->getColorDataType(); + + m_selectionEncodedRGBA.resize(selectionRgbaSize); + + /* + * Encode the primitive index into the RGBA selection data. + */ + int32_t indexRGBA = 0; + const int32_t startIndex = m_vertexOffsetForPrimitive; + const int32_t endIndex = m_vertexOffsetForPrimitive + numberOfPrimitives; + for (int32_t primitiveIndex = startIndex; primitiveIndex < endIndex; primitiveIndex++) { + uint8_t blue = (uint8_t)(primitiveIndex & 0xff); + uint8_t green = (uint8_t)((primitiveIndex >> 8) & 0xff); + uint8_t red = (uint8_t)((primitiveIndex >> 16) & 0xff); + + /* + * Same ID color at all vertices of a primitive + */ + for (int32_t j = 0; j < m_numberOfVerticesPerPrimitive; j++) { + CaretAssertVectorIndex(m_selectionEncodedRGBA, indexRGBA+3); + m_selectionEncodedRGBA[indexRGBA] = red; + m_selectionEncodedRGBA[indexRGBA+1] = green; + m_selectionEncodedRGBA[indexRGBA+2] = blue; + + uint8_t vertexAlpha = 0; + const int32_t vertexAlphaIndex = indexRGBA + 3; + switch (colorType) { + case GraphicsPrimitive::ColorDataType::NONE: + vertexAlpha = 255; + break; + case GraphicsPrimitive::ColorDataType::FLOAT_RGBA: + CaretAssertVectorIndex(m_parentGraphicsPrimitive->m_floatRGBA, + vertexAlphaIndex); + vertexAlpha = static_cast(m_parentGraphicsPrimitive->m_floatRGBA[vertexAlphaIndex] * 255); + break; + case GraphicsPrimitive::ColorDataType::UNSIGNED_BYTE_RGBA: + CaretAssertVectorIndex(m_parentGraphicsPrimitive->m_unsignedByteRGBA, + vertexAlphaIndex); + vertexAlpha = m_parentGraphicsPrimitive->m_unsignedByteRGBA[vertexAlphaIndex]; + break; + } + + m_selectionEncodedRGBA[indexRGBA+3] = vertexAlpha; + + if (primitiveIndex == startIndex) { + if (startIndex > 0) { + /* + * For THE FIRST triangle in a triangle strip or fan, + * its color is at vertex 2 (assuming first index is ZERO). + * Here we copy the color from vertex 2 to vertices + * 0 and 1. This is not absolutely necessary but it + * ensures correct color values and avoid a possible + * NaN from unintialized values. + */ + for (int32_t m = 0; m < startIndex; m++) { + const int32_t m4 = m * 4; + for (int32_t n = 0; n < 4; n++) { + m_selectionEncodedRGBA[m4 + n] = m_selectionEncodedRGBA[indexRGBA + n]; + } + } + } + } + indexRGBA += 4; + } + } + } + } + } + else { + m_selectionEncodedRGBA.clear(); + } +} + +/** + * Get the index of the primitive that was selected using the encoded RGBA. + * + * @param rgba + * RGBA with encoded selection information. + * @return + * Index of selected primitive or -1 if none selected. + */ +int32_t +GraphicsPrimitiveSelectionHelper::getPrimitiveIndexFromEncodedRGBA(const uint8_t rgba[4]) const +{ + int32_t primitiveIndex(-1); + + const int32_t numberOfVertices = static_cast(m_selectionEncodedRGBA.size() / 4); + if (numberOfVertices > 0) { + CaretAssert(m_numberOfVerticesPerPrimitive > 0); + for (int32_t i = 0; i < numberOfVertices; i++) { + const int32_t i4 = i * 4; + CaretAssertVectorIndex(m_selectionEncodedRGBA, i4+3); + if ((rgba[0] == m_selectionEncodedRGBA[i4]) + && (rgba[1] == m_selectionEncodedRGBA[i4+1]) + && (rgba[2] == m_selectionEncodedRGBA[i4+2])) { + if (m_selectionEncodedRGBA[i4+3] > 0) { + primitiveIndex = (i / m_numberOfVerticesPerPrimitive) + m_vertexOffsetForPrimitive; + } + break; + } + } + } + + return primitiveIndex; +} + +/** + * @return The RGBA data in which identification information is encoded. + */ +const +std::vector& +GraphicsPrimitiveSelectionHelper::getSelectionEncodedRGBA() const +{ + return m_selectionEncodedRGBA; +} + + +/** + * Get a description of this object's content. + * @return String describing this object's content. + */ +AString +GraphicsPrimitiveSelectionHelper::toString() const +{ + return "GraphicsPrimitiveSelectionHelper"; +} + diff --git a/src/Graphics/GraphicsPrimitiveSelectionHelper.h b/src/Graphics/GraphicsPrimitiveSelectionHelper.h new file mode 100644 index 0000000000000000000000000000000000000000..096af00027821684c2d63f4e2bf93676fec88a8c --- /dev/null +++ b/src/Graphics/GraphicsPrimitiveSelectionHelper.h @@ -0,0 +1,75 @@ +#ifndef __GRAPHICS_PRIMITIVE_SELECTION_HELPER_H__ +#define __GRAPHICS_PRIMITIVE_SELECTION_HELPER_H__ + +/*LICENSE_START*/ +/* + * Copyright (C) 2017 Washington University School of Medicine + * + * 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 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ +/*LICENSE_END*/ + + + +#include + +#include "CaretObject.h" + + + +namespace caret { + + class GraphicsPrimitive; + + class GraphicsPrimitiveSelectionHelper : public CaretObject { + + public: + GraphicsPrimitiveSelectionHelper(GraphicsPrimitive* parentGraphicsPrimitive); + + virtual ~GraphicsPrimitiveSelectionHelper(); + + void setupSelectionBeforeDrawing(); + + const std::vector& getSelectionEncodedRGBA() const; + + int32_t getPrimitiveIndexFromEncodedRGBA(const uint8_t rgba[4]) const; + + // ADD_NEW_METHODS_HERE + + virtual AString toString() const; + + private: + GraphicsPrimitiveSelectionHelper(const GraphicsPrimitiveSelectionHelper&); + + GraphicsPrimitiveSelectionHelper& operator=(const GraphicsPrimitiveSelectionHelper&); + + GraphicsPrimitive* m_parentGraphicsPrimitive; + + int32_t m_numberOfVerticesPerPrimitive = 0; + + int32_t m_vertexOffsetForPrimitive = 0; + + std::vector m_selectionEncodedRGBA; + + // ADD_NEW_MEMBERS_HERE + + }; + +#ifdef __GRAPHICS_PRIMITIVE_SELECTION_HELPER_DECLARE__ + // +#endif // __GRAPHICS_PRIMITIVE_SELECTION_HELPER_DECLARE__ + +} // namespace +#endif //__GRAPHICS_PRIMITIVE_SELECTION_HELPER_H__ diff --git a/src/Graphics/GraphicsPrimitiveV3f.cxx b/src/Graphics/GraphicsPrimitiveV3f.cxx new file mode 100644 index 0000000000000000000000000000000000000000..f9e36fa1e4943f521cc5c6f35ee4c3a36af9a4ce --- /dev/null +++ b/src/Graphics/GraphicsPrimitiveV3f.cxx @@ -0,0 +1,204 @@ + +/*LICENSE_START*/ +/* + * Copyright (C) 2017 Washington University School of Medicine + * + * 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 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ +/*LICENSE_END*/ + +#define __GRAPHICS_PRIMITIVE_V3F_DECLARE__ +#include "GraphicsPrimitiveV3f.h" +#undef __GRAPHICS_PRIMITIVE_V3F_DECLARE__ + +#include "CaretAssert.h" +#include "CaretLogger.h" +#include "GraphicsEngineDataOpenGL.h" + +using namespace caret; + + + +/** + * \class caret::GraphicsPrimitiveV3f + * \brief Primitive containing XYZ with one color (float, unsigned byte) applied to all vertices. + * \ingroup Graphics + * + * When we move to using the programmable pipeline OpenGL, the color components + * can be passed to the shader to avoid adding the color to each vertex. + */ + +/** + * Constructor for solid color float RGBA. + * + * @param primitiveType + * Type of primitive drawn (triangles, lines, etc.) + * @param rgba + * RGBA color components ranging 0.0 to 1.0. + */ +GraphicsPrimitiveV3f::GraphicsPrimitiveV3f(const PrimitiveType primitiveType, + const float rgba[4]) +: GraphicsPrimitive(VertexDataType::FLOAT_XYZ, + NormalVectorDataType::NONE, + ColorDataType::FLOAT_RGBA, + VertexColorType::SOLID_RGBA, + TextureDataType::NONE, + primitiveType) +{ + m_floatSolidRGBA[0] = rgba[0]; + m_floatSolidRGBA[1] = rgba[1]; + m_floatSolidRGBA[2] = rgba[2]; + m_floatSolidRGBA[3] = rgba[3]; +} + +/** + * Constructor for solid color unsigned byte RGBA. + * + * @param primitiveType + * Type of primitive drawn (triangles, lines, etc.) + * @param rgba + * RGBA color components ranging 0.0 to 1.0. + */ +GraphicsPrimitiveV3f::GraphicsPrimitiveV3f(const PrimitiveType primitiveType, + const uint8_t rgba[4]) +: GraphicsPrimitive(VertexDataType::FLOAT_XYZ, + NormalVectorDataType::NONE, + ColorDataType::UNSIGNED_BYTE_RGBA, + VertexColorType::SOLID_RGBA, + TextureDataType::NONE, + primitiveType) +{ + m_unsignedByteSolidRGBA[0] = rgba[0]; + m_unsignedByteSolidRGBA[1] = rgba[1]; + m_unsignedByteSolidRGBA[2] = rgba[2]; + m_unsignedByteSolidRGBA[3] = rgba[3]; +} + +/** + * Destructor. + */ +GraphicsPrimitiveV3f::~GraphicsPrimitiveV3f() +{ +} + +/** + * Copy constructor. + * @param obj + * Object that is copied. + */ +GraphicsPrimitiveV3f::GraphicsPrimitiveV3f(const GraphicsPrimitiveV3f& obj) +: GraphicsPrimitive(obj) +{ + this->copyHelperGraphicsPrimitiveV3f(obj); +} + +/** + * Helps with copying an object of this type. + * @param obj + * Object that is copied. + */ +void +GraphicsPrimitiveV3f::copyHelperGraphicsPrimitiveV3f(const GraphicsPrimitiveV3f& obj) +{ + for (int32_t i = 0; i < 4; i++) { + m_floatSolidRGBA[i] = obj.m_floatSolidRGBA[i]; + m_unsignedByteSolidRGBA[i] = obj.m_unsignedByteSolidRGBA[i]; + } +} + +/** + * Add a vertex. + * + * @param xyz + * Coordinate of vertex. + */ +void +GraphicsPrimitiveV3f::addVertex(const float xyz[3]) +{ + addVertexProtected(xyz, + NULL, + m_floatSolidRGBA, + m_unsignedByteSolidRGBA, + NULL); +} + +/** + * Add a vertex. + * + * @param x + * X-coordinate of vertex. + * @param y + * Y-coordinate of vertex. + * @param z + * Z-coordinate of vertex. + */ +void +GraphicsPrimitiveV3f::addVertex(const float x, + const float y, + const float z) +{ + const float xyz[3] = { x, y, z }; + addVertexProtected(xyz, + NULL, + m_floatSolidRGBA, + m_unsignedByteSolidRGBA, + NULL); +} + +/** + * Add a 2D vertex. Z will be zero. + * + * @param x + * X-coordinate of vertex. + * @param y + * Y-coordinate of vertex. + * @param rgba + * RGBA color components ranging 0.0 to 1.0. + */ +void +GraphicsPrimitiveV3f::addVertex(const float x, + const float y) +{ + addVertex(x, y, 0.0f); +} + +/** + * Add XYZ vertices from an array. + * + * @param xyzArray + * Array containing XYZ vertex data. + * @param numberOfVertices + * Number of vertices (xyz triplets) to add + */ +void +GraphicsPrimitiveV3f::addVertices(const float xyzArray[], + const int32_t numberOfVertices) +{ + for (int32_t i = 0; i < numberOfVertices; i++) { + addVertex(&xyzArray[i*3]); + } +} + + +/** + * Clone this primitive. + */ +GraphicsPrimitive* +GraphicsPrimitiveV3f::clone() const +{ + GraphicsPrimitiveV3f* obj = new GraphicsPrimitiveV3f(*this); + return obj; +} + diff --git a/src/Graphics/GraphicsPrimitiveV3f.h b/src/Graphics/GraphicsPrimitiveV3f.h new file mode 100644 index 0000000000000000000000000000000000000000..f11925420ea9f63eb7cf2bb58935d5016574df55 --- /dev/null +++ b/src/Graphics/GraphicsPrimitiveV3f.h @@ -0,0 +1,81 @@ +#ifndef __GRAPHICS_PRIMITIVE_V3F_H__ +#define __GRAPHICS_PRIMITIVE_V3F_H__ + +/*LICENSE_START*/ +/* + * Copyright (C) 2017 Washington University School of Medicine + * + * 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 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ +/*LICENSE_END*/ + + + +#include + +#include "GraphicsPrimitive.h" + + + +namespace caret { + + class GraphicsPrimitiveV3f : public GraphicsPrimitive { + + public: + GraphicsPrimitiveV3f(const PrimitiveType primitiveType, + const float rgba[4]); + + GraphicsPrimitiveV3f(const PrimitiveType primitiveType, + const uint8_t rgba[4]); + + virtual ~GraphicsPrimitiveV3f(); + + GraphicsPrimitiveV3f(const GraphicsPrimitiveV3f& obj); + + void addVertex(const float xyz[3]); + + void addVertex(const float x, + const float y, + const float z); + + void addVertex(const float x, + const float y); + + void addVertices(const float xyzArray[], + const int32_t numberOfVertices); + + virtual GraphicsPrimitive* clone() const; + + // ADD_NEW_METHODS_HERE + + private: + GraphicsPrimitiveV3f& operator=(const GraphicsPrimitiveV3f& obj); + + void copyHelperGraphicsPrimitiveV3f(const GraphicsPrimitiveV3f& obj); + + float m_floatSolidRGBA[4]; + + uint8_t m_unsignedByteSolidRGBA[4]; + + // ADD_NEW_MEMBERS_HERE + + }; + +#ifdef __GRAPHICS_PRIMITIVE_V3F_DECLARE__ + // +#endif // __GRAPHICS_PRIMITIVE_V3F_DECLARE__ + +} // namespace +#endif //__GRAPHICS_PRIMITIVE_V3F_H__ diff --git a/src/Graphics/GraphicsPrimitiveV3fC4f.cxx b/src/Graphics/GraphicsPrimitiveV3fC4f.cxx new file mode 100644 index 0000000000000000000000000000000000000000..7a731e077f6a7648db948ef179b1721d01a69d1f --- /dev/null +++ b/src/Graphics/GraphicsPrimitiveV3fC4f.cxx @@ -0,0 +1,152 @@ + +/*LICENSE_START*/ +/* + * Copyright (C) 2017 Washington University School of Medicine + * + * 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 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ +/*LICENSE_END*/ + +#define __GRAPHICS_PRIMITIVE_V3F_C4F_DECLARE__ +#include "GraphicsPrimitiveV3fC4f.h" +#undef __GRAPHICS_PRIMITIVE_V3F_C4F_DECLARE__ + +#include "CaretAssert.h" +using namespace caret; + + + +/** + * \class caret::GraphicsPrimitiveV3fC4f + * \brief Primitive containing XYZ and Float RGBA. + * \ingroup Graphics + */ + +/** + * Constructor. + * + * @param primitiveType + * Type of primitive drawn (triangles, lines, etc.) + */ +GraphicsPrimitiveV3fC4f::GraphicsPrimitiveV3fC4f(const PrimitiveType primitiveType) +: GraphicsPrimitive(VertexDataType::FLOAT_XYZ, + NormalVectorDataType::NONE, + ColorDataType::FLOAT_RGBA, + VertexColorType::PER_VERTEX_RGBA, + TextureDataType::NONE, + primitiveType) +{ + +} + +/** + * Destructor. + */ +GraphicsPrimitiveV3fC4f::~GraphicsPrimitiveV3fC4f() +{ +} + +/** + * Copy constructor. + * @param obj + * Object that is copied. + */ +GraphicsPrimitiveV3fC4f::GraphicsPrimitiveV3fC4f(const GraphicsPrimitiveV3fC4f& obj) +: GraphicsPrimitive(obj) +{ + this->copyHelperGraphicsPrimitiveV3fC4f(obj); +} + +/** + * Helps with copying an object of this type. + * @param obj + * Object that is copied. + */ +void +GraphicsPrimitiveV3fC4f::copyHelperGraphicsPrimitiveV3fC4f(const GraphicsPrimitiveV3fC4f& /*obj*/) +{ + +} + +/** + * Add a vertex. + * + * @param xyz + * Coordinate of vertex. + * @param rgba + * RGBA color components ranging 0.0 to 1.0. + */ +void +GraphicsPrimitiveV3fC4f::addVertex(const float xyz[3], + const float rgba[4]) +{ + addVertexProtected(xyz, + NULL, + rgba, + NULL, + NULL); +} + +/** + * Add a vertex. + * + * @param x + * X-coordinate of vertex. + * @param y + * Y-coordinate of vertex. + * @param z + * Z-coordinate of vertex. + * @param rgba + * RGBA color components ranging 0.0 to 1.0. + */ +void +GraphicsPrimitiveV3fC4f::addVertex(const float x, + const float y, + const float z, + const float rgba[4]) +{ + const float xyz[] { x, y, z }; + addVertex(xyz, rgba); +} + +/** + * Add a 2D vertex. Z will be zero. + * + * @param x + * X-coordinate of vertex. + * @param y + * Y-coordinate of vertex. + * @param rgba + * RGBA color components ranging 0.0 to 1.0. + */ +void +GraphicsPrimitiveV3fC4f::addVertex(const float x, + const float y, + const float rgba[4]) +{ + const float xyz[] { x, y, 0.0f }; + addVertex(xyz, rgba); +} + +/** + * Clone this primitive. + */ +GraphicsPrimitive* +GraphicsPrimitiveV3fC4f::clone() const +{ + GraphicsPrimitiveV3fC4f* obj = new GraphicsPrimitiveV3fC4f(*this); + return obj; +} + diff --git a/src/Graphics/GraphicsPrimitiveV3fC4f.h b/src/Graphics/GraphicsPrimitiveV3fC4f.h new file mode 100644 index 0000000000000000000000000000000000000000..0de2cc905fe1dc6ea6f05f15699929a6078c1cb7 --- /dev/null +++ b/src/Graphics/GraphicsPrimitiveV3fC4f.h @@ -0,0 +1,73 @@ +#ifndef __GRAPHICS_PRIMITIVE_V3F_C4F_H__ +#define __GRAPHICS_PRIMITIVE_V3F_C4F_H__ + +/*LICENSE_START*/ +/* + * Copyright (C) 2017 Washington University School of Medicine + * + * 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 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ +/*LICENSE_END*/ + + + +#include + +#include "GraphicsPrimitive.h" + + + +namespace caret { + + class GraphicsPrimitiveV3fC4f : public GraphicsPrimitive { + + public: + GraphicsPrimitiveV3fC4f(const PrimitiveType primitiveType); + + virtual ~GraphicsPrimitiveV3fC4f(); + + GraphicsPrimitiveV3fC4f(const GraphicsPrimitiveV3fC4f& obj); + + void addVertex(const float xyz[3], + const float rgba[4]); + + void addVertex(const float x, + const float y, + const float z, + const float rgba[4]); + + void addVertex(const float x, + const float y, + const float rgba[4]); + + virtual GraphicsPrimitive* clone() const; + + // ADD_NEW_METHODS_HERE + + private: + GraphicsPrimitiveV3fC4f& operator=(const GraphicsPrimitiveV3fC4f& obj); + + void copyHelperGraphicsPrimitiveV3fC4f(const GraphicsPrimitiveV3fC4f& obj); + + // ADD_NEW_MEMBERS_HERE + + }; + +#ifdef __GRAPHICS_PRIMITIVE_V3F_C4F_DECLARE__ + // +#endif // __GRAPHICS_PRIMITIVE_V3F_C4F_DECLARE__ + +} // namespace +#endif //__GRAPHICS_PRIMITIVE_V3F_C4F_H__ diff --git a/src/Graphics/GraphicsPrimitiveV3fC4ub.cxx b/src/Graphics/GraphicsPrimitiveV3fC4ub.cxx new file mode 100644 index 0000000000000000000000000000000000000000..44a872d832bfe756ac7d5e97c8c4f633f467fb74 --- /dev/null +++ b/src/Graphics/GraphicsPrimitiveV3fC4ub.cxx @@ -0,0 +1,134 @@ + +/*LICENSE_START*/ +/* + * Copyright (C) 2017 Washington University School of Medicine + * + * 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 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ +/*LICENSE_END*/ + +#define __GRAPHICS_PRIMITIVE_V3F_C4UB_DECLARE__ +#include "GraphicsPrimitiveV3fC4ub.h" +#undef __GRAPHICS_PRIMITIVE_V3F_C4UB_DECLARE__ + +#include "CaretAssert.h" +using namespace caret; + + + +/** + * \class caret::GraphicsPrimitiveV3fC4ub + * \brief Primitive containing XYZ and Float RGBA. + * \ingroup Graphics + */ + +/** + * Constructor. + * + * @param primitiveType + * Type of primitive drawn (triangles, lines, etc.) + */ +GraphicsPrimitiveV3fC4ub::GraphicsPrimitiveV3fC4ub(const PrimitiveType primitiveType) +: GraphicsPrimitive(VertexDataType::FLOAT_XYZ, + NormalVectorDataType::NONE, + ColorDataType::UNSIGNED_BYTE_RGBA, + VertexColorType::PER_VERTEX_RGBA, + TextureDataType::NONE, + primitiveType) +{ + +} + +/** + * Destructor. + */ +GraphicsPrimitiveV3fC4ub::~GraphicsPrimitiveV3fC4ub() +{ +} + +/** + * Copy constructor. + * @param obj + * Object that is copied. + */ +GraphicsPrimitiveV3fC4ub::GraphicsPrimitiveV3fC4ub(const GraphicsPrimitiveV3fC4ub& obj) +: GraphicsPrimitive(obj) +{ + this->copyHelperGraphicsPrimitiveV3fC4ub(obj); +} + +/** + * Helps with copying an object of this type. + * @param obj + * Object that is copied. + */ +void +GraphicsPrimitiveV3fC4ub::copyHelperGraphicsPrimitiveV3fC4ub(const GraphicsPrimitiveV3fC4ub& /*obj*/) +{ + +} + +/** + * Add a vertex. + * + * @param xyz + * Coordinate of vertex. + * @param rgba + * RGBA color components ranging 0.0 to 1.0. + */ +void +GraphicsPrimitiveV3fC4ub::addVertex(const float xyz[3], + const uint8_t rgba[4]) +{ + addVertexProtected(xyz, + NULL, + NULL, + rgba, + NULL); +} + +/** + * Add a vertex. + * + * @param x + * X-coordinate of vertex. + * @param y + * Y-coordinate of vertex. + * @param z + * Z-coordinate of vertex. + * @param rgba + * RGBA color components ranging 0.0 to 1.0. + */ +void +GraphicsPrimitiveV3fC4ub::addVertex(const float x, + const float y, + const float z, + const uint8_t rgba[4]) +{ + const float xyz[] { x, y, z }; + addVertex(xyz, + rgba); +} + +/** + * Clone this primitive. + */ +GraphicsPrimitive* +GraphicsPrimitiveV3fC4ub::clone() const +{ + GraphicsPrimitiveV3fC4ub* obj = new GraphicsPrimitiveV3fC4ub(*this); + return obj; +} + diff --git a/src/Graphics/GraphicsPrimitiveV3fC4ub.h b/src/Graphics/GraphicsPrimitiveV3fC4ub.h new file mode 100644 index 0000000000000000000000000000000000000000..b17781e6d51c49674296c86751cccc08e0fe597d --- /dev/null +++ b/src/Graphics/GraphicsPrimitiveV3fC4ub.h @@ -0,0 +1,67 @@ +#ifndef __GRAPHICS_PRIMITIVE_V3F_C4UB_H__ +#define __GRAPHICS_PRIMITIVE_V3F_C4UB_H__ + +/*LICENSE_START*/ +/* + * Copyright (C) 2017 Washington University School of Medicine + * + * 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 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ +/*LICENSE_END*/ + + + +#include + +#include "GraphicsPrimitive.h" + +namespace caret { + + class GraphicsPrimitiveV3fC4ub : public GraphicsPrimitive { + + public: + GraphicsPrimitiveV3fC4ub(const PrimitiveType primitiveType); + + virtual ~GraphicsPrimitiveV3fC4ub(); + + GraphicsPrimitiveV3fC4ub(const GraphicsPrimitiveV3fC4ub& obj); + + void addVertex(const float xyz[3], + const uint8_t rgba[4]); + + void addVertex(const float x, + const float y, + const float z, + const uint8_t rgba[4]); + + virtual GraphicsPrimitive* clone() const; + + // ADD_NEW_METHODS_HERE + + private: + GraphicsPrimitiveV3fC4ub& operator=(const GraphicsPrimitiveV3fC4ub& obj); + + void copyHelperGraphicsPrimitiveV3fC4ub(const GraphicsPrimitiveV3fC4ub& obj); + + // ADD_NEW_MEMBERS_HERE + + }; + +#ifdef __GRAPHICS_PRIMITIVE_V3F_C4UB_DECLARE__ + // +#endif // __GRAPHICS_PRIMITIVE_V3F_C4UB_DECLARE__ + +} // namespace +#endif //__GRAPHICS_PRIMITIVE_V3F_C4UB_H__ diff --git a/src/Graphics/GraphicsPrimitiveV3fN3f.cxx b/src/Graphics/GraphicsPrimitiveV3fN3f.cxx new file mode 100644 index 0000000000000000000000000000000000000000..2062aef84925ab896732c2f688fdf01541285b7b --- /dev/null +++ b/src/Graphics/GraphicsPrimitiveV3fN3f.cxx @@ -0,0 +1,231 @@ + +/*LICENSE_START*/ +/* + * Copyright (C) 2017 Washington University School of Medicine + * + * 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 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ +/*LICENSE_END*/ + +#define __GRAPHICS_PRIMITIVE_V3F_N3F_DECLARE__ +#include "GraphicsPrimitiveV3fN3f.h" +#undef __GRAPHICS_PRIMITIVE_V3F_N3F_DECLARE__ + +#include "CaretAssert.h" +#include "CaretLogger.h" +#include "GraphicsEngineDataOpenGL.h" + +using namespace caret; + + + +/** + * \class caret::GraphicsPrimitiveV3fN3f + * \brief Primitive containing vertices, normals, and with one color (float, unsigned byte) applied to all vertices. + * \ingroup Graphics + * + * When we move to using the programmable pipeline OpenGL, the color components + * can be passed to the shader to avoid adding the color to each vertex. + */ + +/** + * Constructor for solid color float RGBA. + * + * @param primitiveType + * Type of primitive drawn (triangles, lines, etc.) + * @param rgba + * RGBA color components ranging 0.0 to 1.0. + */ +GraphicsPrimitiveV3fN3f::GraphicsPrimitiveV3fN3f(const PrimitiveType primitiveType, + const float rgba[4]) +: GraphicsPrimitive(VertexDataType::FLOAT_XYZ, + NormalVectorDataType::FLOAT_XYZ, + ColorDataType::FLOAT_RGBA, + VertexColorType::SOLID_RGBA, + TextureDataType::NONE, + primitiveType) +{ + m_floatSolidRGBA[0] = rgba[0]; + m_floatSolidRGBA[1] = rgba[1]; + m_floatSolidRGBA[2] = rgba[2]; + m_floatSolidRGBA[3] = rgba[3]; +} + +/** + * Constructor for solid color unsigned byte RGBA. + * + * @param primitiveType + * Type of primitive drawn (triangles, lines, etc.) + * @param rgba + * RGBA color components ranging 0.0 to 1.0. + */ +GraphicsPrimitiveV3fN3f::GraphicsPrimitiveV3fN3f(const PrimitiveType primitiveType, + const uint8_t rgba[4]) +: GraphicsPrimitive(VertexDataType::FLOAT_XYZ, + NormalVectorDataType::FLOAT_XYZ, + ColorDataType::UNSIGNED_BYTE_RGBA, + VertexColorType::SOLID_RGBA, + TextureDataType::NONE, + primitiveType) +{ + m_unsignedByteSolidRGBA[0] = rgba[0]; + m_unsignedByteSolidRGBA[1] = rgba[1]; + m_unsignedByteSolidRGBA[2] = rgba[2]; + m_unsignedByteSolidRGBA[3] = rgba[3]; +} + +/** + * Destructor. + */ +GraphicsPrimitiveV3fN3f::~GraphicsPrimitiveV3fN3f() +{ +} + +/** + * Copy constructor. + * @param obj + * Object that is copied. + */ +GraphicsPrimitiveV3fN3f::GraphicsPrimitiveV3fN3f(const GraphicsPrimitiveV3fN3f& obj) +: GraphicsPrimitive(obj) +{ + this->copyHelperGraphicsPrimitiveV3fN3f(obj); +} + +/** + * Helps with copying an object of this type. + * @param obj + * Object that is copied. + */ +void +GraphicsPrimitiveV3fN3f::copyHelperGraphicsPrimitiveV3fN3f(const GraphicsPrimitiveV3fN3f& obj) +{ + for (int32_t i = 0; i < 4; i++) { + m_floatSolidRGBA[i] = obj.m_floatSolidRGBA[i]; + m_unsignedByteSolidRGBA[i] = obj.m_unsignedByteSolidRGBA[i]; + } +} + +/** + * Add a vertex. + * + * @param xyz + * Coordinate of vertex. + * @param normalXYZ + * Normal vector + */ +void +GraphicsPrimitiveV3fN3f::addVertex(const float xyz[3], + const float normalXYZ[3]) +{ + addVertexProtected(xyz, + normalXYZ, + m_floatSolidRGBA, + m_unsignedByteSolidRGBA, + NULL); +} + +/** + * Add a vertex. + * + * @param x + * X-coordinate of vertex. + * @param y + * Y-coordinate of vertex. + * @param z + * Z-coordinate of vertex. + * @param normalX + * X component of normal vector + * @param normalY + * Y component of normal vector + * @param normalZ + * Z component of normal vector + */ +void +GraphicsPrimitiveV3fN3f::addVertex(const float x, + const float y, + const float z, + const float normalX, + const float normalY, + const float normalZ) +{ + const float xyz[] { x, y, z }; + const float normalXYZ[] { normalX, normalY, normalZ }; + addVertexProtected(xyz, + normalXYZ, + m_floatSolidRGBA, + m_unsignedByteSolidRGBA, + NULL); +} + +/** + * Add a 2D vertex. Z will be zero. + * + * @param x + * X-coordinate of vertex. + * @param y + * Y-coordinate of vertex. + * @param normalX + * X component of normal vector + * @param normalY + * Y component of normal vector + */ +void +GraphicsPrimitiveV3fN3f::addVertex(const float x, + const float y, + const float normalX, + const float normalY) +{ + const float xyz[] { x, y, 0.0f }; + const float normalXYZ[] { normalX, normalY, 1.0f }; + addVertexProtected(xyz, + normalXYZ, + m_floatSolidRGBA, + m_unsignedByteSolidRGBA, + NULL); +} + +/** + * Add XYZ vertices from an array. + * + * @param xyzArray + * Array containing XYZ vertex data. + * @param normalXyzArray + * Array containing XYZ normal vector data. + * @param numberOfVertices + * Number of vertices (xyz triplets) to add + */ +void +GraphicsPrimitiveV3fN3f::addVertices(const float xyzArray[], + const float normalXyzArray[], + const int32_t numberOfVertices) +{ + for (int32_t i = 0; i < numberOfVertices; i++) { + addVertex(&xyzArray[i*3], + &normalXyzArray[i*3]); + } +} + + +/** + * Clone this primitive. + */ +GraphicsPrimitive* +GraphicsPrimitiveV3fN3f::clone() const +{ + GraphicsPrimitiveV3fN3f* obj = new GraphicsPrimitiveV3fN3f(*this); + return obj; +} + diff --git a/src/Graphics/GraphicsPrimitiveV3fN3f.h b/src/Graphics/GraphicsPrimitiveV3fN3f.h new file mode 100644 index 0000000000000000000000000000000000000000..85a49cc1f2c4706d97dbfcd6f9aa418dc7a2c1cb --- /dev/null +++ b/src/Graphics/GraphicsPrimitiveV3fN3f.h @@ -0,0 +1,88 @@ +#ifndef __GRAPHICS_PRIMITIVE_V3F_N3F_H__ +#define __GRAPHICS_PRIMITIVE_V3F_N3F_H__ + +/*LICENSE_START*/ +/* + * Copyright (C) 2017 Washington University School of Medicine + * + * 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 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ +/*LICENSE_END*/ + + + +#include + +#include "GraphicsPrimitive.h" + + + +namespace caret { + + class GraphicsPrimitiveV3fN3f : public GraphicsPrimitive { + + public: + GraphicsPrimitiveV3fN3f(const PrimitiveType primitiveType, + const float rgba[4]); + + GraphicsPrimitiveV3fN3f(const PrimitiveType primitiveType, + const uint8_t rgba[4]); + + virtual ~GraphicsPrimitiveV3fN3f(); + + GraphicsPrimitiveV3fN3f(const GraphicsPrimitiveV3fN3f& obj); + + void addVertex(const float xyz[3], + const float normalXYZ[3]); + + void addVertex(const float x, + const float y, + const float z, + const float normalX, + const float normalY, + const float normalZ); + + void addVertex(const float x, + const float y, + const float normalX, + const float normalY); + + void addVertices(const float xyzArray[], + const float normalXyzArray[], + const int32_t numberOfVertices); + + virtual GraphicsPrimitive* clone() const; + + // ADD_NEW_METHODS_HERE + + private: + GraphicsPrimitiveV3fN3f& operator=(const GraphicsPrimitiveV3fN3f& obj); + + void copyHelperGraphicsPrimitiveV3fN3f(const GraphicsPrimitiveV3fN3f& obj); + + float m_floatSolidRGBA[4]; + + uint8_t m_unsignedByteSolidRGBA[4]; + + // ADD_NEW_MEMBERS_HERE + + }; + +#ifdef __GRAPHICS_PRIMITIVE_V3F_N3F_DECLARE__ + // +#endif // __GRAPHICS_PRIMITIVE_V3F_N3F_DECLARE__ + +} // namespace +#endif //__GRAPHICS_PRIMITIVE_V3F_N3F_H__ diff --git a/src/Graphics/GraphicsPrimitiveV3fT3f.cxx b/src/Graphics/GraphicsPrimitiveV3fT3f.cxx new file mode 100644 index 0000000000000000000000000000000000000000..c6de7875263cdcd488ea9d070b90a649a575749a --- /dev/null +++ b/src/Graphics/GraphicsPrimitiveV3fT3f.cxx @@ -0,0 +1,171 @@ + +/*LICENSE_START*/ +/* + * Copyright (C) 2017 Washington University School of Medicine + * + * 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 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ +/*LICENSE_END*/ + +#define __GRAPHICS_PRIMITIVE_V3F_T3F_DECLARE__ +#include "GraphicsPrimitiveV3fT3f.h" +#undef __GRAPHICS_PRIMITIVE_V3F_T3F_DECLARE__ + +#include "CaretAssert.h" +using namespace caret; + + + +/** + * \class caret::GraphicsPrimitiveV3fT3f + * \brief Primitive containing XYZ with and texture coordinates applied to all vertices. + * \ingroup Graphics + */ + +/** + * Constructor for solid color float RGBA. + * + * @param primitiveType + * Type of primitive drawn (triangles, lines, etc.) + * @param imageBytesRGBA + * Bytes containing the image data. + * @param imageWidth + * Width of the actual image. + * @param imageHeight + * Height of the image. + */ +GraphicsPrimitiveV3fT3f::GraphicsPrimitiveV3fT3f(const PrimitiveType primitiveType, + const uint8_t* imageBytesRGBA, + const int32_t imageWidth, + const int32_t imageHeight) +: GraphicsPrimitive(VertexDataType::FLOAT_XYZ, + NormalVectorDataType::NONE, + ColorDataType::NONE, + VertexColorType::NONE, + TextureDataType::FLOAT_STR, + primitiveType) +{ + setTextureImage(imageBytesRGBA, + imageWidth, + imageHeight); +} + +/** + * Destructor. + */ +GraphicsPrimitiveV3fT3f::~GraphicsPrimitiveV3fT3f() +{ +} + +/** + * Copy constructor. + * @param obj + * Object that is copied. + */ +GraphicsPrimitiveV3fT3f::GraphicsPrimitiveV3fT3f(const GraphicsPrimitiveV3fT3f& obj) +: GraphicsPrimitive(obj) +{ + this->copyHelperGraphicsPrimitiveV3fT3f(obj); +} + +/** + * Helps with copying an object of this type. + * @param obj + * Object that is copied. + */ +void +GraphicsPrimitiveV3fT3f::copyHelperGraphicsPrimitiveV3fT3f(const GraphicsPrimitiveV3fT3f& /*obj*/) +{ + +} + +/** + * Add a vertex. + * + * @param xyz + * Coordinate of vertex. + * @parma st + * Texture coordinates + */ +void +GraphicsPrimitiveV3fT3f::addVertex(const float xyz[3], + const float st[2]) +{ + const float str[] { st[0], st[1], 0.0f } ; + addVertexProtected(xyz, + NULL, + NULL, + NULL, + str); +} + +/** + * Add a vertex. + * + * @param x + * X-coordinate of vertex. + * @param y + * Y-coordinate of vertex. + * @param z + * Z-coordinate of vertex. + * @param s + * S-coordinate of texture + * @param t + * T-coordinate of texture + */ +void +GraphicsPrimitiveV3fT3f::addVertex(const float x, + const float y, + const float z, + const float s, + const float t) +{ + const float xyz[] { x, y, z }; + const float str[] { s, t, 0.0f }; + addVertex(xyz, + str); +} + +/** + * Add a 2D vertex. Z will be zero. + * + * @param x + * X-coordinate of vertex. + * @param y + * Y-coordinate of vertex. + * @param s + * S-coordinate of texture + * @param t + * T-coordinate of texture + */ +void +GraphicsPrimitiveV3fT3f::addVertex(const float x, + const float y, + const float s, + const float t) +{ + addVertex(x, y, 0.0f, s, t); +} + +/** + * Clone this primitive. + */ +GraphicsPrimitive* +GraphicsPrimitiveV3fT3f::clone() const +{ + GraphicsPrimitiveV3fT3f* obj = new GraphicsPrimitiveV3fT3f(*this); + return obj; +} + diff --git a/src/Graphics/GraphicsPrimitiveV3fT3f.h b/src/Graphics/GraphicsPrimitiveV3fT3f.h new file mode 100644 index 0000000000000000000000000000000000000000..a0845280bcaddd718553862ce75044f3e67508c6 --- /dev/null +++ b/src/Graphics/GraphicsPrimitiveV3fT3f.h @@ -0,0 +1,78 @@ +#ifndef __GRAPHICS_PRIMITIVE_V3F_T3F_H__ +#define __GRAPHICS_PRIMITIVE_V3F_T3F_H__ + +/*LICENSE_START*/ +/* + * Copyright (C) 2017 Washington University School of Medicine + * + * 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 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ +/*LICENSE_END*/ + + + +#include + +#include "GraphicsPrimitive.h" + + + +namespace caret { + + class GraphicsPrimitiveV3fT3f : public GraphicsPrimitive { + + public: + GraphicsPrimitiveV3fT3f(const PrimitiveType primitiveType, + const uint8_t* imageBytesRGBA, + const int32_t imageWidth, + const int32_t imageHeight); + + virtual ~GraphicsPrimitiveV3fT3f(); + + GraphicsPrimitiveV3fT3f(const GraphicsPrimitiveV3fT3f& obj); + + void addVertex(const float xyz[3], + const float st[2]); + + void addVertex(const float x, + const float y, + const float z, + const float s, + const float t); + + void addVertex(const float x, + const float y, + const float s, + const float t); + + virtual GraphicsPrimitive* clone() const; + + // ADD_NEW_METHODS_HERE + + private: + GraphicsPrimitiveV3fT3f& operator=(const GraphicsPrimitiveV3fT3f& obj); + + void copyHelperGraphicsPrimitiveV3fT3f(const GraphicsPrimitiveV3fT3f& obj); + + // ADD_NEW_MEMBERS_HERE + + }; + +#ifdef __GRAPHICS_PRIMITIVE_V3F_T3F_DECLARE__ + // +#endif // __GRAPHICS_PRIMITIVE_V3F_T3F_DECLARE__ + +} // namespace +#endif //__GRAPHICS_PRIMITIVE_V3F_T3F_H__ diff --git a/src/Graphics/GraphicsShape.cxx b/src/Graphics/GraphicsShape.cxx new file mode 100644 index 0000000000000000000000000000000000000000..dd58b47cb8360b0e0c58c824d8c27ceae7f44fee --- /dev/null +++ b/src/Graphics/GraphicsShape.cxx @@ -0,0 +1,1190 @@ + +/*LICENSE_START*/ +/* + * Copyright (C) 2017 Washington University School of Medicine + * + * 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 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ +/*LICENSE_END*/ + +#define __GRAPHICS_SHAPE_DECLARE__ +#include "GraphicsShape.h" +#undef __GRAPHICS_SHAPE_DECLARE__ + +#include + +#include "CaretAssert.h" +#include "CaretLogger.h" +#include "GraphicsEngineDataOpenGL.h" +#include "GraphicsPrimitive.h" +#include "GraphicsPrimitiveV3f.h" +#include "GraphicsPrimitiveV3fC4ub.h" +#include "GraphicsPrimitiveV3fN3f.h" +#include "MathFunctions.h" +#include "Matrix4x4.h" + +using namespace caret; + + + +/** + * \class caret::GraphicsShape + * \brief Methods for drawing shapes. + * \ingroup Graphics + */ + +/** + * Constructor. + */ +GraphicsShape::GraphicsShape() +: CaretObject() +{ + +} + +/** + * Destructor. + */ +GraphicsShape::~GraphicsShape() +{ +} + +/** + * Delete all primitives. + */ +void +GraphicsShape::deleteAllPrimitives() +{ + for (auto iter : s_byteSpherePrimitives) { + delete iter.second; + } + s_byteSpherePrimitives.clear(); + + for (auto iter : s_byteCirclePrimitives) { + delete iter.second; + } + s_byteCirclePrimitives.clear(); + + for (auto iter : s_byteRingPrimitives) { + delete iter.second; + } + s_byteRingPrimitives.clear(); + + /* + * Need to delete here or it will cause a crash + * as there is no EventManager when static data + * is deleted. + */ + s_byteSquarePrimitive.reset(); +} + + +/** + * Draw an outline box. + * + * @param v1 + * First vertex. + * @param v2 + * Second vertex. + * @param v3 + * Third vertex. + * @param v4 + * Fourth vertex. + * @param rgba + * Color for drawing. + * @param lineThicknessType + * Type of line thickness. + * @param lineThickness + * Thickness of the line. + */ +void +GraphicsShape::drawBoxOutlineByteColor(const float v1[3], + const float v2[3], + const float v3[3], + const float v4[3], + const uint8_t rgba[4], + const GraphicsPrimitive::LineWidthType lineThicknessType, + const double lineThickness) +{ + std::unique_ptr primitive(GraphicsPrimitive::newPrimitiveV3f(GraphicsPrimitive::PrimitiveType::POLYGONAL_LINE_LOOP_MITER_JOIN, + rgba)); + primitive->reserveForNumberOfVertices(4); + primitive->addVertex(v1); + primitive->addVertex(v2); + primitive->addVertex(v3); + primitive->addVertex(v4); + + primitive->setLineWidth(lineThicknessType, + lineThickness); + primitive->setUsageTypeAll(GraphicsPrimitive::UsageType::MODIFIED_ONCE_DRAWN_FEW_TIMES); + + GraphicsEngineDataOpenGL::draw(primitive.get()); +} + +/** + * Draw an outline box. + * + * @param v1 + * First vertex. + * @param v2 + * Second vertex. + * @param v3 + * Third vertex. + * @param v4 + * Fourth vertex. + * @param rgba + * Color for drawing. + * @param lineThicknessType + * Type of line thickness. + * @param lineThickness + * Thickness of the line. + */ +void +GraphicsShape::drawBoxOutlineFloatColor(const float v1[3], + const float v2[3], + const float v3[3], + const float v4[3], + const float rgba[4], + const GraphicsPrimitive::LineWidthType lineThicknessType, + const double lineThickness) +{ + std::unique_ptr primitive(GraphicsPrimitive::newPrimitiveV3f(GraphicsPrimitive::PrimitiveType::POLYGONAL_LINE_LOOP_MITER_JOIN, + rgba)); + primitive->reserveForNumberOfVertices(4); + primitive->addVertex(v1); + primitive->addVertex(v2); + primitive->addVertex(v3); + primitive->addVertex(v4); + + primitive->setLineWidth(lineThicknessType, + lineThickness); + primitive->setUsageTypeAll(GraphicsPrimitive::UsageType::MODIFIED_ONCE_DRAWN_FEW_TIMES); + + GraphicsEngineDataOpenGL::draw(primitive.get()); +} + +/** + * Draw a filled box. Vertices must be in a counter-clockwise order. + * + * @param v1 + * First vertex. + * @param v2 + * Second vertex. + * @param v3 + * Third vertex. + * @param v4 + * Fourth vertex. + * @param rgba + * Color for drawing. + */ +void +GraphicsShape::drawBoxFilledByteColor(const float v1[3], + const float v2[3], + const float v3[3], + const float v4[3], + const uint8_t rgba[4]) +{ + std::unique_ptr primitive(GraphicsPrimitive::newPrimitiveV3f(GraphicsPrimitive::PrimitiveType::OPENGL_TRIANGLES, + rgba)); + primitive->reserveForNumberOfVertices(6); + primitive->addVertex(v1); + primitive->addVertex(v2); + primitive->addVertex(v3); + primitive->addVertex(v3); + primitive->addVertex(v4); + primitive->addVertex(v1); + + primitive->setUsageTypeAll(GraphicsPrimitive::UsageType::MODIFIED_ONCE_DRAWN_FEW_TIMES); + + GraphicsEngineDataOpenGL::draw(primitive.get()); +} + +/** + * Draw a filled box. Vertices must be in a counter-clockwise order. + * + * @param v1 + * First vertex. + * @param v2 + * Second vertex. + * @param v3 + * Third vertex. + * @param v4 + * Fourth vertex. + * @param rgba + * Color for drawing. + */ +void +GraphicsShape::drawBoxFilledFloatColor(const float v1[3], + const float v2[3], + const float v3[3], + const float v4[3], + const float rgba[4]) +{ + std::unique_ptr primitive(GraphicsPrimitive::newPrimitiveV3f(GraphicsPrimitive::PrimitiveType::OPENGL_TRIANGLES, + rgba)); + primitive->reserveForNumberOfVertices(6); + primitive->addVertex(v1); + primitive->addVertex(v2); + primitive->addVertex(v3); + primitive->addVertex(v3); + primitive->addVertex(v4); + primitive->addVertex(v1); + + primitive->setUsageTypeAll(GraphicsPrimitive::UsageType::MODIFIED_ONCE_DRAWN_FEW_TIMES); + + GraphicsEngineDataOpenGL::draw(primitive.get()); +} + + +/** + * Draw an outline ellipse. + * + * @param majorAxis + * Diameter of the major axis. + * @param minorAxis + * Diameter of the minor axis. + * @param rgba + * Color for drawing. + * @param lineThicknessType + * Type of line thickness. + * @param lineThickness + * Thickness of the line. + */ +void +GraphicsShape::drawEllipseOutlineByteColor(const double majorAxis, + const double minorAxis, + const uint8_t rgba[4], + const GraphicsPrimitive::LineWidthType lineThicknessType, + const double lineThickness) +{ + std::vector ellipseXYZ; + createEllipseVertices(majorAxis, minorAxis, ellipseXYZ); + + std::unique_ptr primitive(GraphicsPrimitive::newPrimitiveV3f(GraphicsPrimitive::PrimitiveType::POLYGONAL_LINE_LOOP_MITER_JOIN, + rgba)); + const int32_t numVertices = static_cast(ellipseXYZ.size() / 3); + primitive->reserveForNumberOfVertices(numVertices); + for (int32_t i = 0; i < numVertices; i++) { + primitive->addVertex(&ellipseXYZ[i * 3]); + } + + primitive->setLineWidth(lineThicknessType, + lineThickness); + primitive->setUsageTypeAll(GraphicsPrimitive::UsageType::MODIFIED_ONCE_DRAWN_FEW_TIMES); + + GraphicsEngineDataOpenGL::draw(primitive.get()); +} + +/** + * Draw a filled ellipse. + * + * @param majorAxis + * Diameter of the major axis. + * @param minorAxis + * Diameter of the minor axis. + * @param rgba + * Color for drawing. + */ +void +GraphicsShape::drawEllipseFilledByteColor(const double majorAxis, + const double minorAxis, + const uint8_t rgba[4]) +{ + std::vector ellipseXYZ; + createEllipseVertices(majorAxis, minorAxis, ellipseXYZ); + const int32_t numVertices = static_cast(ellipseXYZ.size() / 3); + + + std::unique_ptr primitive(GraphicsPrimitive::newPrimitiveV3f(GraphicsPrimitive::PrimitiveType::OPENGL_TRIANGLE_FAN, + rgba)); + primitive->reserveForNumberOfVertices(numVertices + 1); + const float center[3] = { 0.0f, 0.0f, 0.0f }; + primitive->addVertex(center); + for (int32_t i = 0; i < numVertices; i++) { + primitive->addVertex(&ellipseXYZ[i * 3]); + } + primitive->addVertex(&ellipseXYZ[0]); + + primitive->setUsageTypeAll(GraphicsPrimitive::UsageType::MODIFIED_ONCE_DRAWN_FEW_TIMES); + + GraphicsEngineDataOpenGL::draw(primitive.get()); +} + +/** + * Draw lines: Each pair of vertices is a separate line segment + * similar to OpenGL's GL_LINES + * + * @param xyz + * Coordinates of the line end points. + * @param rgba + * Color for drawing. + * @param lineThicknessType + * Type of line thickness. + * @param lineThickness + * Thickness of the line. + */ +void +GraphicsShape::drawLinesByteColor(const std::vector& xyz, + const uint8_t rgba[4], + const GraphicsPrimitive::LineWidthType lineThicknessType, + const double lineThickness) +{ + std::unique_ptr primitive(GraphicsPrimitive::newPrimitiveV3f(GraphicsPrimitive::PrimitiveType::POLYGONAL_LINES, + rgba)); + const int32_t numVertices = static_cast(xyz.size() / 3); + primitive->reserveForNumberOfVertices(numVertices); + for (int32_t i = 0; i < numVertices; i++) { + primitive->addVertex(&xyz[i * 3]); + } + + primitive->setLineWidth(lineThicknessType, + lineThickness); + primitive->setUsageTypeAll(GraphicsPrimitive::UsageType::MODIFIED_ONCE_DRAWN_FEW_TIMES); + + GraphicsEngineDataOpenGL::draw(primitive.get()); +} + +/** + * Draw line strip similar to OpenGL's GL_LINE_STRIP + * and use Bevel Joins at connected vertices + * + * @param xyz + * Coordinates of the line end points. + * @param rgba + * Color for drawing. + * @param lineThicknessType + * Type of line thickness. + * @param lineThickness + * Thickness of the line. + */ +void +GraphicsShape::drawLineStripBevelJoinByteColor(const std::vector& xyz, + const uint8_t rgba[4], + const GraphicsPrimitive::LineWidthType lineThicknessType, + const double lineThickness) +{ + std::unique_ptr primitive(GraphicsPrimitive::newPrimitiveV3f(GraphicsPrimitive::PrimitiveType::POLYGONAL_LINE_STRIP_BEVEL_JOIN, + rgba)); + const int32_t numVertices = static_cast(xyz.size() / 3); + primitive->reserveForNumberOfVertices(numVertices); + for (int32_t i = 0; i < numVertices; i++) { + primitive->addVertex(&xyz[i * 3]); + } + + primitive->setLineWidth(lineThicknessType, + lineThickness); + primitive->setUsageTypeAll(GraphicsPrimitive::UsageType::MODIFIED_ONCE_DRAWN_FEW_TIMES); + + GraphicsEngineDataOpenGL::draw(primitive.get()); +} + +/** + * Draw line strip similar to OpenGL's GL_LINE_STRIP + * and use Bevel Joins at connected vertices + * + * @param xyz + * Coordinates of the line end points. + * @param rgba + * Color for drawing. + * @param lineThicknessType + * Type of line thickness. + * @param lineThickness + * Thickness of the line. + */ +void +GraphicsShape::drawLineStripMiterJoinByteColor(const std::vector& xyz, + const uint8_t rgba[4], + const GraphicsPrimitive::LineWidthType lineThicknessType, + const double lineThickness) +{ + std::unique_ptr primitive(GraphicsPrimitive::newPrimitiveV3f(GraphicsPrimitive::PrimitiveType::POLYGONAL_LINE_STRIP_MITER_JOIN, + rgba)); + const int32_t numVertices = static_cast(xyz.size() / 3); + primitive->reserveForNumberOfVertices(numVertices); + for (int32_t i = 0; i < numVertices; i++) { + primitive->addVertex(&xyz[i * 3]); + } + + primitive->setLineWidth(lineThicknessType, + lineThickness); + primitive->setUsageTypeAll(GraphicsPrimitive::UsageType::MODIFIED_ONCE_DRAWN_FEW_TIMES); + + GraphicsEngineDataOpenGL::draw(primitive.get()); +} + +/** + * Draw one sphere at the given XYZ coordinate. + * NOTE: if you have multiple spheres to draw + * with the same color and diameter the method + * "drawSpheresByteColor() is much more efficient. + * + * @param xyz + * XYZ-coordinate of sphere + * @param rgba + * Color for drawing. + * @param diameter + * Diameter of the sphere. + */ +void +GraphicsShape::drawSphereByteColor(const float xyz[3], + const uint8_t rgba[4], + const float diameter) +{ + drawSpheresByteColor(xyz, + 1, + rgba, + diameter); + +// const int32_t numLatLonDivisions = 10; +// +// GraphicsPrimitive* spherePrimitive = NULL; +// for (const auto iter : s_byteSpherePrimitives) { +// const auto& key = iter.first; +// if ((key == numLatLonDivisions)) { +// spherePrimitive = iter.second; +// CaretAssert(spherePrimitive); +// break; +// } +// } +// +// if (spherePrimitive == NULL) { +// const bool useStripsFlag = true; +// if (useStripsFlag) { +// spherePrimitive = createSpherePrimitiveTriangleStrips(numLatLonDivisions); +// } +// else { +// spherePrimitive = createSpherePrimitiveTriangles(numLatLonDivisions); +// } +// /* colors may change but not coordinates/normals */ +// spherePrimitive->setUsageTypeAll(GraphicsPrimitive::UsageType::MODIFIED_ONCE_DRAWN_MANY_TIMES); +// spherePrimitive->setUsageTypeColors(GraphicsPrimitive::UsageType::MODIFIED_MANY_DRAWN_MANY_TIMES); +// s_byteSpherePrimitives.insert(std::make_pair(numLatLonDivisions, +// spherePrimitive)); +// } +// +// CaretAssert(spherePrimitive); +// +// spherePrimitive->replaceAllVertexSolidByteRGBA(rgba); +// +// glPushMatrix(); +// glTranslatef(xyz[0], xyz[1], xyz[2]); +// glScalef(diameter, diameter, diameter); +// GraphicsEngineDataOpenGL::draw(spherePrimitive); +// glPopMatrix(); +} + +/** + * Draw a spheres at the given XYZ coordinates + * + * @param xyz + * XYZ-coordinates of spheres (must be allocated for + * "numberOfSpheres" + * @param numberOfSpheres + * Number of spheres + * @param rgba + * Color for drawing. + * @param diameter + * Diameter of the spheres. + */ +void +GraphicsShape::drawSpheresByteColor(const float xyz[], + const int32_t numberOfSpheres, + const uint8_t rgba[4], + const float diameter) +{ + const int32_t numLatLonDivisions = 10; + + GraphicsPrimitive* spherePrimitive = NULL; + for (const auto iter : s_byteSpherePrimitives) { + const auto& key = iter.first; + if ((key == numLatLonDivisions)) { + spherePrimitive = iter.second; + CaretAssert(spherePrimitive); + break; + } + } + + if (spherePrimitive == NULL) { + const bool useStripsFlag = true; + if (useStripsFlag) { + spherePrimitive = createSpherePrimitiveTriangleStrips(numLatLonDivisions); + } + else { + spherePrimitive = createSpherePrimitiveTriangles(numLatLonDivisions); + } + /* colors may change but not coordinates/normals */ + spherePrimitive->setUsageTypeAll(GraphicsPrimitive::UsageType::MODIFIED_ONCE_DRAWN_MANY_TIMES); + spherePrimitive->setUsageTypeColors(GraphicsPrimitive::UsageType::MODIFIED_MANY_DRAWN_MANY_TIMES); + s_byteSpherePrimitives.insert(std::make_pair(numLatLonDivisions, + spherePrimitive)); + } + + CaretAssert(spherePrimitive); + + spherePrimitive->replaceAllVertexSolidByteRGBA(rgba); + + for (int32_t i = 0; i < numberOfSpheres; i++) { + const int32_t i3 = i * 3; + glPushMatrix(); + glTranslatef(xyz[i3], xyz[i3+1], xyz[i3+2]); + glScalef(diameter, diameter, diameter); + GraphicsEngineDataOpenGL::draw(spherePrimitive); + glPopMatrix(); + } +} + + +/** + * Draw a filled circle at the given XYZ coordinate + * + * @param xyz + * XYZ-coordinate of circle + * @param rgba + * Color for drawing. + * @param diameter + * Diameter of the circle. + */ +void +GraphicsShape::drawCircleFilled(const float xyz[3], + const uint8_t rgba[4], + const float diameter) +{ + const int32_t numberOfDivisions = 20; + + GraphicsPrimitive* circlePrimitive = NULL; + for (const auto keyPrim : s_byteCirclePrimitives) { + if (keyPrim.first == numberOfDivisions) { + circlePrimitive = keyPrim.second; + break; + } + } + + if (circlePrimitive == NULL) { + const float radius = 0.5f; + circlePrimitive = createCirclePrimitive(numberOfDivisions, + radius); + circlePrimitive->setUsageTypeAll(GraphicsPrimitive::UsageType::MODIFIED_ONCE_DRAWN_MANY_TIMES); + circlePrimitive->setUsageTypeColors(GraphicsPrimitive::UsageType::MODIFIED_MANY_DRAWN_MANY_TIMES); + s_byteCirclePrimitives.insert(std::make_pair(numberOfDivisions, + circlePrimitive)); + } + + CaretAssert(circlePrimitive); + + circlePrimitive->replaceAllVertexSolidByteRGBA(rgba); + + glPushMatrix(); + if (xyz != NULL) { + glTranslatef(xyz[0], xyz[1], xyz[2]); + } + glScalef(diameter, diameter, 1.0f); + GraphicsEngineDataOpenGL::draw(circlePrimitive); + glPopMatrix(); +} + +/** + * Draw a squares at the given XYZ coordinates + * + * @param xyz + * XYZ-coordinates of squares + * @param numberOfSquares + * Number of squares to draw + * @param rgba + * Color for drawing. + * @param diameter + * Diameter of the squares. + */ +void +GraphicsShape::drawSquares(const float xyz[], + const int32_t numberOfSquares, + const uint8_t rgba[4], + const float diameter) +{ + if (numberOfSquares <= 0) { + return; + } + CaretAssert(xyz); + + if ( ! s_byteSquarePrimitive) { + s_byteSquarePrimitive.reset(GraphicsPrimitive::newPrimitiveV3f(GraphicsPrimitive::PrimitiveType::OPENGL_TRIANGLES, + rgba)); + /* + * The square is made up of four triangles + * with two triangles for the 'front' and + * two triangles for the 'back' so that it is + * never culled. + */ + + const float radius = 0.5f; + + /* counter clockwise triangle */ + s_byteSquarePrimitive->addVertex(-radius, -radius); + s_byteSquarePrimitive->addVertex( radius, -radius); + s_byteSquarePrimitive->addVertex( radius, radius); + + /* counter clockwise triangle */ + s_byteSquarePrimitive->addVertex(-radius, -radius); + s_byteSquarePrimitive->addVertex( radius, radius); + s_byteSquarePrimitive->addVertex(-radius, radius); + + /* clockwise triangle */ + s_byteSquarePrimitive->addVertex(-radius, -radius); + s_byteSquarePrimitive->addVertex(-radius, radius); + s_byteSquarePrimitive->addVertex( radius, radius); + + /* clockwise triangle */ + s_byteSquarePrimitive->addVertex(-radius, -radius); + s_byteSquarePrimitive->addVertex( radius, radius); + s_byteSquarePrimitive->addVertex( radius, -radius); + } + + CaretAssert(s_byteSquarePrimitive.get()); + + s_byteSquarePrimitive->replaceAllVertexSolidByteRGBA(rgba); + + for (int32_t i = 0; i < numberOfSquares; i++) { + const int32_t i3 = i * 3; + glPushMatrix(); + glTranslatef(xyz[i3], xyz[i3+1], xyz[i3+2]); + updateModelMatrixToFaceViewer(); + glScalef(diameter, diameter, 1.0f); + GraphicsEngineDataOpenGL::draw(s_byteSquarePrimitive.get()); + glPopMatrix(); + } +} + +/** + * Draw a square at the given XYZ coordinate + * + * @param xyz + * XYZ-coordinate of square + * @param rgba + * Color for drawing. + * @param diameter + * Diameter of the square. + */ +void +GraphicsShape::drawSquare(const float xyz[3], + const uint8_t rgba[4], + const float diameter) +{ + if ( ! s_byteSquarePrimitive) { + s_byteSquarePrimitive.reset(GraphicsPrimitive::newPrimitiveV3f(GraphicsPrimitive::PrimitiveType::OPENGL_TRIANGLES, + rgba)); + /* + * The square is made up of four triangles + * with two triangles for the 'front' and + * two triangles for the 'back' so that it is + * never culled. + */ + + const float radius = 0.5f; + + /* counter clockwise triangle */ + s_byteSquarePrimitive->addVertex(-radius, -radius); + s_byteSquarePrimitive->addVertex( radius, -radius); + s_byteSquarePrimitive->addVertex( radius, radius); + + /* counter clockwise triangle */ + s_byteSquarePrimitive->addVertex(-radius, -radius); + s_byteSquarePrimitive->addVertex( radius, radius); + s_byteSquarePrimitive->addVertex(-radius, radius); + + /* clockwise triangle */ + s_byteSquarePrimitive->addVertex(-radius, -radius); + s_byteSquarePrimitive->addVertex(-radius, radius); + s_byteSquarePrimitive->addVertex( radius, radius); + + /* clockwise triangle */ + s_byteSquarePrimitive->addVertex(-radius, -radius); + s_byteSquarePrimitive->addVertex( radius, radius); + s_byteSquarePrimitive->addVertex( radius, -radius); + } + + CaretAssert(s_byteSquarePrimitive.get()); + + s_byteSquarePrimitive->replaceAllVertexSolidByteRGBA(rgba); + + glPushMatrix(); + if (xyz != NULL) { + glTranslatef(xyz[0], xyz[1], xyz[2]); + updateModelMatrixToFaceViewer(); + glScalef(diameter, diameter, 1.0f); + } + GraphicsEngineDataOpenGL::draw(s_byteSquarePrimitive.get()); + glPopMatrix(); +} + +/** + * Draw a ring at the given XYZ coordinate + * + * @param xyz + * XYZ-coordinate of ring + * @param rgba + * Color for drawing. + * @param innerRadius + * Inner adius of the ring. + * @param outerRadius + * Outer adius of the ring. + */ +void +GraphicsShape::drawRing(const float xyz[3], + const uint8_t rgba[4], + const double innerRadius, + const double outerRadius) +{ + const int32_t numberOfDivisions = 20; + + GraphicsPrimitive* ringPrimitive = NULL; + for (const auto keyPrim : s_byteRingPrimitives) { + if (keyPrim.first.matches(numberOfDivisions, + innerRadius, + outerRadius)) { + ringPrimitive = keyPrim.second; + break; + } + } + + if (ringPrimitive == NULL) { + RingKey ringKey(numberOfDivisions, innerRadius, outerRadius); + ringPrimitive = createRingPrimitive(ringKey); + ringPrimitive->setUsageTypeAll(GraphicsPrimitive::UsageType::MODIFIED_ONCE_DRAWN_MANY_TIMES); + ringPrimitive->setUsageTypeColors(GraphicsPrimitive::UsageType::MODIFIED_MANY_DRAWN_MANY_TIMES); + s_byteRingPrimitives.insert(std::make_pair(RingKey(numberOfDivisions, innerRadius, outerRadius), + ringPrimitive)); + } + + CaretAssert(ringPrimitive); + + ringPrimitive->replaceAllVertexSolidByteRGBA(rgba); + + glPushMatrix(); + if (xyz != NULL) { + glTranslatef(xyz[0], xyz[1], xyz[2]); + } + GraphicsEngineDataOpenGL::draw(ringPrimitive); + glPopMatrix(); +} + +/** + * Create the primitive for a circle. + * + * @param numberOfDivisions + * Number of divisions + * @param radius + * Radius of the circle. + */ +GraphicsPrimitive* +GraphicsShape::createCirclePrimitive(const int32_t numberOfDivisions, + const double radius) +{ + /* + * Setup step size based upon number of points around circle + */ + // const int32_t numberOfPoints = 8; + const float step = (2.0 * M_PI) / numberOfDivisions; + + const uint8_t rgba[] { 255, 255, 255, 255 }; + GraphicsPrimitiveV3f* primitive = GraphicsPrimitive::newPrimitiveV3f(GraphicsPrimitive::PrimitiveType::OPENGL_TRIANGLE_FAN, + rgba); + primitive->reserveForNumberOfVertices(numberOfDivisions + 2); + /* + * Generate points around ring + */ + const float z = 0.0; + primitive->addVertex(0.0f, 0.0f, z); + for (int32_t i = 0; i <= numberOfDivisions; i++) { + const float t = step * i; + const float x = radius * std::cos(t); + const float y = radius * std::sin(t); + + primitive->addVertex(x, y, z); + } + + return primitive; + +} + + +/** + * Create the vertices for a ring. + * + * @param numberOfDivisions + * Number of divisions + * @param innerRadius + * Inner radius of the ring. + * @param outerRadius + * Outer radius of the ring. + * @return + * Primitive containing vertices of ring. + */ +GraphicsPrimitive* +GraphicsShape::createRingPrimitive(const RingKey& ringKey) +{ + /* + * Setup step size based upon number of points around circle + */ + const float step = (2.0 * M_PI) / ringKey.m_numberOfDivisions; + + const uint8_t rgba[] { 255, 255, 255, 255 }; + GraphicsPrimitiveV3f* primitive = GraphicsPrimitive::newPrimitiveV3f(GraphicsPrimitive::PrimitiveType::OPENGL_TRIANGLE_STRIP, + rgba); + primitive->reserveForNumberOfVertices((ringKey.m_numberOfDivisions + 1) * 2); + /* + * Generate points around ring + */ + const float z = 0.0; + for (int32_t i = 0; i <= ringKey.m_numberOfDivisions; i++) { + const float t = step * i; + + const float xin = ringKey.m_innerRadius * std::cos(t); + const float yin = ringKey.m_innerRadius * std::sin(t); + const float xout = ringKey.m_outerRadius * std::cos(t); + const float yout = ringKey.m_outerRadius * std::sin(t); + + primitive->addVertex(xin, yin, z); + primitive->addVertex(xout, yout, z); + } + + return primitive; +} + +/** + * Create the vertices for the ellipse + * + * @param majorAxis + * Diameter of the major axis. + * @param minorAxis + * Diameter of the minor axis. + * @param elliposeXYZOut + * Contains vertices upon exit. + */ +void +GraphicsShape::createEllipseVertices(const double majorAxis, + const double minorAxis, + std::vector& ellipseXYZOut) +{ + const float a = majorAxis / 2.0f; + const float b = minorAxis / 2.0f; + + const float z = 0.0f; + + const int32_t numSlices = 10; + + const float totalPoints = numSlices * 4; + ellipseXYZOut.resize(totalPoints * 3); + + /* + * Computation of the points on the ellipse is performed + * using the parametric equation (a * cos(t), b*sin(t)). + * Using this parametric equation provides an equal length + * between points on the ellipse. + * The symmetry of the four quadrants is used to that + * we only need to compute points in the first quadrant. + */ + const float angleStep = ((M_PI / 2.0f) / numSlices); + + for (int32_t i = 0; i <= numSlices; i++) { + const float x = a * std::cos(i * angleStep); + const float y = b * std::sin(i * angleStep); + + const int32_t quadOneIndex = i * 3; + CaretAssertVectorIndex(ellipseXYZOut, quadOneIndex + 2); + ellipseXYZOut[quadOneIndex] = x; + ellipseXYZOut[quadOneIndex + 1] = y; + ellipseXYZOut[quadOneIndex + 2] = z; + + if (i < numSlices) { + const int32_t quadTwoIndex = ((numSlices * 2) - i) * 3; + CaretAssertVectorIndex(ellipseXYZOut, quadTwoIndex + 2); + ellipseXYZOut[quadTwoIndex] = -x; + ellipseXYZOut[quadTwoIndex + 1] = y; + ellipseXYZOut[quadTwoIndex + 2] = z; + } + + if (i > 0) { + const int32_t quadThreeIndex = ((numSlices * 2) + i) * 3; + CaretAssertVectorIndex(ellipseXYZOut, quadThreeIndex + 2); + ellipseXYZOut[quadThreeIndex] = -x; + ellipseXYZOut[quadThreeIndex + 1] = -y; + ellipseXYZOut[quadThreeIndex + 2] = z; + } + + if ((i > 0) && (i < numSlices)) { + const int32_t quadFourIndex = ((numSlices * 4) - i) * 3; + CaretAssertVectorIndex(ellipseXYZOut, quadFourIndex + 2); + ellipseXYZOut[quadFourIndex] = x; + ellipseXYZOut[quadFourIndex + 1] = -y; + ellipseXYZOut[quadFourIndex + 2] = z; + } + } +} + +/** + * Create an XYZ coordinate on a sphere. + * + * @param radius + * Radius of the sphere + * @param latDegrees + * Latitude on sphere in degrees + * @param lonDegrees + * Longitude on sphere in degrees + * @param latIndex + * Index of the latitude sections + * @param numLat + * Number of latitude sections + * @param xyzOut + * Output with XYZ coordinate + * @param normalXyzOut + * Output with Normal vector + */ +void +GraphicsShape::createSphereXYZ(const float radius, + const float latDegrees, + const float lonDegrees, + const int32_t latIndex, + const int32_t numLat, + float xyzOut[3], + float normalXyzOut[3]) +{ + constexpr float degToRad = M_PI / 180.0f; + + const float lonRad = lonDegrees * degToRad; + const float latRad = latDegrees * degToRad; + const float z = radius * std::sin(latRad); + + + xyzOut[0] = radius * std::cos(latRad) * std::cos(lonRad); + xyzOut[1] = radius * std::cos(latRad) * std::sin(lonRad); + xyzOut[2] = z; + + if (latIndex == 0) { + xyzOut[0] = 0.0f; + xyzOut[1] = 0.0f; + xyzOut[2] = -radius; + } + else if (latIndex == (numLat - 1)) { + xyzOut[0] = 0.0f; + xyzOut[1] = 0.0f; + xyzOut[2] = radius; + } + + normalXyzOut[0] = xyzOut[0]; + normalXyzOut[1] = xyzOut[1]; + normalXyzOut[2] = xyzOut[2]; + MathFunctions::normalizeVector(normalXyzOut); + +} + +/** + * Create a sphere with radius of 0.5 and with the given number + * of latitude and longitude divisions. + * + * @param numberOfLatLon + * Number of latitude/longitude divisions + * @return + * Graphics primitive containing the sphere. + */ +GraphicsPrimitiveV3fN3f* +GraphicsShape::createSpherePrimitiveTriangles(const int32_t numberOfLatLon) +{ + if (numberOfLatLon < 3) { + CaretAssertMessage(0, "For sphere, must have at least 3 lat/lon divisions"); + CaretLogSevere("For sphere, must have at least 3 lat/lon divisions"); + return NULL; + } + + bool debugFlag = false; + if (debugFlag) { + std::cout << std::endl; + std::cout << "TRIANGLE START" << std::endl; + } + + const float radius = 0.5f; + + const int numLat = numberOfLatLon; + const int numLon = numberOfLatLon; + + uint8_t rgba[4] = { 255, 255, 255, 255 }; + GraphicsPrimitiveV3fN3f* primitive = GraphicsPrimitive::newPrimitiveV3fN3f(GraphicsPrimitive::PrimitiveType::OPENGL_TRIANGLES, + rgba); + + const float dLat = 180.0 / numLat; + for (int iLat = 0; iLat < numLat; iLat++) { + const float latDeg = -90.0f + (iLat * dLat); + + const float latDeg2 = -90.0f + ((iLat + 1) * dLat); + + const float dLon = 360.0f / numLon; + for (int iLon = 0; iLon < numLon; iLon++) { + const float lonDeg = iLon * dLon; + + float xyz1[3]; + float normal1[3]; + createSphereXYZ(radius, latDeg, lonDeg, iLat, numLat, xyz1, normal1); + + float xyz2[3]; + float normal2[3]; + createSphereXYZ(radius, latDeg2, lonDeg, iLat + 1, numLat, xyz2, normal2); + + + float xyz3[3]; + float normal3[3]; + createSphereXYZ(radius, latDeg, (lonDeg + dLon), iLat, numLat, xyz3, normal3); + + float xyz4[3]; + float normal4[3]; + createSphereXYZ(radius, latDeg2, (lonDeg + dLon), iLat + 1, numLat, xyz4, normal4); + + primitive->addVertex(xyz1, normal1); + primitive->addVertex(xyz3, normal3); + primitive->addVertex(xyz2, normal2); + + primitive->addVertex(xyz2, normal2); + primitive->addVertex(xyz3, normal3); + primitive->addVertex(xyz4, normal4); + + if (debugFlag) { + const int32_t indx1 = (primitive->getNumberOfVertices() - 2); + const int32_t indx2 = (primitive->getNumberOfVertices() - 1); + std::cout << " " << iLat << ", " << iLon << std::endl; + std::cout << " " << indx1 << ":" << latDeg << ", " << lonDeg << ", " << AString::fromNumbers(xyz1, 3, ",") << std::endl; + std::cout << " " << indx2 << ":" << latDeg2 << ", " << lonDeg << ", " << AString::fromNumbers(xyz2, 3, ",") << std::endl; + std::cout << " " << AString::fromNumbers(xyz3, 3, ",") << std::endl; + std::cout << " " << AString::fromNumbers(xyz4, 3, ",") << std::endl; + } + } + + if (debugFlag) std::cout << std::endl; + } + + return primitive; +} + +/** + * Create a sphere with radius of 0.5 and with the given number + * of latitude and longitude divisions. + * + * @param numberOfLatLon + * Number of latitude/longitude divisions + * @return + * Graphics primitive containing the sphere. + */ +GraphicsPrimitiveV3fN3f* +GraphicsShape::createSpherePrimitiveTriangleStrips(const int32_t numberOfLatLon) +{ + if (numberOfLatLon < 3) { + CaretAssertMessage(0, "For sphere, must have at least 3 lat/lon divisions"); + CaretLogSevere("For sphere, must have at least 3 lat/lon divisions"); + return NULL; + } + + bool debugFlag = false; + if (debugFlag) { + std::cout << std::endl; + std::cout << "TRIANGLE START" << std::endl; + } + + const float radius = 0.5f; + + const int numLat = numberOfLatLon; + const int numLon = numberOfLatLon; + + uint8_t rgba[4] = { 255, 255, 255, 255 }; + GraphicsPrimitiveV3fN3f* primitive = GraphicsPrimitive::newPrimitiveV3fN3f(GraphicsPrimitive::PrimitiveType::OPENGL_TRIANGLE_STRIP, + rgba); + + const float dLat = 180.0 / numLat; + for (int iLat = 0; iLat < numLat; iLat++) { + const float latDeg = -90.0f + (iLat * dLat); + + const float latDeg2 = -90.0f + ((iLat + 1) * dLat); + + const float dLon = 360.0f / numLon; + for (int iLon = 0; iLon <= numLon; iLon++) { + const float lonDeg = iLon * dLon; + + float xyz1[3]; + float normal1[3]; + createSphereXYZ(radius, latDeg, lonDeg, iLat, numLat, xyz1, normal1); + + float xyz2[3]; + float normal2[3]; + createSphereXYZ(radius, latDeg2, lonDeg, iLat + 1, numLat, xyz2, normal2); + + primitive->addVertex(xyz2, normal2); + primitive->addVertex(xyz1, normal1); + + if (debugFlag) { + const int32_t indx1 = (primitive->getNumberOfVertices() - 2); + const int32_t indx2 = (primitive->getNumberOfVertices() - 1); + std::cout << " " << iLat << ", " << iLon << std::endl; + std::cout << " " << indx1 << ":" << latDeg << ", " << lonDeg << ", " << AString::fromNumbers(xyz1, 3, ",") << std::endl; + std::cout << " " << indx2 << ":" << latDeg2 << ", " << lonDeg << ", " << AString::fromNumbers(xyz2, 3, ",") << std::endl; + } + } + + if (iLat < (numLat - 1)) { + primitive->addPrimitiveRestart(); + } + if (debugFlag) std::cout << std::endl; + } + + return primitive; +} + +/** + * Update the model matrix to face the viewer by applying a + * technique known as "billboarding". Callers need to + * push/pop the modelview matrix before/after calling + * this method. + * + * The result is that the X-Y plane faces the user. + */ +void +GraphicsShape::updateModelMatrixToFaceViewer() +{ + /* + * Get the translation and rotation from the + * current modelview matrix. + */ + double modelMatrixArray[16]; + glGetDoublev(GL_MODELVIEW_MATRIX, modelMatrixArray); + Matrix4x4 matrix; + matrix.setMatrixFromOpenGL(modelMatrixArray); + float txyz[3]; + matrix.getTranslation(txyz); + double sx, sy, sz; + matrix.getScale(sx, sy, sz); + + /* + * Replace modelview matrix with + * just translation and scaling. + */ + glLoadIdentity(); + glTranslatef(txyz[0], txyz[1], txyz[2]); + glScalef(sx, sy, sz); +} + +/** + * Get a description of this object's content. + * @return String describing this object's content. + */ +AString +GraphicsShape::toString() const +{ + return "GraphicsShape"; +} + diff --git a/src/Graphics/GraphicsShape.h b/src/Graphics/GraphicsShape.h new file mode 100644 index 0000000000000000000000000000000000000000..db9faedbeff5fcce1eb0402da6492e890af7c244 --- /dev/null +++ b/src/Graphics/GraphicsShape.h @@ -0,0 +1,219 @@ +#ifndef __GRAPHICS_SHAPE_H__ +#define __GRAPHICS_SHAPE_H__ + +/*LICENSE_START*/ +/* + * Copyright (C) 2017 Washington University School of Medicine + * + * 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 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ +/*LICENSE_END*/ + + +#include +#include + +#include "CaretObject.h" +#include "GraphicsPrimitive.h" + + + +namespace caret { + + class GraphicsPrimitiveV3f; + class GraphicsPrimitiveV3fN3f; + + class GraphicsShape : public CaretObject { + + public: + virtual ~GraphicsShape(); + + static void drawBoxOutlineByteColor(const float v1[3], + const float v2[3], + const float v3[3], + const float v4[3], + const uint8_t rgba[4], + const GraphicsPrimitive::LineWidthType lineThicknessType, + const double lineThickness); + + static void drawBoxOutlineFloatColor(const float v1[3], + const float v2[3], + const float v3[3], + const float v4[3], + const float rgba[4], + const GraphicsPrimitive::LineWidthType lineThicknessType, + const double lineThickness); + + static void drawBoxFilledByteColor(const float v1[3], + const float v2[3], + const float v3[3], + const float v4[3], + const uint8_t rgba[4]); + + static void drawBoxFilledFloatColor(const float v1[3], + const float v2[3], + const float v3[3], + const float v4[3], + const float rgba[4]); + + static void drawEllipseOutlineByteColor(const double majorAxis, + const double minorAxis, + const uint8_t rgba[4], + const GraphicsPrimitive::LineWidthType lineThicknessType, + const double lineThickness); + + static void drawEllipseFilledByteColor(const double majorAxis, + const double minorAxis, + const uint8_t rgba[4]); + + static void drawLinesByteColor(const std::vector& xyz, + const uint8_t rgba[4], + const GraphicsPrimitive::LineWidthType lineThicknessType, + const double lineThickness); + + static void drawLineStripBevelJoinByteColor(const std::vector& xyz, + const uint8_t rgba[4], + const GraphicsPrimitive::LineWidthType lineThicknessType, + const double lineThickness); + + static void drawLineStripMiterJoinByteColor(const std::vector& xyz, + const uint8_t rgba[4], + const GraphicsPrimitive::LineWidthType lineThicknessType, + const double lineThickness); + + static void drawSphereByteColor(const float xyz[3], + const uint8_t rgba[4], + const float diameter); + + static void drawSpheresByteColor(const float xyz[], + const int32_t numberOfSpheres, + const uint8_t rgba[4], + const float diameter); + + static void drawCircleFilled(const float xyz[3], + const uint8_t rgba[4], + const float diameter); + + static void drawSquare(const float xyz[3], + const uint8_t rgba[4], + const float diameter); + + static void drawSquares(const float xyz[], + const int32_t numberOfSquares, + const uint8_t rgba[4], + const float diameter); + + static void drawRing(const float xyz[3], + const uint8_t rgba[4], + const double innerRadius, + const double outerRadius); + + static void deleteAllPrimitives(); + + // ADD_NEW_METHODS_HERE + + virtual AString toString() const; + + private: + class RingKey { + public: + RingKey(const int32_t numberOfDivisions, + const float innerRadius, + const float outerRadius) + : m_numberOfDivisions(numberOfDivisions), + m_innerRadius(innerRadius), + m_outerRadius(outerRadius) { } + + bool matches(const int32_t numberOfDivisions, + const float innerRadius, + const float outerRadius) const { + if ((m_numberOfDivisions == numberOfDivisions) + && (m_innerRadius == innerRadius) + && (m_outerRadius == outerRadius)) { + return true; + } + return false; + } + + bool operator<(const RingKey& rhs) const { + if (m_numberOfDivisions < rhs.m_numberOfDivisions) { + return true; + } + if (m_innerRadius < rhs.m_innerRadius) { + return true; + } + if (m_outerRadius < rhs.m_outerRadius) { + return true; + } + return false; + } + + int32_t m_numberOfDivisions; + float m_innerRadius; + float m_outerRadius; + }; + + GraphicsShape(); + + GraphicsShape(const GraphicsShape&); + + GraphicsShape& operator=(const GraphicsShape&); + + static void updateModelMatrixToFaceViewer(); + + static GraphicsPrimitive* createCirclePrimitive(const int32_t numberOfDivisions, + const double radius); + + static GraphicsPrimitive* createRingPrimitive(const RingKey& ringKey); + + static void createEllipseVertices(const double majorAxis, + const double minorAxis, + std::vector& ellipseXYZOut); + + + static GraphicsPrimitiveV3fN3f* createSpherePrimitiveTriangles(const int32_t numberOfLatLon); + + static GraphicsPrimitiveV3fN3f* createSpherePrimitiveTriangleStrips(const int32_t numberOfLatLon); + + static void createSphereXYZ(const float radius, + const float latDegrees, + const float lonDegrees, + const int32_t latIndex, + const int32_t numLat, + float xyzOut[3], + float normalXyzOut[3]); + + static std::unique_ptr s_byteSquarePrimitive; + + static std::map s_byteSpherePrimitives; + + static std::map s_byteCirclePrimitives; + + + static std::map s_byteRingPrimitives; + + // ADD_NEW_MEMBERS_HERE + + }; + +#ifdef __GRAPHICS_SHAPE_DECLARE__ + std::map GraphicsShape::s_byteSpherePrimitives; + std::map GraphicsShape::s_byteCirclePrimitives; + std::map GraphicsShape::s_byteRingPrimitives; + std::unique_ptr GraphicsShape::s_byteSquarePrimitive; +#endif // __GRAPHICS_SHAPE_DECLARE__ + +} // namespace +#endif //__GRAPHICS_SHAPE_H__ diff --git a/src/Graphics/GraphicsUtilitiesOpenGL.cxx b/src/Graphics/GraphicsUtilitiesOpenGL.cxx new file mode 100644 index 0000000000000000000000000000000000000000..8f1b4936f2410ebcac9c6a19f478dad0f6197532 --- /dev/null +++ b/src/Graphics/GraphicsUtilitiesOpenGL.cxx @@ -0,0 +1,211 @@ + +/*LICENSE_START*/ +/* + * Copyright (C) 2017 Washington University School of Medicine + * + * 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 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ +/*LICENSE_END*/ + +#define __GRAPHICS_UTILITIES_OPEN_G_L_DECLARE__ +#include "GraphicsUtilitiesOpenGL.h" +#undef __GRAPHICS_UTILITIES_OPEN_G_L_DECLARE__ + +#include +#include + +#include "CaretAssert.h" +#include "CaretOpenGLInclude.h" +#include "GraphicsOpenGLError.h" +#include "EventManager.h" +#include "EventOpenGLObjectToWindowTransform.h" +#include "MathFunctions.h" +using namespace caret; + + + +/** + * \class caret::GraphicsUtilitiesOpenGL + * \brief Utility functions for use with OpenGL. + * \ingroup Graphics + */ + +/** + * Constructor. + */ +GraphicsUtilitiesOpenGL::GraphicsUtilitiesOpenGL() +: CaretObject() +{ + +} + +/** + * Destructor. + */ +GraphicsUtilitiesOpenGL::~GraphicsUtilitiesOpenGL() +{ +} + + +/** + * Converts pixels to a percentage of the viewport height. + * + * @param pixels + * The value in pixels. + * @return + The percentage of height [0.0, 100.0] for the given number of millimeters. + */ +float +GraphicsUtilitiesOpenGL::convertPixelsToPercentageOfViewportHeight(const float pixels) +{ + float percentageOfViewportHeight = 1.0f; + + EventOpenGLObjectToWindowTransform xform(EventOpenGLObjectToWindowTransform::SpaceType::VOLUME_SLICE_MODEL); + EventManager::get()->sendEvent(xform.getPointer()); + if (xform.isValid()) { + const std::array viewport = xform.getViewport(); + + const float rangePixels = viewport[3]; + if ((rangePixels > 0) + && (pixels > 0)) { + percentageOfViewportHeight = (pixels / rangePixels) * 100.0f; + } + } + + return percentageOfViewportHeight ; + +} + +/** + * Converts millimeters to a percentage of the viewport height. + * The current transformations must be for drawing in millimeters. + * + * @param millimeters + * The value in millimeters. + * @return + The percentage of height [0.0, 100.0] for the given number of millimeters. + */ +float +GraphicsUtilitiesOpenGL::convertMillimetersToPercentageOfViewportHeight(const float millimeters) +{ + float percentageOfViewportHeight = -1.0f; + + EventOpenGLObjectToWindowTransform xform(EventOpenGLObjectToWindowTransform::SpaceType::VOLUME_SLICE_MODEL); + EventManager::get()->sendEvent(xform.getPointer()); + if (xform.isValid()) { + const std::array viewport = xform.getViewport(); + + const float windowZ = 0.0f; + float bottomWindowXYZ[3] = { (float)viewport[0], (float)viewport[1], (float)windowZ }; + float topWindowXYZ[3] = { (float)viewport[0], (float)(viewport[1] + viewport[3]), (float)windowZ }; + + float bottomModelXYZ[3]; + float topModelXYZ[3]; + + xform.inverseTransformPoint(bottomWindowXYZ, bottomModelXYZ); + xform.inverseTransformPoint(topWindowXYZ, topModelXYZ); + + const float rangeMillimeters = MathFunctions::distance3D(bottomModelXYZ, + topModelXYZ); + const float rangePixels = viewport[3]; + if ((rangePixels > 0) + && (rangeMillimeters > 0)) { + percentageOfViewportHeight = (millimeters / rangeMillimeters) * 100.0f; + } + } + + return percentageOfViewportHeight ; +} + +/** + * Convert pixels to millimeters. + * The current transformations must be for drawing in millimeters. + * + * @param pixels + * The pixels size + * @return + * Millimeters value. + */ +float +GraphicsUtilitiesOpenGL::convertPixelsToMillimeters(const float pixels) +{ + float mm = 1.0f; + + EventOpenGLObjectToWindowTransform xform(EventOpenGLObjectToWindowTransform::SpaceType::VOLUME_SLICE_MODEL); + EventManager::get()->sendEvent(xform.getPointer()); + if (xform.isValid()) { + const std::array viewport = xform.getViewport(); + + const float windowZ = 0.0f; + float bottomWindowXYZ[3] = { (float)viewport[0], (float)viewport[1], (float)windowZ }; + float topWindowXYZ[3] = { (float)viewport[0], (float)(viewport[1] + viewport[3]), (float)windowZ }; + + float bottomModelXYZ[3]; + float topModelXYZ[3]; + + xform.inverseTransformPoint(bottomWindowXYZ, bottomModelXYZ); + xform.inverseTransformPoint(topWindowXYZ, topModelXYZ); + + const float rangeMillimeters = MathFunctions::distance3D(bottomModelXYZ, + topModelXYZ); + const float rangePixels = viewport[3]; + if ((rangePixels > 0) + && (rangeMillimeters > 0)) { + const float ratio = rangeMillimeters / rangePixels; + mm = pixels * ratio; + } + } + + return mm; +} + +/** + * Reset and ignore any OpenGL errors. + */ +void +GraphicsUtilitiesOpenGL::resetOpenGLError() +{ + GLenum errorCode = glGetError(); + while (errorCode != GL_NO_ERROR) { + errorCode = glGetError(); + } +} + +/** + * @return Pointer to OpenGL Error Information. + * If the pointer is not valid, there is no error. + * + * @param message + * Optional message added to the OpenGL error + */ +std::unique_ptr +GraphicsUtilitiesOpenGL::getOpenGLError(const AString& message) +{ + std::unique_ptr errorInfo; + + GLenum errorCode = glGetError(); + while (errorCode != GL_NO_ERROR) { + if ( ! errorInfo) { + errorInfo.reset(new GraphicsOpenGLError(message)); + } + errorInfo->addError(errorCode); + + errorCode = glGetError(); + } + + return errorInfo; +} + + diff --git a/src/Graphics/GraphicsUtilitiesOpenGL.h b/src/Graphics/GraphicsUtilitiesOpenGL.h new file mode 100644 index 0000000000000000000000000000000000000000..fda72fb2eddaebdec0064d486a6c2ba5ad39f954 --- /dev/null +++ b/src/Graphics/GraphicsUtilitiesOpenGL.h @@ -0,0 +1,64 @@ +#ifndef __GRAPHICS_UTILITIES_OPEN_G_L_H__ +#define __GRAPHICS_UTILITIES_OPEN_G_L_H__ + +/*LICENSE_START*/ +/* + * Copyright (C) 2017 Washington University School of Medicine + * + * 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 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ +/*LICENSE_END*/ + + + +#include + +#include "CaretObject.h" + +namespace caret { + + class GraphicsOpenGLError; + + class GraphicsUtilitiesOpenGL : public CaretObject { + + public: + + static float convertMillimetersToPercentageOfViewportHeight(const float millimeters); + + static float convertPixelsToPercentageOfViewportHeight(const float pixels); + + static float convertPixelsToMillimeters(const float pixels); + + static void resetOpenGLError(); + + static std::unique_ptr getOpenGLError(const AString& message = ""); + + private: + GraphicsUtilitiesOpenGL(); + + virtual ~GraphicsUtilitiesOpenGL(); + + GraphicsUtilitiesOpenGL(const GraphicsUtilitiesOpenGL&); + + GraphicsUtilitiesOpenGL& operator=(const GraphicsUtilitiesOpenGL&); + + }; + +#ifdef __GRAPHICS_UTILITIES_OPEN_G_L_DECLARE__ + // +#endif // __GRAPHICS_UTILITIES_OPEN_G_L_DECLARE__ + +} // namespace +#endif //__GRAPHICS_UTILITIES_OPEN_G_L_H__ diff --git a/src/GuiQt/AboutWorkbenchDialog.cxx b/src/GuiQt/AboutWorkbenchDialog.cxx index f8114ecbc1aa4119cdc71cb77d9063d98db3f441..0070f86d3c3390d953c633804e1c5e64e508bd63 100644 --- a/src/GuiQt/AboutWorkbenchDialog.cxx +++ b/src/GuiQt/AboutWorkbenchDialog.cxx @@ -70,7 +70,7 @@ AboutWorkbenchDialog::AboutWorkbenchDialog(BrainOpenGLWidget* openGLParentWidget imageLabel->setAlignment(Qt::AlignCenter); } - QLabel* workbenchLabel = new QLabel(appInfo.getName()); + QLabel* workbenchLabel = new QLabel(appInfo.getNameForGuiLabel()); QFont workbenchFont = workbenchLabel->font(); workbenchFont.setBold(true); workbenchFont.setPointSize(32); diff --git a/src/GuiQt/AnnotationChangeCoordinateDialog.cxx b/src/GuiQt/AnnotationChangeCoordinateDialog.cxx index 991ddc0ce0c1aa4d66f94a3a621936fa295b6ba5..aa6f28b1e813c4cbf2aa1059a3360efe114c8d8a 100644 --- a/src/GuiQt/AnnotationChangeCoordinateDialog.cxx +++ b/src/GuiQt/AnnotationChangeCoordinateDialog.cxx @@ -115,9 +115,9 @@ AnnotationChangeCoordinateDialog::createCurrentCoordinateWidget() bool useXyzFlag = true; switch (m_annotation->getCoordinateSpace()) { - case AnnotationCoordinateSpaceEnum::STEREOTAXIC: + case AnnotationCoordinateSpaceEnum::CHART: break; - case AnnotationCoordinateSpaceEnum::PIXELS: + case AnnotationCoordinateSpaceEnum::STEREOTAXIC: break; case AnnotationCoordinateSpaceEnum::SURFACE: { @@ -137,6 +137,8 @@ AnnotationChangeCoordinateDialog::createCurrentCoordinateWidget() case AnnotationCoordinateSpaceEnum::TAB: spaceText += (AString::number(m_annotation->getTabIndex() + 1) + " "); break; + case AnnotationCoordinateSpaceEnum::VIEWPORT: + break; case AnnotationCoordinateSpaceEnum::WINDOW: spaceText += (AString::number(m_annotation->getWindowIndex() + 1) + " "); break; diff --git a/src/GuiQt/AnnotationColorWidget.cxx b/src/GuiQt/AnnotationColorWidget.cxx index f95c592f30199bcb554de5fe5fb24f05771ad761..fc51bbf823c87e4dda163994c5e0624f10f84df1 100644 --- a/src/GuiQt/AnnotationColorWidget.cxx +++ b/src/GuiQt/AnnotationColorWidget.cxx @@ -76,15 +76,8 @@ m_browserWindowIndex(browserWindowIndex) QLabel* foreLineLabel = new QLabel("Line"); QLabel* foreLineColorLabel = new QLabel("Color"); - QLabel* lineWidthLabel = NULL; - switch (m_parentWidgetType) { - case AnnotationWidgetParentEnum::ANNOTATION_TOOL_BAR_WIDGET: - lineWidthLabel = new QLabel("Width"); - break; - case AnnotationWidgetParentEnum::PARENT_ENUM_FOR_LATER_USE: - CaretAssert(0); - break; - } + QLabel* lineLabel = new QLabel("Line"); + QLabel* lineWidthLabel = new QLabel("Width"); const QSize toolButtonSize(16, 16); @@ -107,14 +100,15 @@ m_browserWindowIndex(browserWindowIndex) m_backgroundToolButton = new QToolButton(); m_backgroundToolButton->setDefaultAction(m_backgroundColorAction); m_backgroundToolButton->setIconSize(toolButtonSize); + WuQtUtilities::setToolButtonStyleForQt5Mac(m_backgroundToolButton); /* * Widget/object group for background widgets */ - m_backgroundWidgetGroup = new WuQWidgetObjectGroup(this); - m_backgroundWidgetGroup->add(backFillLabel); - m_backgroundWidgetGroup->add(backFillColorLabel); - m_backgroundWidgetGroup->add(m_backgroundToolButton); + m_backgroundColorWidgetGroup = new WuQWidgetObjectGroup(this); + m_backgroundColorWidgetGroup->add(backFillLabel); + m_backgroundColorWidgetGroup->add(backFillColorLabel); + m_backgroundColorWidgetGroup->add(m_backgroundToolButton); /* * Line color menu @@ -134,30 +128,38 @@ m_browserWindowIndex(browserWindowIndex) m_lineToolButton = new QToolButton(); m_lineToolButton->setDefaultAction(m_lineColorAction); m_lineToolButton->setIconSize(toolButtonSize); + WuQtUtilities::setToolButtonStyleForQt5Mac(m_lineToolButton); + + m_lineColorWidgetGroup = new WuQWidgetObjectGroup(this); + m_lineColorWidgetGroup->add(foreLineLabel); + m_lineColorWidgetGroup->add(foreLineColorLabel); + m_lineColorWidgetGroup->add(m_lineColorMenu); + m_lineColorWidgetGroup->add(m_lineToolButton); /* * Line thickness */ - float minimumLineWidth = 0.0; - float maximumLineWidth = 1.0; - - m_lineThicknessSpinBox = NULL; - if (lineWidthLabel != NULL) { - BrainOpenGL::getMinMaxLineWidth(minimumLineWidth, - maximumLineWidth); - minimumLineWidth = std::max(minimumLineWidth, 1.0f); - m_lineThicknessSpinBox = WuQFactory::newDoubleSpinBoxWithMinMaxStepDecimalsSignalDouble(minimumLineWidth, - maximumLineWidth, - 1.0, - 0, - this, - SLOT(lineThicknessSpinBoxValueChanged(double))); - WuQtUtilities::setWordWrappedToolTip(m_lineThicknessSpinBox, - "Adjust the line thickness"); - m_lineThicknessSpinBox->setFixedWidth(45); - } - + float minimumLineWidthPercentage = 0.1; + float maximumLineWidthPercentage = 100.0; + float lineWidthStep = 0.1; + m_lineThicknessWidgetGroup = new WuQWidgetObjectGroup(this); + m_lineThicknessSpinBox = WuQFactory::newDoubleSpinBoxWithMinMaxStepDecimalsSignalDouble(minimumLineWidthPercentage, + maximumLineWidthPercentage, + lineWidthStep, + 1, + this, + SLOT(lineThicknessSpinBoxValueChanged(double))); + WuQtUtilities::setWordWrappedToolTip(m_lineThicknessSpinBox, + "Adjust the line thickness as percentage of tab/window height"); + m_lineThicknessSpinBox->setSuffix("%"); + + m_lineThicknessWidgetGroup->add(lineLabel); + m_lineThicknessWidgetGroup->add(lineWidthLabel); + m_lineThicknessWidgetGroup->add(m_lineThicknessSpinBox); + /* + * Layout + */ QGridLayout* gridLayout = new QGridLayout(this); WuQtUtilities::setLayoutSpacingAndMargins(gridLayout, 2, 0); @@ -165,13 +167,15 @@ m_browserWindowIndex(browserWindowIndex) case AnnotationWidgetParentEnum::ANNOTATION_TOOL_BAR_WIDGET: { CaretAssert(lineWidthLabel); - gridLayout->addWidget(foreLineLabel, + gridLayout->addWidget(lineLabel, 0, 0, - 1, 2, Qt::AlignHCenter); gridLayout->addWidget(lineWidthLabel, 1, 0, Qt::AlignHCenter); + gridLayout->addWidget(foreLineLabel, + 0, 1, + Qt::AlignHCenter); gridLayout->addWidget(foreLineColorLabel, 1, 1, Qt::AlignHCenter); @@ -222,15 +226,32 @@ AnnotationColorWidget::~AnnotationColorWidget() void AnnotationColorWidget::updateContent(std::vector& annotations) { - m_annotations = annotations; + m_lineColorAnnotations.clear(); + m_lineColorAnnotations.reserve(annotations.size()); - if ( ! m_annotations.empty()) { - setEnabled(true); - } - else { - setEnabled(false); + m_lineThicknessAnnotations.clear(); + m_lineThicknessAnnotations.reserve(annotations.size()); + + m_backgroundColorAnnotations.clear(); + m_backgroundColorAnnotations.reserve(annotations.size()); + + bool haveAnnotationsFlag = false; + for (auto a : annotations) { + if (a->testProperty(Annotation::Property::FILL_COLOR)) { + m_backgroundColorAnnotations.push_back(a); + } + if (a->testProperty(Annotation::Property::LINE_COLOR)) { + m_lineColorAnnotations.push_back(a); + } + if (a->testProperty(Annotation::Property::LINE_THICKNESS)) { + m_lineThicknessAnnotations.push_back(a); + } + + haveAnnotationsFlag = true; } + setEnabled(haveAnnotationsFlag); + updateBackgroundColorButton(); updateLineColorButton(); updateLineThicknessSpinBox(); @@ -245,9 +266,9 @@ AnnotationColorWidget::updateContent(std::vector& annotations) void AnnotationColorWidget::backgroundColorSelected(const CaretColorEnum::Enum caretColor) { - if (! m_annotations.empty()) { + if (! m_backgroundColorAnnotations.empty()) { float rgba[4]; - m_annotations[0]->getCustomBackgroundColor(rgba); + m_backgroundColorAnnotations[0]->getCustomBackgroundColor(rgba); if (caretColor == CaretColorEnum::CUSTOM) { QColor color; @@ -276,14 +297,14 @@ AnnotationColorWidget::backgroundColorSelected(const CaretColorEnum::Enum caretC if ( ! isBothColorsSetToNoneAllowed(this, caretColor, m_lineColorMenu->getSelectedColor(), - m_annotations)) { + m_backgroundColorAnnotations)) { return; } AnnotationRedoUndoCommand* undoCommand = new AnnotationRedoUndoCommand(); undoCommand->setModeColorBackground(caretColor, rgba, - m_annotations); + m_backgroundColorAnnotations); AnnotationManager* annMan = GuiManager::get()->getBrain()->getAnnotationManager(); AString errorMessage; @@ -309,45 +330,43 @@ AnnotationColorWidget::updateBackgroundColorButton() { CaretColorEnum::Enum colorEnum = CaretColorEnum::NONE; float rgba[4]; - CaretColorEnum::toRGBFloat(colorEnum, rgba); + CaretColorEnum::toRGBAFloat(colorEnum, rgba); rgba[3] = 1.0; - const int32_t numAnnotations = static_cast(m_annotations.size()); + bool enableBackgroundFlag = false; + const int32_t numAnnotations = static_cast(m_backgroundColorAnnotations.size()); if (numAnnotations > 0) { bool firstColorSupportFlag = true; - bool enableBackgroundFlag = false; bool allSameColorFlag = true; for (int32_t i = 0; i < numAnnotations; i++) { - if (m_annotations[0]->isBackgroundColorSupported()) { - if (firstColorSupportFlag) { - m_annotations[i]->getBackgroundColorRGBA(rgba); - firstColorSupportFlag = false; - enableBackgroundFlag = true; - } - else { - float colorRGBA[4]; - m_annotations[i]->getBackgroundColorRGBA(colorRGBA); - for (int32_t iColor = 0; iColor < 4; iColor++) { - if (rgba[iColor] != colorRGBA[iColor]) { - allSameColorFlag = false; - break; - } - } - - if ( ! allSameColorFlag) { + if (firstColorSupportFlag) { + m_backgroundColorAnnotations[i]->getBackgroundColorRGBA(rgba); + firstColorSupportFlag = false; + enableBackgroundFlag = true; + } + else { + float colorRGBA[4]; + m_backgroundColorAnnotations[i]->getBackgroundColorRGBA(colorRGBA); + for (int32_t iColor = 0; iColor < 4; iColor++) { + if (rgba[iColor] != colorRGBA[iColor]) { + allSameColorFlag = false; break; } } + + if ( ! allSameColorFlag) { + break; + } } } if (allSameColorFlag) { - colorEnum = m_annotations[0]->getBackgroundColor(); - m_annotations[0]->getBackgroundColorRGBA(rgba); + colorEnum = m_backgroundColorAnnotations[0]->getBackgroundColor(); + m_backgroundColorAnnotations[0]->getBackgroundColorRGBA(rgba); float customRGBA[4]; - m_annotations[0]->getCustomBackgroundColor(customRGBA); + m_backgroundColorAnnotations[0]->getCustomBackgroundColor(customRGBA); m_backgroundColorMenu->setCustomIconColor(customRGBA); switch (m_parentWidgetType) { @@ -362,12 +381,13 @@ AnnotationColorWidget::updateBackgroundColorButton() } - m_backgroundWidgetGroup->setEnabled(enableBackgroundFlag); - if ( ! enableBackgroundFlag) { - colorEnum = CaretColorEnum::NONE; - } } + m_backgroundColorWidgetGroup->setEnabled(enableBackgroundFlag); + if ( ! enableBackgroundFlag) { + colorEnum = CaretColorEnum::NONE; + } + QPixmap pm = WuQtUtilities::createCaretColorEnumPixmap(m_backgroundToolButton, 24, 24, colorEnum, @@ -388,24 +408,24 @@ AnnotationColorWidget::updateLineColorButton() { CaretColorEnum::Enum colorEnum = CaretColorEnum::NONE; float rgba[4]; - CaretColorEnum::toRGBFloat(colorEnum, rgba); + CaretColorEnum::toRGBAFloat(colorEnum, rgba); rgba[3] = 1.0; - const int32_t numAnnotations = static_cast(m_annotations.size()); + const int32_t numAnnotations = static_cast(m_lineColorAnnotations.size()); + bool enableLineFlag = false; if (numAnnotations > 0) { bool firstColorSupportFlag = true; - bool enableLineFlag = false; bool allSameColorFlag = true; for (int32_t i = 0; i < numAnnotations; i++) { if (firstColorSupportFlag) { - m_annotations[i]->getLineColorRGBA(rgba); + m_lineColorAnnotations[i]->getLineColorRGBA(rgba); firstColorSupportFlag = false; enableLineFlag = true; } else { float colorRGBA[4]; - m_annotations[i]->getLineColorRGBA(colorRGBA); + m_lineColorAnnotations[i]->getLineColorRGBA(colorRGBA); for (int32_t iColor = 0; iColor < 4; iColor++) { if (rgba[iColor] != colorRGBA[iColor]) { allSameColorFlag = false; @@ -420,11 +440,11 @@ AnnotationColorWidget::updateLineColorButton() } if (allSameColorFlag) { - colorEnum = m_annotations[0]->getLineColor(); - m_annotations[0]->getLineColorRGBA(rgba); + colorEnum = m_lineColorAnnotations[0]->getLineColor(); + m_lineColorAnnotations[0]->getLineColorRGBA(rgba); float customRGBA[4]; - m_annotations[0]->getCustomLineColor(customRGBA); + m_lineColorAnnotations[0]->getCustomLineColor(customRGBA); m_lineColorMenu->setCustomIconColor(customRGBA); switch (m_parentWidgetType) { @@ -438,11 +458,12 @@ AnnotationColorWidget::updateLineColorButton() } } - - - if ( ! enableLineFlag) { - colorEnum = CaretColorEnum::NONE; - } + } + + m_lineColorWidgetGroup->setEnabled(enableLineFlag); + + if ( ! enableLineFlag) { + colorEnum = CaretColorEnum::NONE; } QPixmap pm = WuQtUtilities::createCaretColorEnumPixmap(m_lineToolButton, 24, 24, colorEnum, rgba, true); @@ -522,11 +543,11 @@ AnnotationColorWidget::isBothColorsSetToNoneAllowed(QWidget* widget, void AnnotationColorWidget::lineColorSelected(const CaretColorEnum::Enum caretColor) { - if ( ! m_annotations.empty()) { - CaretAssertVectorIndex(m_annotations, 0); + if ( ! m_lineColorAnnotations.empty()) { + CaretAssertVectorIndex(m_lineColorAnnotations, 0); float rgba[4]; - m_annotations[0]->getCustomLineColor(rgba); + m_lineColorAnnotations[0]->getCustomLineColor(rgba); if (caretColor == CaretColorEnum::CUSTOM) { QColor color; @@ -545,14 +566,14 @@ AnnotationColorWidget::lineColorSelected(const CaretColorEnum::Enum caretColor) if ( ! isBothColorsSetToNoneAllowed(this, caretColor, m_backgroundColorMenu->getSelectedColor(), - m_annotations)) { + m_lineColorAnnotations)) { return; } AnnotationRedoUndoCommand* undoCommand = new AnnotationRedoUndoCommand(); undoCommand->setModeColorLine(caretColor, rgba, - m_annotations); + m_lineColorAnnotations); AnnotationManager* annMan = GuiManager::get()->getBrain()->getAnnotationManager(); AString errorMessage; @@ -590,12 +611,12 @@ void AnnotationColorWidget::setUserDefaultLineColor(const CaretColorEnum::Enum caretColor, const float customRGBA[4]) { - if ( ! m_annotations.empty()) { + if ( ! m_lineColorAnnotations.empty()) { bool allTextFlag = true; bool someTextFlag = false; - for (std::vector::iterator iter = m_annotations.begin(); - iter != m_annotations.end(); + for (std::vector::iterator iter = m_lineColorAnnotations.begin(); + iter != m_lineColorAnnotations.end(); iter++) { const Annotation* ann = *iter; if (ann->getType() == AnnotationTypeEnum::TEXT) { @@ -653,7 +674,7 @@ AnnotationColorWidget::lineThicknessSpinBoxValueChanged(double value) AnnotationRedoUndoCommand* undoCommand = new AnnotationRedoUndoCommand(); undoCommand->setModeLineWidth(value, - m_annotations); + m_lineThicknessAnnotations); AnnotationManager* annMan = GuiManager::get()->getBrain()->getAnnotationManager(); AString errorMessage; @@ -664,7 +685,7 @@ AnnotationColorWidget::lineThicknessSpinBoxValueChanged(double value) } EventManager::get()->sendSimpleEvent(EventTypeEnum::EVENT_ANNOTATION_TOOLBAR_UPDATE); - Annotation::setUserDefaultLineWidth(value); + Annotation::setUserDefaultLineWidthPercentage(value); EventManager::get()->sendEvent(EventGraphicsUpdateAllWindows().getPointer()); } @@ -683,29 +704,27 @@ AnnotationColorWidget::updateLineThicknessSpinBox() bool lineWidthValid = false; bool haveMultipleLineWidthValues = false; - const int32_t numAnnotations = static_cast(m_annotations.size()); + const int32_t numAnnotations = static_cast(m_lineThicknessAnnotations.size()); if (numAnnotations > 0) { for (int32_t i = 0; i < numAnnotations; i++) { - if (m_annotations[i]->isLineWidthSupported()) { - const float annLineWidth = m_annotations[i]->getLineWidth(); - if (lineWidthValid) { - if (annLineWidth != lineWidthValue) { - haveMultipleLineWidthValues = true; - } - lineWidthValue = std::min(lineWidthValue, - annLineWidth); - } - else { - lineWidthValue = annLineWidth; - lineWidthValid = true; + const float annLineWidth = m_lineThicknessAnnotations[i]->getLineWidthPercentage(); + if (lineWidthValid) { + if (annLineWidth != lineWidthValue) { + haveMultipleLineWidthValues = true; } + lineWidthValue = std::min(lineWidthValue, + annLineWidth); + } + else { + lineWidthValue = annLineWidth; + lineWidthValid = true; } } if (lineWidthValid) { switch (m_parentWidgetType) { case AnnotationWidgetParentEnum::ANNOTATION_TOOL_BAR_WIDGET: - Annotation::setUserDefaultLineWidth(lineWidthValue); + Annotation::setUserDefaultLineWidthPercentage(lineWidthValue); break; case AnnotationWidgetParentEnum::PARENT_ENUM_FOR_LATER_USE: CaretAssert(0); @@ -723,11 +742,13 @@ AnnotationColorWidget::updateLineThicknessSpinBox() m_lineThicknessSpinBox->setValue(lineWidthValue); m_lineThicknessSpinBox->setEnabled(lineWidthValid); if (haveMultipleLineWidthValues) { - m_lineThicknessSpinBox->setSuffix("+"); + m_lineThicknessSpinBox->setSuffix("%+"); } else { - m_lineThicknessSpinBox->setSuffix(""); + m_lineThicknessSpinBox->setSuffix("%"); } m_lineThicknessSpinBox->blockSignals(false); + + m_lineThicknessWidgetGroup->setEnabled(numAnnotations > 0); } diff --git a/src/GuiQt/AnnotationColorWidget.h b/src/GuiQt/AnnotationColorWidget.h index 0dc814e8498e3f4edd796b9746c4780ff7bfd98c..3d74c96e9411fc2527c7277238cab206f4cb5e03 100644 --- a/src/GuiQt/AnnotationColorWidget.h +++ b/src/GuiQt/AnnotationColorWidget.h @@ -98,9 +98,17 @@ namespace caret { QDoubleSpinBox* m_lineThicknessSpinBox; - WuQWidgetObjectGroup* m_backgroundWidgetGroup; + WuQWidgetObjectGroup* m_backgroundColorWidgetGroup; - std::vector m_annotations; + WuQWidgetObjectGroup* m_lineColorWidgetGroup; + + WuQWidgetObjectGroup* m_lineThicknessWidgetGroup; + + std::vector m_lineColorAnnotations; + + std::vector m_lineThicknessAnnotations; + + std::vector m_backgroundColorAnnotations; // ADD_NEW_MEMBERS_HERE diff --git a/src/GuiQt/AnnotationCoordinateInformation.cxx b/src/GuiQt/AnnotationCoordinateInformation.cxx index 2d1234b71d7aa7df0203c8ebab108b5659b0a1ea..0fa8a6f4afed6b8f53b19e89b80922deae7e91ad 100644 --- a/src/GuiQt/AnnotationCoordinateInformation.cxx +++ b/src/GuiQt/AnnotationCoordinateInformation.cxx @@ -100,6 +100,10 @@ AnnotationCoordinateInformation::reset() { m_windowPixelXYZ[0] = 0.0; m_windowPixelXYZ[1] = 0.0; m_windowPixelXYZ[2] = 0.0; + m_chartXYZ[0] = 0.0; + m_chartXYZ[1] = 0.0; + m_chartXYZ[2] = 0.0; + m_chartXYZValid = false; } bool @@ -108,8 +112,8 @@ AnnotationCoordinateInformation::isCoordinateSpaceValid(const AnnotationCoordina bool validSpaceFlag = false; switch (space) { - case AnnotationCoordinateSpaceEnum::PIXELS: - CaretAssertMessage(0, "Space should never be pixels"); + case AnnotationCoordinateSpaceEnum::CHART: + validSpaceFlag = m_chartXYZValid; break; case AnnotationCoordinateSpaceEnum::STEREOTAXIC: validSpaceFlag = m_modelXYZValid; @@ -120,6 +124,9 @@ AnnotationCoordinateInformation::isCoordinateSpaceValid(const AnnotationCoordina case AnnotationCoordinateSpaceEnum::TAB: validSpaceFlag = (m_tabIndex >= 0); break; + case AnnotationCoordinateSpaceEnum::VIEWPORT: + CaretAssertMessage(0, "Should not create/move viewport annotations"); + break; case AnnotationCoordinateSpaceEnum::WINDOW: validSpaceFlag = (m_windowIndex >= 0); break; @@ -157,8 +164,9 @@ AnnotationCoordinateInformation::getValidCoordinateSpaces(const AnnotationCoordi const AnnotationCoordinateSpaceEnum::Enum space = *spaceIter; switch (space) { - case AnnotationCoordinateSpaceEnum::PIXELS: + case AnnotationCoordinateSpaceEnum::VIEWPORT: break; + case AnnotationCoordinateSpaceEnum::CHART: case AnnotationCoordinateSpaceEnum::STEREOTAXIC: case AnnotationCoordinateSpaceEnum::SURFACE: case AnnotationCoordinateSpaceEnum::TAB: @@ -175,7 +183,13 @@ AnnotationCoordinateInformation::getValidCoordinateSpaces(const AnnotationCoordi addItFlag = coordInfoTwo->isCoordinateSpaceValid(space); switch (space) { - case AnnotationCoordinateSpaceEnum::PIXELS: + case AnnotationCoordinateSpaceEnum::CHART: + /* + * Both coord info's must be in the SAME TAB + */ + if (coordInfoOne->m_tabIndex != coordInfoTwo->m_tabIndex) { + addItFlag = false; + } case AnnotationCoordinateSpaceEnum::STEREOTAXIC: /* * Both coord info's must be in the SAME TAB @@ -199,6 +213,11 @@ AnnotationCoordinateInformation::getValidCoordinateSpaces(const AnnotationCoordi addItFlag = false; } break; + case AnnotationCoordinateSpaceEnum::VIEWPORT: +// if (coordInfoOne->m_windowIndex != coordInfoTwo->m_windowIndex) { +// addItFlag = false; +// } + break; case AnnotationCoordinateSpaceEnum::WINDOW: if (coordInfoOne->m_windowIndex != coordInfoTwo->m_windowIndex) { addItFlag = false; @@ -336,6 +355,37 @@ AnnotationCoordinateInformation::createCoordinateInformationFromXY(BrainOpenGLWi } } + if (tabContent != NULL) { + Matrix4x4 projectionMatrix; + Matrix4x4 modelviewMatrix; + int viewport[4]; + if (viewportContent->getChartDataMatricesAndViewport(projectionMatrix, + modelviewMatrix, + viewport)) { + if ((windowX >= viewport[0]) + && (windowX < (viewport[0] + viewport[2])) + && (windowY >= viewport[1]) + && (windowY < (viewport[1] + viewport[3]))) { + double projectionArray[16]; + projectionMatrix.getMatrixForOpenGL(projectionArray); + double modelviewArray[16]; + modelviewMatrix.getMatrixForOpenGL(modelviewArray); + + double chartX = 0.0; + double chartY = 0.0; + double chartZ = 0.0; + if (gluUnProject(windowX, windowY, 0.0, + modelviewArray, projectionArray, viewport, + &chartX, &chartY, &chartZ) == GL_TRUE) { + coordInfoOut.m_chartXYZ[0] = static_cast(chartX); + coordInfoOut.m_chartXYZ[1] = static_cast(chartY); + coordInfoOut.m_chartXYZ[2] = static_cast(chartZ); + coordInfoOut.m_chartXYZValid = true; + } + } + } + } + int windowViewport[4]; viewportContent->getWindowViewport(windowViewport); coordInfoOut.m_windowPixelXYZ[0] = windowX - windowViewport[0]; @@ -423,6 +473,22 @@ AnnotationCoordinateInformation::setOneDimAnnotationCoordinatesForSpace(Annotati CaretAssert(endCoordinate); switch (coordinateSpace) { + case AnnotationCoordinateSpaceEnum::CHART: + if (coordInfoOne->m_chartXYZValid) { + startCoordinate->setXYZ(coordInfoOne->m_chartXYZ); + annotation->setCoordinateSpace(AnnotationCoordinateSpaceEnum::CHART); + + validCoordinateFlag = true; + + if (coordInfoTwo != NULL) { + if (coordInfoTwo->m_chartXYZValid) { + if (endCoordinate != NULL) { + endCoordinate->setXYZ(coordInfoTwo->m_chartXYZ); + } + } + } + } + break; case AnnotationCoordinateSpaceEnum::STEREOTAXIC: if (coordInfoOne->m_modelXYZValid) { startCoordinate->setXYZ(coordInfoOne->m_modelXYZ); @@ -439,9 +505,6 @@ AnnotationCoordinateInformation::setOneDimAnnotationCoordinatesForSpace(Annotati } } break; - case AnnotationCoordinateSpaceEnum::PIXELS: - CaretAssert(0); - break; case AnnotationCoordinateSpaceEnum::SURFACE: if (coordInfoOne->m_surfaceNodeValid) { const float surfaceOffsetLength = startCoordinate->getSurfaceOffsetLength(); @@ -493,14 +556,19 @@ AnnotationCoordinateInformation::setOneDimAnnotationCoordinatesForSpace(Annotati }; if (xyz[1] > 50.0) { xyz[1] -= 25.0; + endCoordinate->setXYZ(xyz); } else { xyz[1] += 25.0; + endCoordinate->setXYZ(coordInfoOne->m_tabXYZ); + startCoordinate->setXYZ(xyz); } - endCoordinate->setXYZ(xyz); } } break; + case AnnotationCoordinateSpaceEnum::VIEWPORT: + CaretAssert(0); + break; case AnnotationCoordinateSpaceEnum::WINDOW: if (coordInfoOne->m_windowIndex >= 0) { startCoordinate->setXYZ(coordInfoOne->m_windowXYZ); @@ -524,11 +592,13 @@ AnnotationCoordinateInformation::setOneDimAnnotationCoordinatesForSpace(Annotati }; if (xyz[1] > 50.0) { xyz[1] -= 25.0; + endCoordinate->setXYZ(xyz); } else { xyz[1] += 25.0; + endCoordinate->setXYZ(coordInfoOne->m_windowXYZ); + startCoordinate->setXYZ(xyz); } - endCoordinate->setXYZ(xyz); } } break; @@ -569,6 +639,26 @@ AnnotationCoordinateInformation::setTwoDimAnnotationCoordinatesForSpace(Annotati AnnotationCoordinate* coordinate = annotation->getCoordinate(); switch (coordinateSpace) { + case AnnotationCoordinateSpaceEnum::CHART: + if (coordInfoOne->m_chartXYZValid) { + coordinate->setXYZ(coordInfoOne->m_chartXYZ); + annotation->setCoordinateSpace(AnnotationCoordinateSpaceEnum::CHART); + + validCoordinateFlag = true; + + if (optionalCoordInfoTwo != NULL) { + if (optionalCoordInfoTwo->m_chartXYZValid) { + float centerXYZ[3] = { + (float)(coordInfoOne->m_modelXYZ[0] + optionalCoordInfoTwo->m_modelXYZ[0]) / 2.0f, + (float)(coordInfoOne->m_modelXYZ[1] + optionalCoordInfoTwo->m_modelXYZ[1]) / 2.0f, + (float)(coordInfoOne->m_modelXYZ[2] + optionalCoordInfoTwo->m_modelXYZ[2]) / 2.0f + }; + coordinate->setXYZ(centerXYZ); + setWidthHeightWithTabCoordsFlag = true; + } + } + } + break; case AnnotationCoordinateSpaceEnum::STEREOTAXIC: if (coordInfoOne->m_modelXYZValid) { coordinate->setXYZ(coordInfoOne->m_modelXYZ); @@ -579,9 +669,9 @@ AnnotationCoordinateInformation::setTwoDimAnnotationCoordinatesForSpace(Annotati if (optionalCoordInfoTwo != NULL) { if (optionalCoordInfoTwo->m_modelXYZValid) { float centerXYZ[3] = { - (coordInfoOne->m_modelXYZ[0] + optionalCoordInfoTwo->m_modelXYZ[0]) / 2.0, - (coordInfoOne->m_modelXYZ[1] + optionalCoordInfoTwo->m_modelXYZ[1]) / 2.0, - (coordInfoOne->m_modelXYZ[2] + optionalCoordInfoTwo->m_modelXYZ[2]) / 2.0 + (float)(coordInfoOne->m_modelXYZ[0] + optionalCoordInfoTwo->m_modelXYZ[0]) / 2.0f, + (float)(coordInfoOne->m_modelXYZ[1] + optionalCoordInfoTwo->m_modelXYZ[1]) / 2.0f, + (float)(coordInfoOne->m_modelXYZ[2] + optionalCoordInfoTwo->m_modelXYZ[2]) / 2.0f }; coordinate->setXYZ(centerXYZ); setWidthHeightWithTabCoordsFlag = true; @@ -589,9 +679,6 @@ AnnotationCoordinateInformation::setTwoDimAnnotationCoordinatesForSpace(Annotati } } break; - case AnnotationCoordinateSpaceEnum::PIXELS: - CaretAssert(0); - break; case AnnotationCoordinateSpaceEnum::SURFACE: if (coordInfoOne->m_surfaceNodeValid) { coordinate->setSurfaceSpace(coordInfoOne->m_surfaceStructure, @@ -648,9 +735,9 @@ AnnotationCoordinateInformation::setTwoDimAnnotationCoordinatesForSpace(Annotati if (optionalCoordInfoTwo != NULL) { if (optionalCoordInfoTwo->m_tabIndex == coordInfoOne->m_tabIndex) { float centerXYZ[3] = { - (coordInfoOne->m_tabXYZ[0] + optionalCoordInfoTwo->m_tabXYZ[0]) / 2.0, - (coordInfoOne->m_tabXYZ[1] + optionalCoordInfoTwo->m_tabXYZ[1]) / 2.0, - (coordInfoOne->m_tabXYZ[2] + optionalCoordInfoTwo->m_tabXYZ[2]) / 2.0 + (coordInfoOne->m_tabXYZ[0] + optionalCoordInfoTwo->m_tabXYZ[0]) / 2.0f, + (coordInfoOne->m_tabXYZ[1] + optionalCoordInfoTwo->m_tabXYZ[1]) / 2.0f, + (coordInfoOne->m_tabXYZ[2] + optionalCoordInfoTwo->m_tabXYZ[2]) / 2.0f }; coordinate->setXYZ(centerXYZ); setWidthHeightWithTabCoordsFlag = true; @@ -658,6 +745,9 @@ AnnotationCoordinateInformation::setTwoDimAnnotationCoordinatesForSpace(Annotati } } break; + case AnnotationCoordinateSpaceEnum::VIEWPORT: + CaretAssert(0); + break; case AnnotationCoordinateSpaceEnum::WINDOW: if (coordInfoOne->m_windowIndex >= 0) { coordinate->setXYZ(coordInfoOne->m_windowXYZ); @@ -669,9 +759,9 @@ AnnotationCoordinateInformation::setTwoDimAnnotationCoordinatesForSpace(Annotati if (optionalCoordInfoTwo != NULL) { if (optionalCoordInfoTwo->m_windowIndex == coordInfoOne->m_windowIndex) { float centerXYZ[3] = { - (coordInfoOne->m_windowXYZ[0] + optionalCoordInfoTwo->m_windowXYZ[0]) / 2.0, - (coordInfoOne->m_windowXYZ[1] + optionalCoordInfoTwo->m_windowXYZ[1]) / 2.0, - (coordInfoOne->m_windowXYZ[2] + optionalCoordInfoTwo->m_windowXYZ[2]) / 2.0 + (coordInfoOne->m_windowXYZ[0] + optionalCoordInfoTwo->m_windowXYZ[0]) / 2.0f, + (coordInfoOne->m_windowXYZ[1] + optionalCoordInfoTwo->m_windowXYZ[1]) / 2.0f, + (coordInfoOne->m_windowXYZ[2] + optionalCoordInfoTwo->m_windowXYZ[2]) / 2.0f }; coordinate->setXYZ(centerXYZ); setWidthHeightWithWindowCoordsFlag = true; diff --git a/src/GuiQt/AnnotationCoordinateInformation.h b/src/GuiQt/AnnotationCoordinateInformation.h index 669fe32ecc900bf4acf4084636ef30fdd0d81938..b951fa17055d43d65a6145f3f5cd0d1057e8165a 100644 --- a/src/GuiQt/AnnotationCoordinateInformation.h +++ b/src/GuiQt/AnnotationCoordinateInformation.h @@ -92,6 +92,9 @@ namespace caret { AnnotationSurfaceOffsetVectorTypeEnum::Enum m_surfaceNodeVector; bool m_surfaceNodeValid; + + float m_chartXYZ[3]; + bool m_chartXYZValid; private: AnnotationCoordinateInformation(const AnnotationCoordinateInformation&); diff --git a/src/GuiQt/AnnotationCoordinateSelectionWidget.cxx b/src/GuiQt/AnnotationCoordinateSelectionWidget.cxx index da47d05b4e1f9a33232890fd6d8cd7b35bcaf749..589698e4f2268baf3dfb59e423810ceb7a665888 100644 --- a/src/GuiQt/AnnotationCoordinateSelectionWidget.cxx +++ b/src/GuiQt/AnnotationCoordinateSelectionWidget.cxx @@ -83,6 +83,7 @@ m_annotationType(annotationType), m_coordInfo(coordInfo), m_optionalSecondCoordInfo(optionalSecondCoordInfo) { + bool enableChartSpaceFlag = false; bool enableModelSpaceFlag = false; bool enableSurfaceSpaceFlag = false; bool enableTabSpaceFlag = true; @@ -90,24 +91,29 @@ m_optionalSecondCoordInfo(optionalSecondCoordInfo) switch (m_annotationType) { case AnnotationTypeEnum::BOX: + enableChartSpaceFlag = true; enableModelSpaceFlag = true; enableSurfaceSpaceFlag = true; break; case AnnotationTypeEnum::COLOR_BAR: break; case AnnotationTypeEnum::IMAGE: + enableChartSpaceFlag = true; enableModelSpaceFlag = true; enableSurfaceSpaceFlag = true; break; case AnnotationTypeEnum::LINE: + enableChartSpaceFlag = true; enableModelSpaceFlag = true; enableSurfaceSpaceFlag = true; break; case AnnotationTypeEnum::OVAL: + enableChartSpaceFlag = true; enableModelSpaceFlag = true; enableSurfaceSpaceFlag = true; break; case AnnotationTypeEnum::TEXT: + enableChartSpaceFlag = true; enableModelSpaceFlag = true; enableSurfaceSpaceFlag = true; break; @@ -169,6 +175,56 @@ m_optionalSecondCoordInfo(optionalSecondCoordInfo) } } + if (enableChartSpaceFlag) { + if ( ! m_coordInfo.m_chartXYZValid) { + enableChartSpaceFlag = false; + } + if (m_optionalSecondCoordInfo != NULL) { + if ( ! m_optionalSecondCoordInfo->m_chartXYZValid) { + enableChartSpaceFlag = false; + } + /* + * Only allow chart space for two coordinates in the SAME tab. + * If tab space is not enabled, then the two chart coordinates are + * in different tabs. + */ + if ( ! enableTabSpaceFlag) { + enableChartSpaceFlag = false; + } + } + } + + if (enableChartSpaceFlag) { + QRadioButton* rb = createRadioButtonForSpace(AnnotationCoordinateSpaceEnum::CHART); + m_spaceButtonGroup->addButton(rb, + AnnotationCoordinateSpaceEnum::toIntegerCode(AnnotationCoordinateSpaceEnum::CHART)); + + const int rowNum = gridLayout->rowCount(); + gridLayout->addWidget(rb, + rowNum, COLUMN_RADIO_BUTTON); + gridLayout->addWidget(new QLabel(AString::number(m_coordInfo.m_chartXYZ[0], 'f', 1)), + rowNum, COLUMN_COORD_X, + Qt::AlignRight); + gridLayout->addWidget(new QLabel(AString::number(m_coordInfo.m_chartXYZ[1], 'f', 1)), + rowNum, COLUMN_COORD_Y, + Qt::AlignRight); + gridLayout->addWidget(new QLabel(AString::number(m_coordInfo.m_chartXYZ[2], 'f', 1)), + rowNum, COLUMN_COORD_Z, + Qt::AlignRight); + + if (m_optionalSecondCoordInfo != NULL) { + gridLayout->addWidget(new QLabel(AString::number(m_optionalSecondCoordInfo->m_chartXYZ[0], 'f', 1)), + rowNum, COLUMN_COORD_TWO_X, + Qt::AlignRight); + gridLayout->addWidget(new QLabel(AString::number(m_optionalSecondCoordInfo->m_chartXYZ[1], 'f', 1)), + rowNum, COLUMN_COORD_TWO_Y, + Qt::AlignRight); + gridLayout->addWidget(new QLabel(AString::number(m_optionalSecondCoordInfo->m_chartXYZ[2], 'f', 1)), + rowNum, COLUMN_COORD_TWO_Z, + Qt::AlignRight); + } + } + if (enableModelSpaceFlag) { if ( ! m_coordInfo.m_modelXYZValid) { enableModelSpaceFlag = false; @@ -349,12 +405,15 @@ m_optionalSecondCoordInfo(optionalSecondCoordInfo) */ const AnnotationCoordinateSpaceEnum::Enum space = AnnotationCoordinateSpaceEnum::TAB; switch (space) { + case AnnotationCoordinateSpaceEnum::CHART: case AnnotationCoordinateSpaceEnum::STEREOTAXIC: - case AnnotationCoordinateSpaceEnum::PIXELS: case AnnotationCoordinateSpaceEnum::SURFACE: case AnnotationCoordinateSpaceEnum::TAB: case AnnotationCoordinateSpaceEnum::WINDOW: break; + case AnnotationCoordinateSpaceEnum::VIEWPORT: + CaretAssert(0); + break; } setSizePolicy(QSizePolicy::Fixed, @@ -381,7 +440,7 @@ AnnotationCoordinateSelectionWidget::createRadioButtonForSpace(const AnnotationC const QString spaceGuiName = AnnotationCoordinateSpaceEnum::toGuiName(space); const QString spaceEnumName = AnnotationCoordinateSpaceEnum::toName(space); QRadioButton* rb = new QRadioButton(spaceGuiName); - rb->setProperty(s_SPACE_PROPERTY_NAME.toAscii().constData(), + rb->setProperty(s_SPACE_PROPERTY_NAME.toLatin1().constData(), spaceEnumName); return rb; @@ -430,7 +489,7 @@ AnnotationCoordinateSpaceEnum::Enum AnnotationCoordinateSelectionWidget::getSelectedCoordinateSpace(bool& validOut) const { validOut = false; - AnnotationCoordinateSpaceEnum::Enum space = AnnotationCoordinateSpaceEnum::PIXELS; + AnnotationCoordinateSpaceEnum::Enum space = AnnotationCoordinateSpaceEnum::VIEWPORT; QAbstractButton* button = m_spaceButtonGroup->checkedButton(); if (button == NULL) { @@ -491,10 +550,11 @@ AnnotationCoordinateSelectionWidget::changeAnnotationCoordinate(Annotation* anno float oldViewportHeight = 0.0; switch (oldSpace) { - case AnnotationCoordinateSpaceEnum::STEREOTAXIC: + case AnnotationCoordinateSpaceEnum::CHART: oldViewportHeight = m_coordInfo.m_tabHeight; break; - case AnnotationCoordinateSpaceEnum::PIXELS: + case AnnotationCoordinateSpaceEnum::STEREOTAXIC: + oldViewportHeight = m_coordInfo.m_tabHeight; break; case AnnotationCoordinateSpaceEnum::SURFACE: oldViewportHeight = m_coordInfo.m_tabHeight; @@ -502,6 +562,9 @@ AnnotationCoordinateSelectionWidget::changeAnnotationCoordinate(Annotation* anno case AnnotationCoordinateSpaceEnum::TAB: oldViewportHeight = m_coordInfo.m_tabHeight; break; + case AnnotationCoordinateSpaceEnum::VIEWPORT: + CaretAssert(0); + break; case AnnotationCoordinateSpaceEnum::WINDOW: oldViewportHeight = m_coordInfo.m_windowHeight; break; @@ -522,16 +585,19 @@ AnnotationCoordinateSelectionWidget::changeAnnotationCoordinate(Annotation* anno otherCoordinate->getXYZ(otherXyz); switch (oldSpace) { - case AnnotationCoordinateSpaceEnum::STEREOTAXIC: + case AnnotationCoordinateSpaceEnum::CHART: + diffXyzValid = true; break; - case AnnotationCoordinateSpaceEnum::PIXELS: - CaretAssert(0); + case AnnotationCoordinateSpaceEnum::STEREOTAXIC: break; case AnnotationCoordinateSpaceEnum::SURFACE: break; case AnnotationCoordinateSpaceEnum::TAB: diffXyzValid = true; break; + case AnnotationCoordinateSpaceEnum::VIEWPORT: + CaretAssert(0); + break; case AnnotationCoordinateSpaceEnum::WINDOW: diffXyzValid = true; break; @@ -547,6 +613,13 @@ AnnotationCoordinateSelectionWidget::changeAnnotationCoordinate(Annotation* anno float newViewportHeight = 0.0; bool setOtherCoordinateFlag = false; switch (newSpace) { + case AnnotationCoordinateSpaceEnum::CHART: + if (m_coordInfo.m_chartXYZValid) { + coordinate->setXYZ(m_coordInfo.m_chartXYZ); + redoAnnotation->setCoordinateSpace(AnnotationCoordinateSpaceEnum::CHART); + newViewportHeight = m_coordInfo.m_tabHeight; + } + break; case AnnotationCoordinateSpaceEnum::STEREOTAXIC: if (m_coordInfo.m_modelXYZValid) { coordinate->setXYZ(m_coordInfo.m_modelXYZ); @@ -554,9 +627,6 @@ AnnotationCoordinateSelectionWidget::changeAnnotationCoordinate(Annotation* anno newViewportHeight = m_coordInfo.m_tabHeight; } break; - case AnnotationCoordinateSpaceEnum::PIXELS: - CaretAssert(0); - break; case AnnotationCoordinateSpaceEnum::SURFACE: if (m_coordInfo.m_surfaceNodeValid) { coordinate->setSurfaceSpace(m_coordInfo.m_surfaceStructure, @@ -587,6 +657,9 @@ AnnotationCoordinateSelectionWidget::changeAnnotationCoordinate(Annotation* anno newViewportHeight = m_coordInfo.m_tabHeight; } break; + case AnnotationCoordinateSpaceEnum::VIEWPORT: + CaretAssert(0); + break; case AnnotationCoordinateSpaceEnum::WINDOW: if (m_coordInfo.m_windowIndex >= 0) { const int32_t oldWindowIndex = redoAnnotation->getWindowIndex(); @@ -755,12 +828,13 @@ AnnotationCoordinateSelectionWidget::setWidthAndHeightForImage(AnnotationImage* float vpWidth = 0.0; float vpHeight = 0.0; switch (imageAnn->getCoordinateSpace()) { - case AnnotationCoordinateSpaceEnum::STEREOTAXIC: + case AnnotationCoordinateSpaceEnum::CHART: vpWidth = m_coordInfo.m_tabWidth; vpHeight = m_coordInfo.m_tabHeight; break; - case AnnotationCoordinateSpaceEnum::PIXELS: - CaretAssert(0); + case AnnotationCoordinateSpaceEnum::STEREOTAXIC: + vpWidth = m_coordInfo.m_tabWidth; + vpHeight = m_coordInfo.m_tabHeight; break; case AnnotationCoordinateSpaceEnum::SURFACE: vpWidth = m_coordInfo.m_tabWidth; @@ -770,6 +844,9 @@ AnnotationCoordinateSelectionWidget::setWidthAndHeightForImage(AnnotationImage* vpWidth = m_coordInfo.m_tabWidth; vpHeight = m_coordInfo.m_tabHeight; break; + case AnnotationCoordinateSpaceEnum::VIEWPORT: + CaretAssert(0); + break; case AnnotationCoordinateSpaceEnum::WINDOW: vpWidth = m_coordInfo.m_windowWidth; vpHeight = m_coordInfo.m_windowHeight; @@ -824,19 +901,21 @@ AnnotationCoordinateSelectionWidget::updateAnnotationDisplayProperties(const Ann CaretAssert(annotation); switch (annotation->getCoordinateSpace()) { + case AnnotationCoordinateSpaceEnum::CHART: + break; case AnnotationCoordinateSpaceEnum::STEREOTAXIC: if (m_coordInfo.m_tabIndex >= 0) { } break; - case AnnotationCoordinateSpaceEnum::PIXELS: - CaretAssert(0); - break; case AnnotationCoordinateSpaceEnum::SURFACE: if (m_coordInfo.m_tabIndex >= 0) { } break; case AnnotationCoordinateSpaceEnum::TAB: break; + case AnnotationCoordinateSpaceEnum::VIEWPORT: + CaretAssert(0); + break; case AnnotationCoordinateSpaceEnum::WINDOW: dpa->setDisplayWindowAnnotationsInSingleTabViews(annotation->getWindowIndex(), true); diff --git a/src/GuiQt/AnnotationCoordinateSpaceWidget.cxx b/src/GuiQt/AnnotationCoordinateSpaceWidget.cxx index 7c6ab537b9ad538ee3601ccdf59e83bdb1f04aa7..9d801ceded07d1e25799ab5a2abd8742f26fc7f2 100644 --- a/src/GuiQt/AnnotationCoordinateSpaceWidget.cxx +++ b/src/GuiQt/AnnotationCoordinateSpaceWidget.cxx @@ -117,9 +117,9 @@ AnnotationCoordinateSpaceWidget::updateContent(std::vector annotati else { text = AnnotationCoordinateSpaceEnum::toGuiAbbreviatedName(space); switch (space) { - case AnnotationCoordinateSpaceEnum::STEREOTAXIC: + case AnnotationCoordinateSpaceEnum::CHART: break; - case AnnotationCoordinateSpaceEnum::PIXELS: + case AnnotationCoordinateSpaceEnum::STEREOTAXIC: break; case AnnotationCoordinateSpaceEnum::SURFACE: break; @@ -141,6 +141,8 @@ AnnotationCoordinateSpaceWidget::updateContent(std::vector annotati text.append(tabString); } break; + case AnnotationCoordinateSpaceEnum::VIEWPORT: + break; case AnnotationCoordinateSpaceEnum::WINDOW: break; } diff --git a/src/GuiQt/AnnotationCoordinateWidget.cxx b/src/GuiQt/AnnotationCoordinateWidget.cxx index 360c45c3023142e9d115a04db7d68235b6e05056..eddd54c3daaf4448826195a4cf1e9b33c85dce44 100644 --- a/src/GuiQt/AnnotationCoordinateWidget.cxx +++ b/src/GuiQt/AnnotationCoordinateWidget.cxx @@ -23,6 +23,10 @@ #include "AnnotationCoordinateWidget.h" #undef __ANNOTATION_COORDINATE_WIDGET_DECLARE__ +#include +#include +#include + #include #include #include @@ -37,12 +41,19 @@ #include "AnnotationRedoUndoCommand.h" #include "AnnotationTwoDimensionalShape.h" #include "Brain.h" +#include "BrainBrowserWindow.h" +#include "BrowserTabContent.h" #include "CaretAssert.h" +#include "ChartTwoCartesianAxis.h" +#include "ChartTwoOverlaySet.h" #include "EnumComboBoxTemplate.h" +#include "EventBrowserWindowContent.h" #include "EventGraphicsUpdateAllWindows.h" #include "EventManager.h" #include "EventOverlaySettingsEditorDialogRequest.h" #include "GuiManager.h" +#include "MathFunctions.h" +#include "ModelChartTwo.h" #include "StructureEnumComboBox.h" #include "WuQFactory.h" #include "WuQMessageBox.h" @@ -143,6 +154,11 @@ m_browserWindowIndex(browserWindowIndex) " 0.0% => Toward's viewer\n" " 100.0% => Away from viewer\n"); + const float spinBoxMaximumWidth = 80.0f; + m_xCoordSpinBox->setMaximumWidth(spinBoxMaximumWidth); + m_yCoordSpinBox->setMaximumWidth(spinBoxMaximumWidth); + m_zCoordSpinBox->setMaximumWidth(spinBoxMaximumWidth); + m_surfaceOffsetVectorTypeComboBox = new EnumComboBoxTemplate(this); m_surfaceOffsetVectorTypeComboBox->setup(); QObject::connect(m_surfaceOffsetVectorTypeComboBox, SIGNAL(itemActivated()), @@ -160,7 +176,7 @@ m_browserWindowIndex(browserWindowIndex) QToolButton* setCoordinateToolButton = NULL; switch (m_parentWidgetType) { case AnnotationWidgetParentEnum::ANNOTATION_TOOL_BAR_WIDGET: - // disabled as change space cause grouping problems. setCoordinateToolButton = new QToolButton(); + /** disabled as change space cause grouping problems. setCoordinateToolButton = new QToolButton(); */ break; case AnnotationWidgetParentEnum::PARENT_ENUM_FOR_LATER_USE: CaretAssert(0); @@ -175,6 +191,7 @@ m_browserWindowIndex(browserWindowIndex) this, SLOT(setCoordinateActionTriggered())); setCoordinateToolButton->setDefaultAction(setCoordinateAction); + WuQtUtilities::setToolButtonStyleForQt5Mac(setCoordinateToolButton); } m_surfaceWidget = new QWidget(); @@ -255,7 +272,12 @@ AnnotationCoordinateWidget::getCoordinate() void AnnotationCoordinateWidget::updateContent(Annotation* annotation) { - m_annotation = annotation; + m_annotation = NULL; + if (annotation != NULL) { + if (annotation->testProperty(Annotation::Property::COORDINATE)) { + m_annotation = annotation; + } + } m_surfaceStructureComboBox->listOnlyValidStructures(); @@ -264,59 +286,181 @@ AnnotationCoordinateWidget::updateContent(Annotation* annotation) bool surfaceFlag = false; if (coordinate != NULL) { - double xyMin = 0.0; - double xyMax = 100.0; - double zMin = -100.0; - double zMax = 100.0; - double xyzStep = 0.1; - QString suffix("%"); + float xyz[3]; + coordinate->getXYZ(xyz); + + bool viewportSpaceFlag = false; + const double percentageMinimum = 0.0; + const double percentageMaximum = 100.0; + const double zDepthMinimum = 0.0; + const double zDepthMaximum = 100.0; + const double coordinateMinimum = -std::numeric_limits::max(); + const double coordinateMaximum = std::numeric_limits::max(); + double xMin = 0.0; + double xMax = 0.0; + double yMin = 0.0; + double yMax = 0.0; + double zMin = 0.0; + double zMax = 0.0; + double xStep = 0.1; + double yStep = 0.1; + double zStep = 0.1; + QString suffix; + int32_t digitsRightOfDecimalX = 2; + int32_t digitsRightOfDecimalY = 2; + int32_t digitsRightOfDecimalZ = 2; switch (m_annotation->getCoordinateSpace()) { - case AnnotationCoordinateSpaceEnum::STEREOTAXIC: - xyMax = 10000.0; - xyMin = -xyMax; - zMin = xyMin; - zMax = xyMax; - xyzStep = 1.0; - suffix.clear(); + case AnnotationCoordinateSpaceEnum::CHART: + { + xMin = coordinateMinimum; + xMax = coordinateMaximum; + yMin = coordinateMinimum; + yMax = coordinateMaximum; + zMin = coordinateMinimum; + zMax = coordinateMaximum; + digitsRightOfDecimalX = 3; + digitsRightOfDecimalX = 3; + xStep = 1.0; + yStep = 1.0; + + BrainBrowserWindow* bbw = GuiManager::get()->getBrowserWindowByWindowIndex(m_browserWindowIndex); + CaretAssert(bbw); + BrowserTabContent* browserTabContent = bbw->getBrowserTabContent(); + if (browserTabContent != NULL) { + ModelChartTwo* modelChartTwo = browserTabContent->getDisplayedChartTwoModel(); + const int32_t tabIndex = browserTabContent->getTabNumber(); + if (modelChartTwo != NULL) { + ChartTwoOverlaySet* chartOverlaySet = NULL; + switch (modelChartTwo->getSelectedChartTwoDataType(tabIndex)) { + case ChartTwoDataTypeEnum::CHART_DATA_TYPE_INVALID: + break; + case ChartTwoDataTypeEnum::CHART_DATA_TYPE_HISTOGRAM: + chartOverlaySet = modelChartTwo->getChartTwoOverlaySet(tabIndex); + break; + case ChartTwoDataTypeEnum::CHART_DATA_TYPE_LINE_SERIES: + chartOverlaySet = modelChartTwo->getChartTwoOverlaySet(tabIndex); + break; + case ChartTwoDataTypeEnum::CHART_DATA_TYPE_MATRIX: + break; + } + + if (chartOverlaySet != NULL) { + float xAxisMin = std::numeric_limits::max(); + float xAxisMax = -std::numeric_limits::max(); + float yAxisMin = std::numeric_limits::max(); + float yAxisMax = -std::numeric_limits::max(); + + std::vector axes; + chartOverlaySet->getDisplayedChartAxes(axes); + const int32_t numAxes = static_cast(axes.size()); + for (int32_t i = 0; i < numAxes; i++) { + float rangeMin(0), rangeMax(0); + axes[i]->getDataRange(rangeMin, rangeMax); + + if (rangeMax > rangeMin) { + const ChartAxisLocationEnum::Enum axisLocation = axes[i]->getAxisLocation(); + switch (axisLocation) { + case ChartAxisLocationEnum::CHART_AXIS_LOCATION_BOTTOM: + case ChartAxisLocationEnum::CHART_AXIS_LOCATION_TOP: + xAxisMin = std::min(xAxisMin, rangeMin); + xAxisMax = std::max(xAxisMax, rangeMax); + break; + case ChartAxisLocationEnum::CHART_AXIS_LOCATION_LEFT: + case ChartAxisLocationEnum::CHART_AXIS_LOCATION_RIGHT: + yAxisMin = std::min(yAxisMin, rangeMin); + yAxisMax = std::max(yAxisMax, rangeMax); + break; + } + } + } + + if (xAxisMax > xAxisMin) { + const float range(xAxisMax - xAxisMin); + int32_t digits = 6 - static_cast(std::round(std::log10(range))); + digitsRightOfDecimalX = MathFunctions::clamp(digits, 3, 6); + xStep = range * 0.001f; + } + if (yAxisMax > yAxisMin) { + const float range(yAxisMax - yAxisMin); + int32_t digits = 6 - static_cast(std::round(std::log10(range))); + digitsRightOfDecimalY = MathFunctions::clamp(digits, 3, 6); + yStep = range * 0.001f; + } + } + } + } + } break; - case AnnotationCoordinateSpaceEnum::PIXELS: + case AnnotationCoordinateSpaceEnum::STEREOTAXIC: + xMin = coordinateMinimum; + xMax = coordinateMaximum; + yMin = coordinateMinimum; + yMax = coordinateMaximum; + zMin = coordinateMinimum; + zMax = coordinateMaximum; + xStep = 1.0; + yStep = 1.0; break; case AnnotationCoordinateSpaceEnum::SURFACE: surfaceFlag = true; break; case AnnotationCoordinateSpaceEnum::TAB: - zMin = 0.0; + xMin = percentageMinimum; + xMax = percentageMaximum; + yMin = percentageMinimum; + yMax = percentageMaximum; + zMin = zDepthMinimum; + zMax = zDepthMaximum; + suffix = "%"; + break; + case AnnotationCoordinateSpaceEnum::VIEWPORT: + /* + * Cannot move + */ + xMin = xyz[0]; + xMax = xyz[0]; + yMin = xyz[1]; + yMax = xyz[1]; + zMin = xyz[2]; + zMax = xyz[2]; + viewportSpaceFlag = true; break; case AnnotationCoordinateSpaceEnum::WINDOW: - zMin = 0.0; + xMin = percentageMinimum; + xMax = percentageMaximum; + yMin = percentageMinimum; + yMax = percentageMaximum; + zMin = zDepthMinimum; + zMax = zDepthMaximum; + suffix = "%"; break; } - float xyz[3]; - coordinate->getXYZ(xyz); - m_xCoordSpinBox->blockSignals(true); - m_xCoordSpinBox->setRange(xyMin, - xyMax); - m_xCoordSpinBox->setSingleStep(xyzStep); + m_xCoordSpinBox->setRange(xMin, + xMax); + m_xCoordSpinBox->setSingleStep(xStep); m_xCoordSpinBox->setSuffix(suffix); m_xCoordSpinBox->setValue(xyz[0]); + m_xCoordSpinBox->setDecimals(digitsRightOfDecimalX); m_xCoordSpinBox->blockSignals(false); m_yCoordSpinBox->blockSignals(true); - m_yCoordSpinBox->setRange(xyMin, - xyMax); - m_yCoordSpinBox->setSingleStep(xyzStep); + m_yCoordSpinBox->setRange(yMin, + yMax); + m_yCoordSpinBox->setSingleStep(yStep); m_yCoordSpinBox->setSuffix(suffix); m_yCoordSpinBox->setValue(xyz[1]); + m_yCoordSpinBox->setDecimals(digitsRightOfDecimalY); m_yCoordSpinBox->blockSignals(false); m_zCoordSpinBox->blockSignals(true); m_zCoordSpinBox->setRange(zMin, zMax); - m_zCoordSpinBox->setSingleStep(xyzStep); + m_zCoordSpinBox->setSingleStep(zStep); m_zCoordSpinBox->setSuffix(suffix); m_zCoordSpinBox->setValue(xyz[2]); + m_zCoordSpinBox->setDecimals(digitsRightOfDecimalZ); m_zCoordSpinBox->blockSignals(false); if (surfaceFlag) { @@ -342,7 +486,12 @@ AnnotationCoordinateWidget::updateContent(Annotation* annotation) m_surfaceOffsetLengthSpinBox->blockSignals(false); } - setEnabled(true); + if (viewportSpaceFlag) { + setEnabled(false); + } + else { + setEnabled(true); + } } else { setEnabled(false); @@ -363,15 +512,17 @@ AnnotationCoordinateWidget::valueChanged() && (coordinate != NULL)) { bool surfaceFlag = false; switch (m_annotation->getCoordinateSpace()) { - case AnnotationCoordinateSpaceEnum::STEREOTAXIC: + case AnnotationCoordinateSpaceEnum::CHART: break; - case AnnotationCoordinateSpaceEnum::PIXELS: + case AnnotationCoordinateSpaceEnum::STEREOTAXIC: break; case AnnotationCoordinateSpaceEnum::SURFACE: surfaceFlag = true; break; case AnnotationCoordinateSpaceEnum::TAB: break; + case AnnotationCoordinateSpaceEnum::VIEWPORT: + break; case AnnotationCoordinateSpaceEnum::WINDOW: break; } @@ -406,9 +557,9 @@ AnnotationCoordinateWidget::valueChanged() } else { float xyz[3] = { - m_xCoordSpinBox->value(), - m_yCoordSpinBox->value(), - m_zCoordSpinBox->value() + (float)m_xCoordSpinBox->value(), + (float)m_yCoordSpinBox->value(), + (float)m_zCoordSpinBox->value() }; coordinateCopy.setXYZ(xyz); } @@ -454,7 +605,7 @@ AnnotationCoordinateWidget::valueChanged() EventManager::get()->sendEvent(EventOverlaySettingsEditorDialogRequest(EventOverlaySettingsEditorDialogRequest::MODE_UPDATE_ALL, m_browserWindowIndex, NULL, - NULL, + (CaretMappableDataFile*)NULL, -1).getPointer()); } } diff --git a/src/GuiQt/AnnotationCreateDialog.cxx b/src/GuiQt/AnnotationCreateDialog.cxx index e177d84dfef51cd1fa2fc8839461f4d62a710dae..486d81070c0414901aa429f532c385d519af4111 100644 --- a/src/GuiQt/AnnotationCreateDialog.cxx +++ b/src/GuiQt/AnnotationCreateDialog.cxx @@ -211,7 +211,8 @@ AnnotationCreateDialog::newAnnotationFromSpaceTypeAndCoords(const Mode mode, needToLaunchDialogFlag = true; } else { - Annotation* newAnn = createAnnotation(newInfo, annotationSpace); + AString errorMessage; + Annotation* newAnn = createAnnotation(newInfo, annotationSpace, errorMessage); if (newAnn != NULL) { DisplayPropertiesAnnotation* dpa = GuiManager::get()->getBrain()->getDisplayPropertiesAnnotation(); dpa->updateForNewAnnotation(newAnn); @@ -220,8 +221,12 @@ AnnotationCreateDialog::newAnnotationFromSpaceTypeAndCoords(const Mode mode, return newAnn; } + if ( ! errorMessage.isEmpty()) { + WuQMessageBox::errorOk(mouseEvent.getOpenGLWidget(), + errorMessage); + return NULL; + } - delete newAnn; needToLaunchDialogFlag = true; } } @@ -251,11 +256,18 @@ AnnotationCreateDialog::newAnnotationFromSpaceTypeAndCoords(const Mode mode, * Information about the new annotation. * @param annotationSpace * Coordinate space for new annotaiton. + * @param errorMessageOut + * Output with error message. + * @return + * Pointer to new annotation or NULL if creating annotation failed. */ Annotation* AnnotationCreateDialog::createAnnotation(NewAnnotationInfo& newAnnotationInfo, - const AnnotationCoordinateSpaceEnum::Enum annotationSpace) + const AnnotationCoordinateSpaceEnum::Enum annotationSpace, + AString& errorMessageOut) { + errorMessageOut.clear(); + Annotation* newAnnotation = NULL; if (newAnnotationInfo.m_annotationType == AnnotationTypeEnum::TEXT) { newAnnotation = new AnnotationPercentSizeText(AnnotationAttributesDefaultTypeEnum::USER); @@ -265,7 +277,8 @@ AnnotationCreateDialog::createAnnotation(NewAnnotationInfo& newAnnotationInfo, */ bool adjustTextPctSizeFlag = false; switch (annotationSpace) { - case AnnotationCoordinateSpaceEnum::PIXELS: + case AnnotationCoordinateSpaceEnum::CHART: + adjustTextPctSizeFlag = true; break; case AnnotationCoordinateSpaceEnum::STEREOTAXIC: adjustTextPctSizeFlag = true; @@ -275,6 +288,9 @@ AnnotationCreateDialog::createAnnotation(NewAnnotationInfo& newAnnotationInfo, break; case AnnotationCoordinateSpaceEnum::TAB: break; + case AnnotationCoordinateSpaceEnum::VIEWPORT: + CaretAssert(0); + break; case AnnotationCoordinateSpaceEnum::WINDOW: break; } @@ -312,6 +328,49 @@ AnnotationCreateDialog::createAnnotation(NewAnnotationInfo& newAnnotationInfo, if (newAnnotationInfo.m_coordTwoInfoValid) { coordTwo = &newAnnotationInfo.m_coordTwoInfo; } + else { + bool threeDimSpaceFlag = false; + switch (annotationSpace) { + case AnnotationCoordinateSpaceEnum::CHART: + threeDimSpaceFlag = true; + break; + case AnnotationCoordinateSpaceEnum::STEREOTAXIC: + threeDimSpaceFlag = true; + break; + case AnnotationCoordinateSpaceEnum::SURFACE: + threeDimSpaceFlag = true; + break; + case AnnotationCoordinateSpaceEnum::TAB: + break; + case AnnotationCoordinateSpaceEnum::VIEWPORT: + CaretAssert(0); + break; + case AnnotationCoordinateSpaceEnum::WINDOW: + break; + } + if (threeDimSpaceFlag) { + switch (newAnnotationInfo.m_annotationType) { + case AnnotationTypeEnum::BOX: + break; + case AnnotationTypeEnum::COLOR_BAR: + break; + case AnnotationTypeEnum::IMAGE: + break; + case AnnotationTypeEnum::LINE: + delete newAnnotation; + newAnnotation = NULL; + errorMessageOut = ("A line annotation cannot be created from a mouse click in " + + AnnotationCoordinateSpaceEnum::toGuiName(annotationSpace) + + " coordinate space. Hold the mouse down, drag the mouse, and then release the mouse."); + return NULL; + break; + case AnnotationTypeEnum::OVAL: + break; + case AnnotationTypeEnum::TEXT: + break; + } + } + } const bool validFlag = AnnotationCoordinateInformation::setAnnotationCoordinatesForSpace(newAnnotation, annotationSpace, &newAnnotationInfo.m_coordOneInfo, @@ -328,7 +387,8 @@ AnnotationCreateDialog::createAnnotation(NewAnnotationInfo& newAnnotationInfo, finishAnnotationCreation(newAnnotationInfo.m_annotationFile, newAnnotation, - newAnnotationInfo.m_mouseEvent.getBrowserWindowIndex()); + newAnnotationInfo.m_mouseEvent.getBrowserWindowIndex(), + newAnnotationInfo.m_coordOneInfo.m_tabIndex); return newAnnotation; } @@ -648,11 +708,17 @@ AnnotationCreateDialog::okButtonClicked() CaretPointer annotation; annotation.grabNew(NULL); - annotation.grabNew(createAnnotation(m_newAnnotationInfo, space)); + annotation.grabNew(createAnnotation(m_newAnnotationInfo, space, errorMessage)); if (annotation == NULL) { - WuQMessageBox::errorOk(this, - "Failed to create annotation in space: " - + AnnotationCoordinateSpaceEnum::toGuiName(space)); + if (errorMessage.isEmpty()) { + WuQMessageBox::errorOk(this, + "Failed to create annotation in space: " + + AnnotationCoordinateSpaceEnum::toGuiName(space)); + } + else { + WuQMessageBox::errorOk(this, + errorMessage); + } return; } @@ -693,11 +759,14 @@ AnnotationCreateDialog::okButtonClicked() * Annotation that was created. * @param browserWindowIndex * Index of window in which annotation was created. + * @param tabIndex + * Index of tab in which annotation was created. */ void AnnotationCreateDialog::finishAnnotationCreation(AnnotationFile* annotationFile, Annotation* annotation, - const int32_t browswerWindowIndex) + const int32_t browswerWindowIndex, + const int32_t tabIndex) { AnnotationManager* annotationManager = GuiManager::get()->getBrain()->getAnnotationManager(); @@ -720,8 +789,81 @@ AnnotationCreateDialog::finishAnnotationCreation(AnnotationFile* annotationFile, AnnotationManager::SELECTION_MODE_SINGLE, false, annotation); + + /* + * A new chart annotation is displayed only in the tab in which it was created + */ + if (annotation->getCoordinateSpace() == AnnotationCoordinateSpaceEnum::CHART) { + + //annotation->setItemDisplaySelectedInNoDisplayGroups(); + annotation->setItemDisplaySelectedInOneTab(tabIndex); + annotation->setItemDisplaySelected(DisplayGroupEnum::DISPLAY_GROUP_TAB, + tabIndex, + TriStateSelectionStatusEnum::SELECTED); + } + } +///** +// * Constructor for information used to create a new annotation. +// * +// * @param mouseEvent +// * The mouse event. +// * @param selectedSpace +// * The space selected by the user. +// * @param annotationType +// * The annotation type. +// * @param useBothCoordinatesFromMouseFlag +// * Use both coords (X/Y and pressed X/Y) +// * @param annotationFile +// * File to which annotation is added. +// */ +//AnnotationCreateDialog::NewAnnotationInfo::NewAnnotationInfo(const MouseEvent& mouseEvent, +// const AnnotationCoordinateSpaceEnum::Enum selectedSpace, +// const AnnotationTypeEnum::Enum annotationType, +// const bool useBothCoordinatesFromMouseFlag, +// AnnotationFile* annotationFile) +//: m_mouseEvent(mouseEvent), +//m_selectedSpace(selectedSpace), +//m_annotationType(annotationType), +//m_annotationFile(annotationFile) +//{ +// CaretAssert(annotationFile); +// +// m_validSpaces.clear(); +// m_coordOneInfo.reset(); +// m_coordTwoInfo.reset(); +// m_coordTwoInfoValid = false; +// m_percentageWidth = -1; +// m_percentageHeight = -1; +// +// AnnotationCoordinateInformation::createCoordinateInformationFromXY(mouseEvent, +// mouseEvent.getX(), +// mouseEvent.getY(), +// m_coordOneInfo); +// +// if (useBothCoordinatesFromMouseFlag) { +// AnnotationCoordinateInformation::createCoordinateInformationFromXY(mouseEvent, +// mouseEvent.getPressedX(), +// mouseEvent.getPressedY(), +// m_coordTwoInfo); +// +// AnnotationCoordinateInformation::getValidCoordinateSpaces(&m_coordOneInfo, +// &m_coordTwoInfo, +// m_validSpaces); +// +// if (isValid()) { +// m_coordTwoInfoValid = true; +// +// processTwoCoordInfo(); +// } +// } +// else { +// AnnotationCoordinateInformation::getValidCoordinateSpaces(&m_coordOneInfo, +// NULL, +// m_validSpaces); +// } +//} /** * Constructor for information used to create a new annotation. * @@ -755,16 +897,16 @@ m_annotationFile(annotationFile) m_percentageWidth = -1; m_percentageHeight = -1; - AnnotationCoordinateInformation::createCoordinateInformationFromXY(mouseEvent, - mouseEvent.getX(), - mouseEvent.getY(), - m_coordOneInfo); if (useBothCoordinatesFromMouseFlag) { + AnnotationCoordinateInformation::createCoordinateInformationFromXY(mouseEvent, + mouseEvent.getX(), + mouseEvent.getY(), + m_coordTwoInfo); AnnotationCoordinateInformation::createCoordinateInformationFromXY(mouseEvent, mouseEvent.getPressedX(), mouseEvent.getPressedY(), - m_coordTwoInfo); + m_coordOneInfo); AnnotationCoordinateInformation::getValidCoordinateSpaces(&m_coordOneInfo, &m_coordTwoInfo, @@ -777,6 +919,35 @@ m_annotationFile(annotationFile) } } else { + AnnotationCoordinateInformation::createCoordinateInformationFromXY(mouseEvent, + mouseEvent.getX(), + mouseEvent.getY(), + m_coordOneInfo); + + /* + * For one-dimensional annotations (line), we need a second coordinate + */ + bool addSecondCoordFlag = true; + switch (annotationType) { + case AnnotationTypeEnum::BOX: + break; + case AnnotationTypeEnum::COLOR_BAR: + break; + case AnnotationTypeEnum::IMAGE: + break; + case AnnotationTypeEnum::LINE: + addSecondCoordFlag = true; + break; + case AnnotationTypeEnum::OVAL: + break; + case AnnotationTypeEnum::TEXT: + break; + } + + if (addSecondCoordFlag) { + + } + AnnotationCoordinateInformation::getValidCoordinateSpaces(&m_coordOneInfo, NULL, m_validSpaces); @@ -847,8 +1018,7 @@ AnnotationCreateDialog::NewAnnotationInfo::processTwoCoordInfo() float viewportWidth = 0.0; float viewportHeight = 0.0; switch (m_selectedSpace) { - case AnnotationCoordinateSpaceEnum::PIXELS: - break; + case AnnotationCoordinateSpaceEnum::CHART: case AnnotationCoordinateSpaceEnum::STEREOTAXIC: case AnnotationCoordinateSpaceEnum::SURFACE: { @@ -880,6 +1050,9 @@ AnnotationCreateDialog::NewAnnotationInfo::processTwoCoordInfo() viewportHeight = viewport[3]; } break; + case AnnotationCoordinateSpaceEnum::VIEWPORT: + CaretAssert(0); + break; case AnnotationCoordinateSpaceEnum::WINDOW: { int viewport[4]; diff --git a/src/GuiQt/AnnotationCreateDialog.h b/src/GuiQt/AnnotationCreateDialog.h index 76e1d26b7ca60c9ac9ae1d2ca394c8a823c1b715..e6d79952b2efe82ab6a048119620e07215a43f2e 100644 --- a/src/GuiQt/AnnotationCreateDialog.h +++ b/src/GuiQt/AnnotationCreateDialog.h @@ -134,11 +134,13 @@ namespace caret { void invalidateImage(); static Annotation* createAnnotation(NewAnnotationInfo& newAnnotationInfo, - const AnnotationCoordinateSpaceEnum::Enum annotationSpace); + const AnnotationCoordinateSpaceEnum::Enum annotationSpace, + AString& errorMessageOut); static void finishAnnotationCreation(AnnotationFile* annotationFile, Annotation* annotation, - const int32_t browswerWindowIndex); + const int32_t browswerWindowIndex, + const int32_t tabIndex); const Mode m_mode; diff --git a/src/GuiQt/AnnotationDeleteWidget.cxx b/src/GuiQt/AnnotationDeleteWidget.cxx index e82d394101cee97db41fde422445c5f9646d6916..a6dd38cd018df0f7e3d56e2ab44a891b78ce3eb2 100644 --- a/src/GuiQt/AnnotationDeleteWidget.cxx +++ b/src/GuiQt/AnnotationDeleteWidget.cxx @@ -136,6 +136,7 @@ AnnotationDeleteWidget::createDeleteToolButton() toolButton->setIconSize(pixmap.size()); toolButton->setDefaultAction(m_deleteToolButtonAction); + WuQtUtilities::setToolButtonStyleForQt5Mac(toolButton); return toolButton; } @@ -148,9 +149,16 @@ AnnotationDeleteWidget::deleteActionTriggered() { AnnotationManager* annotationManager = GuiManager::get()->getBrain()->getAnnotationManager(); std::vector selectedAnnotations = annotationManager->getAnnotationsSelectedForEditing(m_browserWindowIndex); - if ( ! selectedAnnotations.empty()) { + std::vector deleteAnnotations; + for (auto a : selectedAnnotations) { + if (a->testProperty(Annotation::Property::DELETION)) { + deleteAnnotations.push_back(a); + } + } + + if ( ! deleteAnnotations.empty()) { AnnotationRedoUndoCommand* undoCommand = new AnnotationRedoUndoCommand(); - undoCommand->setModeDeleteAnnotations(selectedAnnotations); + undoCommand->setModeDeleteAnnotations(deleteAnnotations); AString errorMessage; if ( ! annotationManager->applyCommand(undoCommand, diff --git a/src/GuiQt/AnnotationFontWidget.cxx b/src/GuiQt/AnnotationFontWidget.cxx index e07e3f585ba0f4282dd6832d74d11563d9deb946..14509e044330a2d53ae7d3a01fe534671bb06d72 100644 --- a/src/GuiQt/AnnotationFontWidget.cxx +++ b/src/GuiQt/AnnotationFontWidget.cxx @@ -42,11 +42,12 @@ #include "CaretAssert.h" #include "CaretColorEnumMenu.h" #include "EnumComboBoxTemplate.h" +#include "EventBrowserWindowGraphicsRedrawn.h" #include "EventGraphicsUpdateAllWindows.h" #include "EventManager.h" #include "GuiManager.h" #include "ModelSurfaceMontage.h" -#include "WuQSpecialIncrementDoubleSpinBox.h" +#include "WuQDoubleSpinBox.h" #include "WuQMessageBox.h" #include "WuQtUtilities.h" @@ -60,19 +61,6 @@ using namespace caret; * \ingroup GuiQt */ -/** - * Processes increment and decrements for double spin box. - */ -class FontSizeFunctionObject : public WuQSpecialIncrementDoubleSpinBox::StepFunctionObject { -public: - double getNewValue(const double currentValue, - const int steps) const { - const double stepAmount = 0.1; - const double outputValue = currentValue + (stepAmount * steps); - return outputValue; - } -}; - /** * Constructor. * @@ -106,16 +94,37 @@ m_browserWindowIndex(browserWindowIndex) /* * Combo box for font size */ - m_fontSizeSpinBox = new WuQSpecialIncrementDoubleSpinBox(new FontSizeFunctionObject); - m_fontSizeSpinBox->setRange(0.0, 1000.0); - m_fontSizeSpinBox->setDecimals(1); - m_fontSizeSpinBox->setSingleStep(0.1); - m_fontSizeSpinBox->setSuffix("%"); + const AString fontSizeToolTop("" + "Adjusts font height (size), as a percentage of the viewport height, that is " + "converted to a pixel height when the text is drawn. " + "

" + "The numeric value in this control will be RED " + "when the pixel height is estimated to be too small and some or all " + "characters may not be drawn. " + "Reducing the height of the text and/or the height of the window may cause " + "too small text. " + ""); + m_fontSizeSpinBox = new WuQDoubleSpinBox(this); + m_fontSizeSpinBox->setRangePercentage(0.0, 100.0); QObject::connect(m_fontSizeSpinBox, SIGNAL(valueChanged(double)), this, SLOT(fontSizeChanged())); - WuQtUtilities::setToolTipAndStatusTip(m_fontSizeSpinBox, - "Change font size (height) as percentage, zero to one-hundred, of viewport height"); + WuQtUtilities::setToolTipAndStatusTip(m_fontSizeSpinBox->getWidget(), + fontSizeToolTop); + /* + * Default palette for size spin box + */ + m_fontSizeSpinBoxDefaultPalette = m_fontSizeSpinBox->getWidget()->palette(); + + /* + * Palette for spin box that colors text in red when the font height is "too small" + */ + m_fontSizeSpinBoxRedTextPalette = m_fontSizeSpinBoxDefaultPalette; + QBrush brush = m_fontSizeSpinBoxRedTextPalette.brush(QPalette::Active, QPalette::Text); + brush.setColor(Qt::red); + m_fontSizeSpinBoxRedTextPalette.setBrush(QPalette::Active, QPalette::Text, brush); + m_fontSizeSpinBoxRedTextPalette.setBrush(QPalette::Active, QPalette::WindowText, brush); + m_fontSizeSpinBoxRedTextPalette.setBrush(QPalette::Active, QPalette::HighlightedText, brush); /* * Text color menu @@ -136,6 +145,7 @@ m_browserWindowIndex(browserWindowIndex) m_textColorToolButton = new QToolButton(); m_textColorToolButton->setDefaultAction(m_textColorAction); m_textColorToolButton->setIconSize(toolButtonSize); + WuQtUtilities::setToolButtonStyleForQt5Mac(m_textColorToolButton); QToolButton* boldFontToolButton = NULL; QToolButton* italicFontToolButton = NULL; @@ -149,12 +159,13 @@ m_browserWindowIndex(browserWindowIndex) /* * Bold Font */ - m_boldFontAction = WuQtUtilities::createAction("B", //boldFontText.toStringWithHtmlBody(), + m_boldFontAction = WuQtUtilities::createAction("B", "Enable/disable bold styling", this, this, SLOT(fontBoldChanged())); m_boldFontAction->setCheckable(true); boldFontToolButton = new QToolButton(); boldFontToolButton->setDefaultAction(m_boldFontAction); + WuQtUtilities::setToolButtonStyleForQt5Mac(boldFontToolButton); /* * Change the bold toolbutton's font to bold. @@ -171,6 +182,7 @@ m_browserWindowIndex(browserWindowIndex) m_italicFontAction->setCheckable(true); italicFontToolButton = new QToolButton(); italicFontToolButton->setDefaultAction(m_italicFontAction); + WuQtUtilities::setToolButtonStyleForQt5Mac(italicFontToolButton); /* * Change the italic toolbutton's font to italic. @@ -187,6 +199,7 @@ m_browserWindowIndex(browserWindowIndex) m_underlineFontAction->setCheckable(true); underlineFontToolButton = new QToolButton(); underlineFontToolButton->setDefaultAction(m_underlineFontAction); + WuQtUtilities::setToolButtonStyleForQt5Mac(underlineFontToolButton); /* * Change the underline toolbutton's font to underline. @@ -213,7 +226,6 @@ m_browserWindowIndex(browserWindowIndex) QHBoxLayout* stylesLayout = new QHBoxLayout(); WuQtUtilities::setLayoutSpacingAndMargins(stylesLayout, 0, 0); - //stylesLayout->addStretch(); stylesLayout->addWidget(boldFontToolButton); stylesLayout->addWidget(italicFontToolButton); stylesLayout->addWidget(underlineFontToolButton); @@ -229,7 +241,7 @@ m_browserWindowIndex(browserWindowIndex) fontNameSizeLayout->addWidget(m_fontNameComboBox->getWidget(), 0, 1, 1, 3); fontNameSizeLayout->addWidget(sizeLabel, 1, 0); - fontNameSizeLayout->addWidget(m_fontSizeSpinBox, + fontNameSizeLayout->addWidget(m_fontSizeSpinBox->getWidget(), 1, 1); fontNameSizeLayout->addWidget(styleLabel, 2, 0); fontNameSizeLayout->addLayout(stylesLayout, 2, 1); @@ -245,6 +257,8 @@ m_browserWindowIndex(browserWindowIndex) setSizePolicy(QSizePolicy::Fixed, QSizePolicy::Fixed); + + EventManager::get()->addEventListener(this, EventTypeEnum::EVENT_BROWSER_WINDOW_GRAPHICS_HAVE_BEEN_REDRAWN); } /** @@ -252,6 +266,28 @@ m_browserWindowIndex(browserWindowIndex) */ AnnotationFontWidget::~AnnotationFontWidget() { + EventManager::get()->removeAllEventsFromListener(this); +} + +/** + * Receive an event. + * + * @param event + * The event that the receive can respond to. + */ +void +AnnotationFontWidget::receiveEvent(Event* event) +{ + if (event->getEventType() == EventTypeEnum::EVENT_BROWSER_WINDOW_GRAPHICS_HAVE_BEEN_REDRAWN) { + EventBrowserWindowGraphicsRedrawn* redrawEvent = dynamic_cast(event); + CaretAssert(redrawEvent); + + if (m_browserWindowIndex == redrawEvent->getBrowserWindowIndex()) { + if (isVisible()) { + updateFontSizeControls(); + } + } + } } /** @@ -263,38 +299,99 @@ AnnotationFontWidget::~AnnotationFontWidget() void AnnotationFontWidget::updateContent(std::vector& annotations) { - m_annotationsFontStyle = annotations; - m_annotations.clear(); + m_annotationsFontColor.clear(); + m_annotationsFontColor.reserve(annotations.size()); + m_annotationsFontName.clear(); + m_annotationsFontName.reserve(annotations.size()); + m_annotationsFontSize.clear(); + m_annotationsFontSize.reserve(annotations.size()); + m_annotationsFontStyle.clear(); + m_annotationsFontStyle.reserve(annotations.size()); + + for (auto a : annotations) { + Annotation* ann = dynamic_cast(a); + if (ann->testProperty(Annotation::Property::TEXT_FONT_NAME)) { + m_annotationsFontName.push_back(a); + } + if (ann->testProperty(Annotation::Property::TEXT_FONT_SIZE)) { + m_annotationsFontSize.push_back(a); + } + if (ann->testProperty(Annotation::Property::TEXT_FONT_STYLE)) { + m_annotationsFontStyle.push_back(a); + } + if (ann->testProperty(Annotation::Property::TEXT_COLOR)) { + m_annotationsFontColor.push_back(a); + } + } - if ( ! m_annotationsFontStyle.empty()) { - bool boldOnFlag = true; - bool italicOnFlag = true; - bool underlineOnFlag = true; - int32_t stylesEnabledCount = 0; - + updateFontNameControls(); + updateFontSizeControls(); + updateFontStyleControls(); + updateTextColorButton(); + + setEnabled((m_annotationsFontColor.size() + + m_annotationsFontName.size() + + m_annotationsFontSize.size() + + m_annotationsFontStyle.size()) > 0); +} + +/** + * Update the font name controls. + */ +void +AnnotationFontWidget::updateFontNameControls() +{ + if ( ! m_annotationsFontName.empty()) { AnnotationTextFontNameEnum::Enum fontName = AnnotationTextFontNameEnum::VERA; - bool fontNameValid = true; + //bool fontNameValid = true; + + const int32_t numAnn = static_cast(m_annotationsFontName.size()); + for (int32_t i = 0; i < numAnn; i++) { + CaretAssertVectorIndex(m_annotationsFontName, i); + AnnotationFontAttributesInterface* annText = m_annotationsFontName[i]; + CaretAssert(annText); + + if (i == 0) { + fontName = annText->getFont(); + } + else { + if (annText->getFont() != fontName) { + //fontNameValid = false; + } + } + } + + m_fontNameComboBox->setSelectedItem(fontName); + + AnnotationText::setUserDefaultFont(fontName); + } +} + +/** + * Update the font size controls. + */ +void +AnnotationFontWidget::updateFontSizeControls() +{ + if ( ! m_annotationsFontSize.empty()) { float fontSizeValue = 5.0; bool haveMultipleFontSizeValues = false; const float surfaceMontageRowCount = getSurfaceMontageRowCount(); - const int32_t numAnn = static_cast(m_annotationsFontStyle.size()); + bool tooSmallFlag = false; + const int32_t numAnn = static_cast(m_annotationsFontSize.size()); for (int32_t i = 0; i < numAnn; i++) { - CaretAssertVectorIndex(m_annotationsFontStyle, i); - AnnotationFontAttributesInterface* annText = m_annotationsFontStyle[i]; + CaretAssertVectorIndex(m_annotationsFontSize, i); + AnnotationFontAttributesInterface* annText = m_annotationsFontSize[i]; CaretAssert(annText); - Annotation* annotation = dynamic_cast(annText); - CaretAssert(annotation); - m_annotations.push_back(annotation); - float sizeValue = annText->getFontPercentViewportSize(); Annotation* ann = dynamic_cast(annText); CaretAssert(ann); switch (ann->getCoordinateSpace()) { - case AnnotationCoordinateSpaceEnum::PIXELS: + case AnnotationCoordinateSpaceEnum::CHART: break; case AnnotationCoordinateSpaceEnum::STEREOTAXIC: sizeValue /= surfaceMontageRowCount; @@ -304,18 +401,16 @@ AnnotationFontWidget::updateContent(std::vectorgetFont(); fontSizeValue = sizeValue; } else { - if (annText->getFont() != fontName) { - fontNameValid = false; - } if (fontSizeValue != sizeValue) { haveMultipleFontSizeValues = true; fontSizeValue = std::min(fontSizeValue, @@ -323,7 +418,46 @@ AnnotationFontWidget::updateContent(std::vectorisStylesSupported()) { + if (annText->isFontTooSmallWhenLastDrawn()) { + tooSmallFlag = true; + } +// const AnnotationText* textAnnotation = dynamic_cast(annText); +// if (textAnnotation != NULL) { +// if (textAnnotation->isFontTooSmallWhenLastDrawn()) { +// tooSmallFlag = true; +// } +// } + } + + updateFontSizeSpinBox(fontSizeValue, + haveMultipleFontSizeValues, + tooSmallFlag); + + AnnotationText::setUserDefaultFontPercentViewportSize(fontSizeValue); + } +} + +/** + * Update the font style controls. + */ +void +AnnotationFontWidget::updateFontStyleControls() +{ + if ( ! m_annotationsFontStyle.empty()) { + bool boldOnFlag = true; + bool italicOnFlag = true; + bool underlineOnFlag = true; + int32_t stylesEnabledCount = 0; + + const int32_t numAnn = static_cast(m_annotationsFontStyle.size()); + for (int32_t i = 0; i < numAnn; i++) { + CaretAssertVectorIndex(m_annotationsFontStyle, i); + AnnotationFontAttributesInterface* annText = m_annotationsFontStyle[i]; + CaretAssert(annText); + + Annotation* annotation = dynamic_cast(annText); + CaretAssert(annotation); + if (annotation->testProperty(Annotation::Property::TEXT_FONT_STYLE)) { if ( ! annText->isBoldStyleEnabled()) { boldOnFlag = false; } @@ -338,11 +472,6 @@ AnnotationFontWidget::updateContent(std::vectorsetSelectedItem(fontName); - - updateFontSizeSpinBox(fontSizeValue, - haveMultipleFontSizeValues); - /* * Font styles are ON only if all selected * text annotations have the style enabled @@ -351,29 +480,22 @@ AnnotationFontWidget::updateContent(std::vectorsetEnabled(stylesEnabledFlag); m_boldFontAction->setChecked(boldOnFlag && stylesEnabledFlag); - + m_italicFontAction->setEnabled(stylesEnabledFlag); m_italicFontAction->setChecked(italicOnFlag && stylesEnabledFlag); m_underlineFontAction->setEnabled(stylesEnabledFlag); m_underlineFontAction->setChecked(underlineOnFlag && stylesEnabledFlag); - AnnotationText::setUserDefaultFont(fontName); - AnnotationText::setUserDefaultFontPercentViewportSize(fontSizeValue); if (stylesEnabledFlag) { AnnotationText::setUserDefaultBoldEnabled(boldOnFlag); AnnotationText::setUserDefaultItalicEnabled(italicOnFlag); AnnotationText::setUserDefaultUnderlineEnabled(underlineOnFlag); } } - - CaretAssert(m_annotations.size() == m_annotationsFontStyle.size()); - - updateTextColorButton(); - - setEnabled( ! m_annotations.empty()); } + /** * Update the font size spin box. * @@ -382,19 +504,39 @@ AnnotationFontWidget::updateContent(std::vectorblockSignals(true); + QSignalBlocker blocker(m_fontSizeSpinBox->getWidget()); m_fontSizeSpinBox->setValue(value); - m_fontSizeSpinBox->blockSignals(false); + + if (tooSmallFontFlag) { + m_fontSizeSpinBox->getWidget()->setPalette(m_fontSizeSpinBoxRedTextPalette); + } + else { + m_fontSizeSpinBox->getWidget()->setPalette(m_fontSizeSpinBoxDefaultPalette); + } + QString fontSizeSuffix("%"); if (haveMultipleValuesFlag) { fontSizeSuffix = "%+"; } + m_fontSizeSpinBox->setValue(value); m_fontSizeSpinBox->setSuffix(fontSizeSuffix); + +// if (tooSmallFontFlag) { +// m_fontSizeSpinBox->getWidget()->setPalette(m_fontSizeSpinBoxRedTextPalette); +// } +// else { +// m_fontSizeSpinBox->getWidget()->setPalette(m_fontSizeSpinBoxDefaultPalette); +// } +// m_fontSizeSpinBox->setValue(value); } /** @@ -406,9 +548,9 @@ AnnotationFontWidget::updateFontSizeSpinBox(const float value, void AnnotationFontWidget::textColorSelected(const CaretColorEnum::Enum caretColor) { - if ( ! m_annotationsFontStyle.empty()) { + if ( ! m_annotationsFontColor.empty()) { float rgba[4]; - m_annotationsFontStyle[0]->getTextColorRGBA(rgba); + m_annotationsFontColor[0]->getTextColorRGBA(rgba); if (caretColor == CaretColorEnum::CUSTOM) { QColor color; @@ -441,7 +583,7 @@ AnnotationFontWidget::textColorSelected(const CaretColorEnum::Enum caretColor) AnnotationRedoUndoCommand* undoCommand = new AnnotationRedoUndoCommand(); undoCommand->setModeTextColor(caretColor, rgba, - m_annotations); + convertToAnnotations(m_annotationsFontColor)); AnnotationManager* annMan = GuiManager::get()->getBrain()->getAnnotationManager(); AString errorMessage; if ( ! annMan->applyCommand(undoCommand, @@ -480,7 +622,7 @@ AnnotationFontWidget::updateTextColorButton() { CaretColorEnum::Enum colorEnum = CaretColorEnum::NONE; float rgba[4]; - CaretColorEnum::toRGBFloat(colorEnum, rgba); + CaretColorEnum::toRGBAFloat(colorEnum, rgba); rgba[3] = 1.0; bool colorButtonValidFlag = false; @@ -488,20 +630,20 @@ AnnotationFontWidget::updateTextColorButton() switch (m_parentWidgetType) { case AnnotationWidgetParentEnum::ANNOTATION_TOOL_BAR_WIDGET: { - const int32_t numAnnotations = static_cast(m_annotationsFontStyle.size()); + const int32_t numAnnotations = static_cast(m_annotationsFontColor.size()); if (numAnnotations > 0) { bool firstColorSupportFlag = true; bool allSameColorFlag = true; for (int32_t i = 0; i < numAnnotations; i++) { if (firstColorSupportFlag) { - m_annotationsFontStyle[i]->getTextColorRGBA(rgba); + m_annotationsFontColor[i]->getTextColorRGBA(rgba); firstColorSupportFlag = false; colorButtonValidFlag = true; } else { float colorRGBA[4]; - m_annotationsFontStyle[i]->getTextColorRGBA(colorRGBA); + m_annotationsFontColor[i]->getTextColorRGBA(colorRGBA); for (int32_t iColor = 0; iColor < 4; iColor++) { if (rgba[iColor] != colorRGBA[iColor]) { allSameColorFlag = false; @@ -516,11 +658,11 @@ AnnotationFontWidget::updateTextColorButton() } if (allSameColorFlag) { - colorEnum = m_annotationsFontStyle[0]->getTextColor(); - m_annotationsFontStyle[0]->getTextColorRGBA(rgba); + colorEnum = m_annotationsFontColor[0]->getTextColor(); + m_annotationsFontColor[0]->getTextColorRGBA(rgba); float customRGBA[4]; - m_annotationsFontStyle[0]->getCustomTextColor(customRGBA); + m_annotationsFontColor[0]->getCustomTextColor(customRGBA); m_textColorMenu->setCustomIconColor(customRGBA); switch (m_parentWidgetType) { @@ -547,6 +689,7 @@ AnnotationFontWidget::updateTextColorButton() m_textColorAction->setIcon(QIcon(pm)); m_textColorMenu->setSelectedColor(colorEnum); + m_textColorToolButton->setEnabled(colorButtonValidFlag); if (colorButtonValidFlag) { } @@ -560,7 +703,7 @@ AnnotationFontWidget::fontBoldChanged() { AnnotationRedoUndoCommand* command = new AnnotationRedoUndoCommand(); command->setModeTextFontBold(m_boldFontAction->isChecked(), - m_annotations); + convertToAnnotations(m_annotationsFontStyle)); AnnotationManager* annMan = GuiManager::get()->getBrain()->getAnnotationManager(); AString errorMessage; if ( ! annMan->applyCommand(command, @@ -588,7 +731,7 @@ AnnotationFontWidget::fontItalicChanged() { AnnotationRedoUndoCommand* command = new AnnotationRedoUndoCommand(); command->setModeTextFontItalic(m_italicFontAction->isChecked(), - m_annotations); + convertToAnnotations(m_annotationsFontStyle)); AnnotationManager* annMan = GuiManager::get()->getBrain()->getAnnotationManager(); AString errorMessage; if ( ! annMan->applyCommand(command, @@ -618,7 +761,7 @@ AnnotationFontWidget::fontNameChanged() AnnotationRedoUndoCommand* command = new AnnotationRedoUndoCommand(); command->setModeTextFontName(fontName, - m_annotations); + convertToAnnotations(m_annotationsFontName)); AnnotationManager* annMan = GuiManager::get()->getBrain()->getAnnotationManager(); AString errorMessage; if ( ! annMan->applyCommand(command, @@ -650,7 +793,7 @@ AnnotationFontWidget::fontSizeChanged() AnnotationRedoUndoCommand* command = new AnnotationRedoUndoCommand(); command->setModeTextFontPercentSize(fontPercentSize, - m_annotations, + convertToAnnotations(m_annotationsFontSize), getSurfaceMontageRowCount()); AnnotationManager* annMan = GuiManager::get()->getBrain()->getAnnotationManager(); AString errorMessage; @@ -672,6 +815,11 @@ AnnotationFontWidget::fontSizeChanged() EventManager::get()->sendEvent(EventGraphicsUpdateAllWindows().getPointer()); + /* + * "Font too small" status is set while drawing so need + * to update the size spin box AFTER the graphics update + */ + updateFontSizeControls(); } /** @@ -706,7 +854,7 @@ AnnotationFontWidget::fontUnderlineChanged() { AnnotationRedoUndoCommand* command = new AnnotationRedoUndoCommand(); command->setModeTextFontUnderline(m_underlineFontAction->isChecked(), - m_annotations); + convertToAnnotations(m_annotationsFontStyle)); AnnotationManager* annMan = GuiManager::get()->getBrain()->getAnnotationManager(); AString errorMessage; if ( ! annMan->applyCommand(command, @@ -727,3 +875,26 @@ AnnotationFontWidget::fontUnderlineChanged() } } +/** + * Convert the font style interfade annotations to annotations + * + * @param fontInterfaces + * Input font interface annotations. + * @return + * Vector with input converted to Annotation class. + */ +std::vector +AnnotationFontWidget::convertToAnnotations(const std::vector& fontInterfaces) +{ + std::vector annotationsOut; + + for (auto f : fontInterfaces) { + Annotation* a = dynamic_cast(f); + CaretAssert(a); + annotationsOut.push_back(a); + } + + return annotationsOut; +} + + diff --git a/src/GuiQt/AnnotationFontWidget.h b/src/GuiQt/AnnotationFontWidget.h index 9a20de2bff066276702dd48e2a8a3293a0ab3ddc..ad9a3e8e0be93c300273af911aed4cbdaba57b96 100644 --- a/src/GuiQt/AnnotationFontWidget.h +++ b/src/GuiQt/AnnotationFontWidget.h @@ -28,6 +28,7 @@ #include "AnnotationWidgetParentEnum.h" #include "CaretColorEnum.h" +#include "EventListenerInterface.h" class QToolButton; @@ -38,9 +39,9 @@ namespace caret { class AnnotationText; class CaretColorEnumMenu; class EnumComboBoxTemplate; - class WuQSpecialIncrementDoubleSpinBox; + class WuQDoubleSpinBox; - class AnnotationFontWidget : public QWidget { + class AnnotationFontWidget : public QWidget, public EventListenerInterface { Q_OBJECT @@ -53,6 +54,8 @@ namespace caret { void updateContent(std::vector& annotations); + virtual void receiveEvent(Event* event) override; + // ADD_NEW_METHODS_HERE private slots: @@ -74,19 +77,32 @@ namespace caret { AnnotationFontWidget& operator=(const AnnotationFontWidget&); void updateFontSizeSpinBox(const float value, - const bool haveMultipleValuesFlag); + const bool haveMultipleValuesFlag, + const bool tooSmallFontFlag); void updateTextColorButton(); + void updateFontNameControls(); + + void updateFontSizeControls(); + + void updateFontStyleControls(); + float getSurfaceMontageRowCount() const; + static std::vector convertToAnnotations(const std::vector& fontInterfaces); + const AnnotationWidgetParentEnum::Enum m_parentWidgetType; const int32_t m_browserWindowIndex; EnumComboBoxTemplate* m_fontNameComboBox; - WuQSpecialIncrementDoubleSpinBox* m_fontSizeSpinBox; + WuQDoubleSpinBox* m_fontSizeSpinBox; + + QPalette m_fontSizeSpinBoxDefaultPalette; + + QPalette m_fontSizeSpinBoxRedTextPalette; QToolButton* m_textColorToolButton; @@ -100,12 +116,18 @@ namespace caret { QAction* m_underlineFontAction; - /** Contains annotations */ - std::vector m_annotations; + /** Contains annotations supporting font name */ + std::vector m_annotationsFontName; - /** Contains annotations with type as font style */ + /** Contains annotations supporting font style */ + std::vector m_annotationsFontSize; + + /** Contains annotations supporting font style */ std::vector m_annotationsFontStyle; + /** Contains annotations supporting font color */ + std::vector m_annotationsFontColor; + // ADD_NEW_MEMBERS_HERE }; diff --git a/src/GuiQt/AnnotationFormatWidget.cxx b/src/GuiQt/AnnotationFormatWidget.cxx index 3e200f4d5e583ba6ff1538403c43a9643c71d657..908a5cd06bacf7763879738ecff163e551bdffc3 100644 --- a/src/GuiQt/AnnotationFormatWidget.cxx +++ b/src/GuiQt/AnnotationFormatWidget.cxx @@ -98,6 +98,7 @@ AnnotationFormatWidget::createArrangeMenuToolButton() QToolButton* arrangeToolButton = new QToolButton(); arrangeToolButton->setDefaultAction(arrangeAction); + WuQtUtilities::setToolButtonStyleForQt5Mac(arrangeToolButton); return arrangeToolButton; } diff --git a/src/GuiQt/AnnotationInsertNewWidget.cxx b/src/GuiQt/AnnotationInsertNewWidget.cxx index 29482588161b4d2e0b9e495cd7feacb7308a2e18..2a60f4ee1eb9b595d46569d8b7b9feb006db70a1 100644 --- a/src/GuiQt/AnnotationInsertNewWidget.cxx +++ b/src/GuiQt/AnnotationInsertNewWidget.cxx @@ -29,6 +29,7 @@ #include #include #include +#include #include #include @@ -38,6 +39,8 @@ #include "AnnotationMenuFileSelection.h" #include "AnnotationRedoUndoCommand.h" #include "Brain.h" +#include "BrainBrowserWindow.h" +#include "BrowserTabContent.h" #include "CaretAssert.h" #include "CaretUndoStack.h" #include "DisplayPropertiesAnnotation.h" @@ -46,6 +49,7 @@ #include "EventUserInterfaceUpdate.h" #include "EventManager.h" #include "GuiManager.h" +#include "Model.h" #include "WuQMessageBox.h" #include "WuQtUtilities.h" @@ -72,16 +76,39 @@ AnnotationInsertNewWidget::AnnotationInsertNewWidget(const int32_t browserWindow : QWidget(parent), m_browserWindowIndex(browserWindowIndex) { - QToolButton* shapeBoxToolButton = createShapeToolButton(AnnotationTypeEnum::BOX); - QToolButton* shapeImageToolButton = createShapeToolButton(AnnotationTypeEnum::IMAGE); - QToolButton* shapeLineToolButton = createShapeToolButton(AnnotationTypeEnum::LINE); - QToolButton* shapeOvalToolButton = createShapeToolButton(AnnotationTypeEnum::OVAL); - QToolButton* shapeTextToolButton = createShapeToolButton(AnnotationTypeEnum::TEXT); + /* + * Shape buttons + */ + m_shapeActionGroup = new QActionGroup(this); + QToolButton* shapeBoxToolButton = createShapeToolButton(AnnotationTypeEnum::BOX, + m_shapeActionGroup); + QToolButton* shapeImageToolButton = createShapeToolButton(AnnotationTypeEnum::IMAGE, + m_shapeActionGroup); + QToolButton* shapeLineToolButton = createShapeToolButton(AnnotationTypeEnum::LINE, + m_shapeActionGroup); + QToolButton* shapeOvalToolButton = createShapeToolButton(AnnotationTypeEnum::OVAL, + m_shapeActionGroup); + QToolButton* shapeTextToolButton = createShapeToolButton(AnnotationTypeEnum::TEXT, + m_shapeActionGroup); + QObject::connect(m_shapeActionGroup, SIGNAL(triggered(QAction*)), + this, SLOT(spaceOrShapeActionTriggered())); - QToolButton* tabSpaceToolButton = createSpaceToolButton(AnnotationCoordinateSpaceEnum::TAB); - QToolButton* stereotaxicSpaceToolButton = createSpaceToolButton(AnnotationCoordinateSpaceEnum::STEREOTAXIC); - QToolButton* surfaceSpaceToolButton = createSpaceToolButton(AnnotationCoordinateSpaceEnum::SURFACE); - QToolButton* windowSpaceToolButton = createSpaceToolButton(AnnotationCoordinateSpaceEnum::WINDOW); + /* + * Space buttons + */ + m_spaceActionGroup = new QActionGroup(this); + QToolButton* chartSpaceToolButton = createSpaceToolButton(AnnotationCoordinateSpaceEnum::CHART, + m_spaceActionGroup); + QToolButton* tabSpaceToolButton = createSpaceToolButton(AnnotationCoordinateSpaceEnum::TAB, + m_spaceActionGroup); + QToolButton* stereotaxicSpaceToolButton = createSpaceToolButton(AnnotationCoordinateSpaceEnum::STEREOTAXIC, + m_spaceActionGroup); + QToolButton* surfaceSpaceToolButton = createSpaceToolButton(AnnotationCoordinateSpaceEnum::SURFACE, + m_spaceActionGroup); + QToolButton* windowSpaceToolButton = createSpaceToolButton(AnnotationCoordinateSpaceEnum::WINDOW, + m_spaceActionGroup); + QObject::connect(m_spaceActionGroup, SIGNAL(triggered(QAction*)), + this, SLOT(spaceOrShapeActionTriggered())); const bool smallButtonsFlag = false; if (smallButtonsFlag) { @@ -94,32 +121,15 @@ m_browserWindowIndex(browserWindowIndex) shapeOvalToolButton->setMaximumSize(mw, mh); shapeTextToolButton->setMaximumSize(mw, mh); + chartSpaceToolButton->setMaximumSize(mw, mh); tabSpaceToolButton->setMaximumSize(mw, mh); stereotaxicSpaceToolButton->setMaximumSize(mw, mh); surfaceSpaceToolButton->setMaximumSize(mw, mh); windowSpaceToolButton->setMaximumSize(mw, mh); } - m_spaceActionGroup = new QActionGroup(this); - m_spaceActionGroup->addAction(tabSpaceToolButton->defaultAction()); - m_spaceActionGroup->addAction(stereotaxicSpaceToolButton->defaultAction()); - m_spaceActionGroup->addAction(surfaceSpaceToolButton->defaultAction()); - m_spaceActionGroup->addAction(windowSpaceToolButton->defaultAction()); - QObject::connect(m_spaceActionGroup, SIGNAL(triggered(QAction*)), - this, SLOT(spaceOrShapeActionTriggered())); - - m_shapeActionGroup = new QActionGroup(this); - m_shapeActionGroup->addAction(shapeBoxToolButton->defaultAction()); - m_shapeActionGroup->addAction(shapeImageToolButton->defaultAction()); - m_shapeActionGroup->addAction(shapeLineToolButton->defaultAction()); - m_shapeActionGroup->addAction(shapeOvalToolButton->defaultAction()); - m_shapeActionGroup->addAction(shapeTextToolButton->defaultAction()); - QObject::connect(m_shapeActionGroup, SIGNAL(triggered(QAction*)), - this, SLOT(spaceOrShapeActionTriggered())); - QToolButton* fileSelectionToolButton = createFileSelectionToolButton(); - QLabel* fileLabel = new QLabel("File"); QLabel* spaceLabel = new QLabel("Space"); QLabel* typeLabel = new QLabel("Type"); @@ -149,14 +159,16 @@ m_browserWindowIndex(browserWindowIndex) gridLayout->addWidget(spaceLabel, 1, 2, Qt::AlignLeft); - gridLayout->addWidget(stereotaxicSpaceToolButton, + gridLayout->addWidget(chartSpaceToolButton, 1, 3); - gridLayout->addWidget(surfaceSpaceToolButton, + gridLayout->addWidget(stereotaxicSpaceToolButton, 1, 4); - gridLayout->addWidget(tabSpaceToolButton, + gridLayout->addWidget(surfaceSpaceToolButton, 1, 5); - gridLayout->addWidget(windowSpaceToolButton, + gridLayout->addWidget(tabSpaceToolButton, 1, 6); + gridLayout->addWidget(windowSpaceToolButton, + 1, 7); gridLayout->setRowMinimumHeight(2, 2); @@ -193,14 +205,16 @@ m_browserWindowIndex(browserWindowIndex) gridLayout->addWidget(spaceLabel, 1, 2, Qt::AlignLeft); - gridLayout->addWidget(stereotaxicSpaceToolButton, + gridLayout->addWidget(chartSpaceToolButton, 1, 3); - gridLayout->addWidget(surfaceSpaceToolButton, + gridLayout->addWidget(stereotaxicSpaceToolButton, 1, 4); - gridLayout->addWidget(tabSpaceToolButton, + gridLayout->addWidget(surfaceSpaceToolButton, 1, 5); - gridLayout->addWidget(windowSpaceToolButton, + gridLayout->addWidget(tabSpaceToolButton, 1, 6); + gridLayout->addWidget(windowSpaceToolButton, + 1, 7); QSpacerItem* rowSpaceItem = new QSpacerItem(5, 5, QSizePolicy::Fixed, @@ -253,6 +267,7 @@ AnnotationInsertNewWidget::~AnnotationInsertNewWidget() void AnnotationInsertNewWidget::updateContent() { + enableDisableSpaceActions(); itemSelectedFromFileSelectionMenu(); } @@ -268,6 +283,109 @@ AnnotationInsertNewWidget::itemSelectedFromFileSelectionMenu() m_fileSelectionToolButtonAction->setText(m_fileSelectionMenu->getSelectedNameForToolButton()); } +/** + * Enable/disable space action based upon model in window. + */ +void +AnnotationInsertNewWidget::enableDisableSpaceActions() +{ + QSignalBlocker signalBlocker(m_spaceActionGroup); + + /* + * Window will be invalid when this widget is being created as part of window being created + */ + BrainBrowserWindow* window = GuiManager::get()->getBrowserWindowByWindowIndex(m_browserWindowIndex); + if (window == NULL) { + return; + } + BrowserTabContent* tabContent = window->getBrowserTabContent(); + if (tabContent == NULL) { + return; + } + Model* model = tabContent->getModelForDisplay(); + if (model == NULL) { + return; + } + const ModelTypeEnum::Enum modelType = model->getModelType(); + + bool chartSpaceValidFlag = false; + bool surfaceSpaceValidFlag = false; + bool stereotaxicSpaceValidFlag = false; + + switch (modelType) { + case ModelTypeEnum::MODEL_TYPE_CHART: + break; + case ModelTypeEnum::MODEL_TYPE_CHART_TWO: + chartSpaceValidFlag = true; + break; + case ModelTypeEnum::MODEL_TYPE_INVALID: + break; + case ModelTypeEnum::MODEL_TYPE_SURFACE: + stereotaxicSpaceValidFlag = true; + surfaceSpaceValidFlag = true; + break; + case ModelTypeEnum::MODEL_TYPE_SURFACE_MONTAGE: + stereotaxicSpaceValidFlag = true; + surfaceSpaceValidFlag = true; + break; + case ModelTypeEnum::MODEL_TYPE_VOLUME_SLICES: + stereotaxicSpaceValidFlag = true; + break; + case ModelTypeEnum::MODEL_TYPE_WHOLE_BRAIN: + stereotaxicSpaceValidFlag = true; + surfaceSpaceValidFlag = true; + break; + } + + QAction* selectedAction = m_spaceActionGroup->checkedAction(); + QAction* tabSpaceAction = NULL; + QListIterator actionsIterator(m_spaceActionGroup->actions()); + while (actionsIterator.hasNext()) { + QAction* action = actionsIterator.next(); + const int spaceInt = action->data().toInt(); + bool spaceValidFlag = false; + AnnotationCoordinateSpaceEnum::Enum annSpace = AnnotationCoordinateSpaceEnum::fromIntegerCode(spaceInt, + &spaceValidFlag); + CaretAssert(spaceValidFlag); + + bool enableSpaceFlag = false; + switch (annSpace) { + case AnnotationCoordinateSpaceEnum::CHART: + enableSpaceFlag = chartSpaceValidFlag; + break; + case AnnotationCoordinateSpaceEnum::STEREOTAXIC: + enableSpaceFlag = stereotaxicSpaceValidFlag; + break; + case AnnotationCoordinateSpaceEnum::SURFACE: + enableSpaceFlag = surfaceSpaceValidFlag; + break; + case AnnotationCoordinateSpaceEnum::TAB: + tabSpaceAction = action; + enableSpaceFlag = true; + break; + case AnnotationCoordinateSpaceEnum::VIEWPORT: + break; + case AnnotationCoordinateSpaceEnum::WINDOW: + enableSpaceFlag = true; + break; + } + + action->setEnabled(enableSpaceFlag); + + if (action == selectedAction) { + if ( ! action->isEnabled()) { + selectedAction = NULL; + } + } + } + + if (selectedAction == NULL) { + CaretAssert(tabSpaceAction); + tabSpaceAction->setChecked(true); + } +} + + /** * @return Create a file selection/menu toolbutton. */ @@ -286,6 +404,7 @@ AnnotationInsertNewWidget::createFileSelectionToolButton() QToolButton* fileSelectionToolButton = new QToolButton(); fileSelectionToolButton->setDefaultAction(m_fileSelectionToolButtonAction); fileSelectionToolButton->setFixedWidth(fileSelectionToolButton->sizeHint().width()); + WuQtUtilities::setToolButtonStyleForQt5Mac(fileSelectionToolButton); return fileSelectionToolButton; } @@ -295,9 +414,12 @@ AnnotationInsertNewWidget::createFileSelectionToolButton() * * @param annotationType * The annotation type. + * @param actionGroup + * Action group to which button is added to make button exclusive. */ QToolButton* -AnnotationInsertNewWidget::createShapeToolButton(const AnnotationTypeEnum::Enum annotationType) +AnnotationInsertNewWidget::createShapeToolButton(const AnnotationTypeEnum::Enum annotationType, + QActionGroup* actionGroup) { const QString typeGuiName = AnnotationTypeEnum::toGuiName(annotationType); QToolButton* toolButton = new QToolButton(); @@ -317,6 +439,13 @@ AnnotationInsertNewWidget::createShapeToolButton(const AnnotationTypeEnum::Enum toolButton->setDefaultAction(action); + /* + * Must set style AFTER button is added to action group + * so that checked property is enabled for button + */ + actionGroup->addAction(action); + WuQtUtilities::setToolButtonStyleForQt5Mac(toolButton); + return toolButton; } @@ -470,13 +599,15 @@ AnnotationInsertNewWidget::createShapePixmap(const QWidget* widget, * * @param annotationSpace * The annotation space + * @param actionGroup + * Action group to which button is added to make button exclusive. */ QToolButton* -AnnotationInsertNewWidget::createSpaceToolButton(const AnnotationCoordinateSpaceEnum::Enum annotationSpace) +AnnotationInsertNewWidget::createSpaceToolButton(const AnnotationCoordinateSpaceEnum::Enum annotationSpace, + QActionGroup* actionGroup) { switch (annotationSpace) { - case AnnotationCoordinateSpaceEnum::PIXELS: - CaretAssertMessage(0, "Annotations in pixel space not supported."); + case AnnotationCoordinateSpaceEnum::CHART: break; case AnnotationCoordinateSpaceEnum::STEREOTAXIC: break; @@ -484,6 +615,9 @@ AnnotationInsertNewWidget::createSpaceToolButton(const AnnotationCoordinateSpace break; case AnnotationCoordinateSpaceEnum::TAB: break; + case AnnotationCoordinateSpaceEnum::VIEWPORT: + CaretAssertMessage(0, "Annotations in viewport space not supported."); + break; case AnnotationCoordinateSpaceEnum::WINDOW: break; } @@ -504,11 +638,18 @@ AnnotationInsertNewWidget::createSpaceToolButton(const AnnotationCoordinateSpace action->setData((int)AnnotationCoordinateSpaceEnum::toIntegerCode(annotationSpace)); action->setToolTip(AnnotationCoordinateSpaceEnum::toToolTip(annotationSpace)); - toolButton->setDefaultAction(action); - action->setCheckable(true); action->setChecked(false); + toolButton->setDefaultAction(action); + + /* + * Must set style AFTER button is added to action group + * so that checked property is enabled for button + */ + actionGroup->addAction(action); + WuQtUtilities::setToolButtonStyleForQt5Mac(toolButton); + return toolButton; } @@ -544,8 +685,7 @@ AnnotationInsertNewWidget::createSpacePixmap(const QWidget* widget, * NOTE: ORIGIN is in TOP LEFT corner of pixmap. */ switch (annotationSpace) { - case AnnotationCoordinateSpaceEnum::PIXELS: - CaretAssertMessage(0, "Annotations in pixel space not supported."); + case AnnotationCoordinateSpaceEnum::CHART: break; case AnnotationCoordinateSpaceEnum::STEREOTAXIC: break; @@ -553,6 +693,9 @@ AnnotationInsertNewWidget::createSpacePixmap(const QWidget* widget, break; case AnnotationCoordinateSpaceEnum::TAB: break; + case AnnotationCoordinateSpaceEnum::VIEWPORT: + CaretAssertMessage(0, "Annotations in viewport space not supported."); + break; case AnnotationCoordinateSpaceEnum::WINDOW: break; } diff --git a/src/GuiQt/AnnotationInsertNewWidget.h b/src/GuiQt/AnnotationInsertNewWidget.h index 5b3ff7967e039d20448edc667655538be8a3467b..eb6f03e4c1015d4e428d858bb2008cebe09b94b6 100644 --- a/src/GuiQt/AnnotationInsertNewWidget.h +++ b/src/GuiQt/AnnotationInsertNewWidget.h @@ -58,9 +58,11 @@ namespace caret { AnnotationInsertNewWidget& operator=(const AnnotationInsertNewWidget&); - QToolButton* createShapeToolButton(const AnnotationTypeEnum::Enum annotationType); + QToolButton* createShapeToolButton(const AnnotationTypeEnum::Enum annotationType, + QActionGroup* actionGroup); - QToolButton* createSpaceToolButton(const AnnotationCoordinateSpaceEnum::Enum annotationSpace); + QToolButton* createSpaceToolButton(const AnnotationCoordinateSpaceEnum::Enum annotationSpace, + QActionGroup* actionGroup); QPixmap createShapePixmap(const QWidget* widget, const AnnotationTypeEnum::Enum annotationType); @@ -70,6 +72,8 @@ namespace caret { QToolButton* createFileSelectionToolButton(); + void enableDisableSpaceActions(); + const int32_t m_browserWindowIndex; QActionGroup* m_spaceActionGroup; diff --git a/src/GuiQt/AnnotationLineArrowTipsWidget.cxx b/src/GuiQt/AnnotationLineArrowTipsWidget.cxx index ab187b9cb9d5f67bcf775fb7039ce021e1348e14..081af4789c9c4d8cf7fa948dc99101179015537d 100644 --- a/src/GuiQt/AnnotationLineArrowTipsWidget.cxx +++ b/src/GuiQt/AnnotationLineArrowTipsWidget.cxx @@ -26,6 +26,7 @@ #include #include #include +#include #include #include "AnnotationLine.h" @@ -59,31 +60,38 @@ m_browserWindowIndex(browserWindowIndex) { QLabel* label = new QLabel("Line"); - const QSize toolButtonSize(18, 18); + const QSize toolButtonSize(24, 24); - m_endArrowToolButton = new QToolButton(); - m_endArrowToolButton->setArrowType(Qt::DownArrow); - m_endArrowToolButton->setCheckable(true); - m_endArrowToolButton->setToolTip("Show arrow at line's end coordinate"); - m_endArrowToolButton->setFixedSize(toolButtonSize); - QObject::connect(m_endArrowToolButton, SIGNAL(clicked(bool)), - this, SLOT(endArrowTipActionToggled())); - m_startArrowToolButton = new QToolButton(); - m_startArrowToolButton->setArrowType(Qt::UpArrow); - m_startArrowToolButton->setCheckable(true); - m_startArrowToolButton->setToolTip("Show arrow at line's start coordinate"); - m_startArrowToolButton->setFixedSize(toolButtonSize); - QObject::connect(m_startArrowToolButton, SIGNAL(clicked(bool)), - this, SLOT(startArrowTipActionToggled())); + + QToolButton* endArrowToolButton = new QToolButton(); + m_endArrowAction = new QAction(this); + m_endArrowAction->setCheckable(true); + m_endArrowAction->setToolTip("Show arrow at line's end coordinate"); + m_endArrowAction->setIcon(QIcon(createArrowPixmap(endArrowToolButton, ArrowType::DOWN))); + QObject::connect(m_endArrowAction, &QAction::triggered, + this, &AnnotationLineArrowTipsWidget::endArrowTipActionToggled); + endArrowToolButton->setDefaultAction(m_endArrowAction); + WuQtUtilities::setToolButtonStyleForQt5Mac(endArrowToolButton); + + + QToolButton* startArrowToolButton = new QToolButton(); + m_startArrowAction = new QAction(this); + m_startArrowAction->setCheckable(true); + m_startArrowAction->setToolTip("Show arrow at line's start coordinate"); + m_startArrowAction->setIcon(QIcon(createArrowPixmap(startArrowToolButton, ArrowType::UP))); + QObject::connect(m_startArrowAction, &QAction::triggered, + this, &AnnotationLineArrowTipsWidget::startArrowTipActionToggled); + startArrowToolButton->setDefaultAction(m_startArrowAction); + WuQtUtilities::setToolButtonStyleForQt5Mac(startArrowToolButton); QGridLayout* gridLayout = new QGridLayout(this); WuQtUtilities::setLayoutSpacingAndMargins(gridLayout, 2, 0); gridLayout->addWidget(label, 0, 0, Qt::AlignHCenter); - gridLayout->addWidget(m_startArrowToolButton, + gridLayout->addWidget(startArrowToolButton, 1, 0, Qt::AlignHCenter); - gridLayout->addWidget(m_endArrowToolButton, + gridLayout->addWidget(endArrowToolButton, 2, 0, Qt::AlignHCenter); setSizePolicy(QSizePolicy::Fixed, @@ -106,15 +114,13 @@ void AnnotationLineArrowTipsWidget::updateContent(std::vector& annotationLines) { m_annotations.clear(); - m_annotations.insert(m_annotations.end(), - annotationLines.begin(), - annotationLines.end()); - - AnnotationLine* line = NULL; - if ( ! annotationLines.empty()) { - line = annotationLines[0]; + m_annotations.reserve(annotationLines.size()); + for (auto a : annotationLines) { + if (a->testProperty(Annotation::Property::LINE_ARROWS)) { + m_annotations.push_back(a); + } } - + bool allStartOnFlag = true; bool allEndOnFlag = true; @@ -134,14 +140,14 @@ AnnotationLineArrowTipsWidget::updateContent(std::vector& annot allEndOnFlag = false; } - m_startArrowToolButton->setChecked(allStartOnFlag); - m_endArrowToolButton->setChecked(allEndOnFlag); + m_startArrowAction->setChecked(allStartOnFlag); + m_endArrowAction->setChecked(allEndOnFlag); if (numLines > 0) { setEnabled(true); - AnnotationLine::setUserDefaultDisplayStartArrow(m_startArrowToolButton->isChecked()); - AnnotationLine::setUserDefaultDisplayEndArrow(m_endArrowToolButton->isChecked()); + AnnotationLine::setUserDefaultDisplayStartArrow(m_startArrowAction->isChecked()); + AnnotationLine::setUserDefaultDisplayEndArrow(m_endArrowAction->isChecked()); } else { setEnabled(false); @@ -156,7 +162,7 @@ void AnnotationLineArrowTipsWidget::startArrowTipActionToggled() { AnnotationRedoUndoCommand* undoCommand = new AnnotationRedoUndoCommand(); - undoCommand->setModeLineArrowStart(m_startArrowToolButton->isChecked(), + undoCommand->setModeLineArrowStart(m_startArrowAction->isChecked(), m_annotations); AnnotationManager* annMan = GuiManager::get()->getBrain()->getAnnotationManager(); @@ -169,7 +175,7 @@ AnnotationLineArrowTipsWidget::startArrowTipActionToggled() EventManager::get()->sendSimpleEvent(EventTypeEnum::EVENT_ANNOTATION_TOOLBAR_UPDATE); EventManager::get()->sendEvent(EventGraphicsUpdateAllWindows().getPointer()); - AnnotationLine::setUserDefaultDisplayStartArrow(m_startArrowToolButton->isChecked()); + AnnotationLine::setUserDefaultDisplayStartArrow(m_startArrowAction->isChecked()); } /** @@ -179,7 +185,7 @@ void AnnotationLineArrowTipsWidget::endArrowTipActionToggled() { AnnotationRedoUndoCommand* undoCommand = new AnnotationRedoUndoCommand(); - undoCommand->setModeLineArrowEnd(m_endArrowToolButton->isChecked(), + undoCommand->setModeLineArrowEnd(m_endArrowAction->isChecked(), m_annotations); AnnotationManager* annMan = GuiManager::get()->getBrain()->getAnnotationManager(); @@ -193,5 +199,66 @@ AnnotationLineArrowTipsWidget::endArrowTipActionToggled() EventManager::get()->sendSimpleEvent(EventTypeEnum::EVENT_ANNOTATION_TOOLBAR_UPDATE); EventManager::get()->sendEvent(EventGraphicsUpdateAllWindows().getPointer()); - AnnotationLine::setUserDefaultDisplayEndArrow(m_endArrowToolButton->isChecked()); + AnnotationLine::setUserDefaultDisplayEndArrow(m_endArrowAction->isChecked()); } + +/** + * Create a pixmap for the given arrow type type. + * + * @param widget + * To color the pixmap with backround and foreground, + * the palette from the given widget is used. + * @param arrowType + * The arrow type. + * @return + * Pixmap with icon for the given arrow type. + */ +QPixmap +AnnotationLineArrowTipsWidget::createArrowPixmap(const QWidget* widget, + const ArrowType arrowType) +{ + CaretAssert(widget); + + /* + * Create a small, square pixmap that will contain + * the foreground color around the pixmap's perimeter. + */ + const float width = 24.0; + const float height = 24.0; + QPixmap pixmap(static_cast(width), + static_cast(height)); + QSharedPointer painter = WuQtUtilities::createPixmapWidgetPainterOriginBottomLeft(widget, pixmap); + + const bool fillShapeFlag = false; + if (fillShapeFlag) { + QBrush brush = painter->brush(); + brush.setColor(painter->pen().color()); + brush.setStyle(Qt::SolidPattern); + painter->setBrush(brush); + } + + const float percentage = 0.10f; + const float left = width * percentage; + const float right = width * (1.0 - percentage); + const float bottom = height * percentage; + const float top = height * (1.0 - percentage); + const float centerX = width * 0.5; + QPolygonF triangle; + switch (arrowType) { + case ArrowType::DOWN: + triangle.push_back(QPointF(right, top)); + triangle.push_back(QPointF(left, top)); + triangle.push_back(QPointF(centerX, bottom)); + break; + case ArrowType::UP: + triangle.push_back(QPointF(left, bottom)); + triangle.push_back(QPointF(right, bottom)); + triangle.push_back(QPointF(centerX, top)); + break; + } + painter->drawPolygon(triangle); + + return pixmap; +} + + diff --git a/src/GuiQt/AnnotationLineArrowTipsWidget.h b/src/GuiQt/AnnotationLineArrowTipsWidget.h index dbc0bc584167b53077425a6a23e9df276b5499e4..688f54b5dba74f786f594883a6999f633269dd23 100644 --- a/src/GuiQt/AnnotationLineArrowTipsWidget.h +++ b/src/GuiQt/AnnotationLineArrowTipsWidget.h @@ -26,8 +26,6 @@ #include -class QToolButton; - namespace caret { class Annotation; @@ -54,15 +52,24 @@ namespace caret { // ADD_NEW_METHODS_HERE private: + enum class ArrowType { + DOWN, + UP + }; + AnnotationLineArrowTipsWidget(const AnnotationLineArrowTipsWidget&); AnnotationLineArrowTipsWidget& operator=(const AnnotationLineArrowTipsWidget&); + QPixmap createArrowPixmap(const QWidget* widget, + const ArrowType arrowType); + + const int32_t m_browserWindowIndex; - QToolButton* m_startArrowToolButton; + QAction* m_startArrowAction; - QToolButton* m_endArrowToolButton; + QAction* m_endArrowAction; std::vector m_annotations; diff --git a/src/GuiQt/AnnotationMenuArrange.cxx b/src/GuiQt/AnnotationMenuArrange.cxx index ce0ec5eac74fdf0a87ef26611ccfcb01495b22b0..a5b9f9e3d43d402d03a258f9756e0c195533129c 100644 --- a/src/GuiQt/AnnotationMenuArrange.cxx +++ b/src/GuiQt/AnnotationMenuArrange.cxx @@ -247,7 +247,7 @@ AnnotationMenuArrange::menuActionTriggered(QAction* action) void AnnotationMenuArrange::applyAlignment(const AnnotationAlignmentEnum::Enum alignment) { - EventGetBrainOpenGLTextRenderer textRendererEvent(m_browserWindowIndex); + EventGetBrainOpenGLTextRenderer textRendererEvent; EventManager::get()->sendEvent(textRendererEvent.getPointer()); BrainOpenGLTextRenderInterface* textRenderer = textRendererEvent.getTextRenderer(); if (textRenderer == NULL) { @@ -281,7 +281,7 @@ AnnotationMenuArrange::applyAlignment(const AnnotationAlignmentEnum::Enum alignm void AnnotationMenuArrange::applyDistribute(const AnnotationDistributeEnum::Enum distribute) { - EventGetBrainOpenGLTextRenderer textRendererEvent(m_browserWindowIndex); + EventGetBrainOpenGLTextRenderer textRendererEvent; EventManager::get()->sendEvent(textRendererEvent.getPointer()); BrainOpenGLTextRenderInterface* textRenderer = textRendererEvent.getTextRenderer(); if (textRenderer == NULL) { diff --git a/src/GuiQt/AnnotationPasteDialog.cxx b/src/GuiQt/AnnotationPasteDialog.cxx index 6221c2bd713a9ce0772fc732795862a7a07586ce..7ada21c497eebf2636015b757f7019d698609f8a 100644 --- a/src/GuiQt/AnnotationPasteDialog.cxx +++ b/src/GuiQt/AnnotationPasteDialog.cxx @@ -296,7 +296,7 @@ AnnotationPasteDialog::pasteOneDimensionalShape(AnnotationOneDimensionalShape* o bool windowFlag = false; switch (oneDimShape->getCoordinateSpace()) { - case AnnotationCoordinateSpaceEnum::PIXELS: + case AnnotationCoordinateSpaceEnum::CHART: break; case AnnotationCoordinateSpaceEnum::STEREOTAXIC: break; @@ -305,6 +305,8 @@ AnnotationPasteDialog::pasteOneDimensionalShape(AnnotationOneDimensionalShape* o case AnnotationCoordinateSpaceEnum::TAB: tabFlag = true; break; + case AnnotationCoordinateSpaceEnum::VIEWPORT: + break; case AnnotationCoordinateSpaceEnum::WINDOW: windowFlag = true; break; diff --git a/src/GuiQt/AnnotationRedoUndoWidget.cxx b/src/GuiQt/AnnotationRedoUndoWidget.cxx index 5801bed6a0aebb815b2753bac531b6bbd68f3bdc..3b7519b67b67f8c8665294336c5da38fec268ba7 100644 --- a/src/GuiQt/AnnotationRedoUndoWidget.cxx +++ b/src/GuiQt/AnnotationRedoUndoWidget.cxx @@ -65,6 +65,7 @@ m_browserWindowIndex(browserWindowIndex) SLOT(redoActionTriggered())); QToolButton* redoToolButton = new QToolButton(); redoToolButton->setDefaultAction(m_redoAction); + WuQtUtilities::setToolButtonStyleForQt5Mac(redoToolButton); m_undoAction = WuQtUtilities::createAction("Undo", "Undo ToolTip", @@ -73,6 +74,7 @@ m_browserWindowIndex(browserWindowIndex) SLOT(undoActionTriggered())); QToolButton* undoToolButton = new QToolButton(); undoToolButton->setDefaultAction(m_undoAction); + WuQtUtilities::setToolButtonStyleForQt5Mac(undoToolButton); QGridLayout* gridLayout = new QGridLayout(this); WuQtUtilities::setLayoutSpacingAndMargins(gridLayout, 2, 0); diff --git a/src/GuiQt/AnnotationRotationWidget.cxx b/src/GuiQt/AnnotationRotationWidget.cxx index 2146e30c3496d4cc3089e87d6748004486d7178a..de9a2363dd2810044975387608d37c941a58b665 100644 --- a/src/GuiQt/AnnotationRotationWidget.cxx +++ b/src/GuiQt/AnnotationRotationWidget.cxx @@ -110,7 +110,7 @@ AnnotationRotationWidget::getValidOneDimAnnotation(Annotation* annotation) if (oneDimAnn != NULL) { bool validSpaceFlag = false; switch (oneDimAnn->getCoordinateSpace()) { - case AnnotationCoordinateSpaceEnum::PIXELS: + case AnnotationCoordinateSpaceEnum::CHART: break; case AnnotationCoordinateSpaceEnum::STEREOTAXIC: break; @@ -119,6 +119,8 @@ AnnotationRotationWidget::getValidOneDimAnnotation(Annotation* annotation) case AnnotationCoordinateSpaceEnum::TAB: validSpaceFlag = true; break; + case AnnotationCoordinateSpaceEnum::VIEWPORT: + break; case AnnotationCoordinateSpaceEnum::WINDOW: validSpaceFlag = true; break; @@ -152,79 +154,83 @@ AnnotationRotationWidget::updateContent(std::vector& annotations) for (int32_t i = 0; i < numAnns; i++) { CaretAssertVectorIndex(annotations, i); Annotation* ann = annotations[i]; - if (ann->isSizeHandleValid(AnnotationSizingHandleTypeEnum::ANNOTATION_SIZING_HANDLE_ROTATION)) { - AnnotationTwoDimensionalShape* twoDimAnn = dynamic_cast(ann); - AnnotationOneDimensionalShape* oneDimAnn = getValidOneDimAnnotation(ann); - - float angle = 0.0; - float angleValid = false; - if (twoDimAnn != NULL) { - angle = twoDimAnn->getRotationAngle(); - angleValid = true; - } - else if (oneDimAnn != NULL) { - int32_t viewport[4] = { 0, 0, 0, 0 }; - bool viewportValidFlag = false; - switch (oneDimAnn->getCoordinateSpace()) { - case AnnotationCoordinateSpaceEnum::PIXELS: - break; - case AnnotationCoordinateSpaceEnum::STEREOTAXIC: - break; - case AnnotationCoordinateSpaceEnum::SURFACE: - break; - case AnnotationCoordinateSpaceEnum::TAB: - { - const int tabIndex = oneDimAnn->getTabIndex(); - EventGetViewportSize vpSizeEvent(EventGetViewportSize::MODE_TAB_AFTER_MARGINS_INDEX, - tabIndex); - EventManager::get()->sendEvent(vpSizeEvent.getPointer()); - if (vpSizeEvent.isViewportSizeValid()) { - vpSizeEvent.getViewportSize(viewport); - viewportValidFlag = true; - } - } - break; - case AnnotationCoordinateSpaceEnum::WINDOW: - { - const int windowIndex = oneDimAnn->getWindowIndex(); - EventGetViewportSize vpSizeEvent(EventGetViewportSize::MODE_WINDOW_INDEX, - windowIndex); - EventManager::get()->sendEvent(vpSizeEvent.getPointer()); - if (vpSizeEvent.isViewportSizeValid()) { - vpSizeEvent.getViewportSize(viewport); - viewportValidFlag = true; - } - } - break; - } + if (ann->testProperty(Annotation::Property::ROTATION)) { + if (ann->isSizeHandleValid(AnnotationSizingHandleTypeEnum::ANNOTATION_SIZING_HANDLE_ROTATION)) { + AnnotationTwoDimensionalShape* twoDimAnn = dynamic_cast(ann); + AnnotationOneDimensionalShape* oneDimAnn = getValidOneDimAnnotation(ann); - if (viewportValidFlag) { - angle = oneDimAnn->getRotationAngle(viewport[2], viewport[3]); + float angle = 0.0; + float angleValid = false; + if (twoDimAnn != NULL) { + angle = twoDimAnn->getRotationAngle(); angleValid = true; } - } - - if (angleValid) { - if (angle < 0.0) { - angle += 360.0; - } - else if (angle > 360.0) { - angle -= 360.0; + else if (oneDimAnn != NULL) { + int32_t viewport[4] = { 0, 0, 0, 0 }; + bool viewportValidFlag = false; + switch (oneDimAnn->getCoordinateSpace()) { + case AnnotationCoordinateSpaceEnum::CHART: + break; + case AnnotationCoordinateSpaceEnum::STEREOTAXIC: + break; + case AnnotationCoordinateSpaceEnum::SURFACE: + break; + case AnnotationCoordinateSpaceEnum::TAB: + { + const int tabIndex = oneDimAnn->getTabIndex(); + EventGetViewportSize vpSizeEvent(EventGetViewportSize::MODE_TAB_AFTER_MARGINS_INDEX, + tabIndex); + EventManager::get()->sendEvent(vpSizeEvent.getPointer()); + if (vpSizeEvent.isViewportSizeValid()) { + vpSizeEvent.getViewportSize(viewport); + viewportValidFlag = true; + } + } + break; + case AnnotationCoordinateSpaceEnum::VIEWPORT: + break; + case AnnotationCoordinateSpaceEnum::WINDOW: + { + const int windowIndex = oneDimAnn->getWindowIndex(); + EventGetViewportSize vpSizeEvent(EventGetViewportSize::MODE_WINDOW_INDEX, + windowIndex); + EventManager::get()->sendEvent(vpSizeEvent.getPointer()); + if (vpSizeEvent.isViewportSizeValid()) { + vpSizeEvent.getViewportSize(viewport); + viewportValidFlag = true; + } + } + break; + } + + if (viewportValidFlag) { + angle = oneDimAnn->getRotationAngle(viewport[2], viewport[3]); + angleValid = true; + } } - if (rotationAngleValid) { - if (rotationAngle != angle) { - haveMultipleRotationAnglesFlag = true; + if (angleValid) { + if (angle < 0.0) { + angle += 360.0; } - rotationAngle = std::min(rotationAngle, - angle); - } - else { - rotationAngle = angle; - rotationAngleValid = true; + else if (angle > 360.0) { + angle -= 360.0; + } + + if (rotationAngleValid) { + if (rotationAngle != angle) { + haveMultipleRotationAnglesFlag = true; + } + rotationAngle = std::min(rotationAngle, + angle); + } + else { + rotationAngle = angle; + rotationAngleValid = true; + } + + m_annotations.push_back(ann); } - - m_annotations.push_back(ann); } } } @@ -257,22 +263,6 @@ AnnotationRotationWidget::updateContent(std::vector& annotations) void AnnotationRotationWidget::rotationValueChanged(double value) { -// AnnotationManager* annMan = GuiManager::get()->getBrain()->getAnnotationManager(); -// std::vector annotations = annMan->getAnnotationsSelectedForEditing(m_browserWindowIndex); -// -// std::vector rotateAnnotations; -// const int32_t numAnns = static_cast(annotations.size()); -// for (int32_t i = 0; i < numAnns; i++) { -// CaretAssertVectorIndex(annotations, i); -// Annotation* ann = annotations[i]; -// AnnotationTwoDimensionalShape* twoDimAnn = dynamic_cast(ann); -// AnnotationOneDimensionalShape* oneDimAnn = getValidOneDimAnnotation(ann); -// if ((oneDimAnn != NULL) -// || (twoDimAnn != NULL)) { -// rotateAnnotations.push_back(ann); -// } -// } - if ( ! m_annotations.empty()) { AnnotationRedoUndoCommand* undoCommand = new AnnotationRedoUndoCommand(); undoCommand->setModeRotationAngle(value, @@ -287,13 +277,4 @@ AnnotationRotationWidget::rotationValueChanged(double value) EventManager::get()->sendSimpleEvent(EventTypeEnum::EVENT_ANNOTATION_TOOLBAR_UPDATE); EventManager::get()->sendEvent(EventGraphicsUpdateAllWindows().getPointer()); } - -// AnnotationManager* annMan = GuiManager::get()->getBrain()->getAnnotationManager(); -// AnnotationRedoUndoCommand* undoCommand = new AnnotationRedoUndoCommand(); -// undoCommand->setModeRotationAngle(value, -// annMan->getAnnotationsSelectedForEditing()); -// annMan->applyCommand(undoCommand); -// -// EventManager::get()->sendSimpleEvent(EventTypeEnum::EVENT_ANNOTATION_TOOLBAR_UPDATE); -// EventManager::get()->sendEvent(EventGraphicsUpdateAllWindows().getPointer()); } diff --git a/src/GuiQt/AnnotationRotationWidget.h b/src/GuiQt/AnnotationRotationWidget.h index 69643210457205cadc8b53bdda0fae5884c25d8c..6fb7419a6af1c8df9607af551352a9b477518ad8 100644 --- a/src/GuiQt/AnnotationRotationWidget.h +++ b/src/GuiQt/AnnotationRotationWidget.h @@ -45,8 +45,6 @@ namespace caret { // ADD_NEW_METHODS_HERE - -// void updateContent(std::vector& annotation2D); void updateContent(std::vector& annotations); diff --git a/src/GuiQt/AnnotationSelectionViewController.cxx b/src/GuiQt/AnnotationSelectionViewController.cxx index 58505d27c9cfe209868c246a088fba1a818a6781..6334d6ce0f01a98ea3a3a80a9681c5fe2b57bb4e 100644 --- a/src/GuiQt/AnnotationSelectionViewController.cxx +++ b/src/GuiQt/AnnotationSelectionViewController.cxx @@ -266,6 +266,7 @@ AnnotationSelectionViewController::displayGroupSelected(const DisplayGroupEnum:: dpa->setDisplayGroupForTab(browserTabIndex, displayGroup); updateAnnotationSelections(); + EventManager::get()->sendEvent(EventGraphicsUpdateAllWindows().getPointer()); } diff --git a/src/GuiQt/AnnotationTextAlignmentWidget.cxx b/src/GuiQt/AnnotationTextAlignmentWidget.cxx index 70d472bef169f66abaf1423eec47b5e535b0a472..8e5398dfcb69b907509197a42227f193832d723c 100644 --- a/src/GuiQt/AnnotationTextAlignmentWidget.cxx +++ b/src/GuiQt/AnnotationTextAlignmentWidget.cxx @@ -77,7 +77,7 @@ m_browserWindowIndex(browserWindowIndex) QToolButton* rightAlignToolButton = createHorizontalAlignmentToolButton(AnnotationTextAlignHorizontalEnum::RIGHT); m_horizontalAlignActionGroup = new QActionGroup(this); - m_horizontalAlignActionGroup->setExclusive(false); // not exclusive as may need to turn all off + m_horizontalAlignActionGroup->setExclusive(false); /** not exclusive as may need to turn all off */ m_horizontalAlignActionGroup->addAction(leftAlignToolButton->defaultAction()); m_horizontalAlignActionGroup->addAction(centerAlignToolButton->defaultAction()); m_horizontalAlignActionGroup->addAction(rightAlignToolButton->defaultAction()); @@ -90,7 +90,7 @@ m_browserWindowIndex(browserWindowIndex) QToolButton* bottomAlignToolButton = createVerticalAlignmentToolButton(AnnotationTextAlignVerticalEnum::BOTTOM); m_verticalAlignActionGroup = new QActionGroup(this); - m_verticalAlignActionGroup->setExclusive(false); // not exclusive as may need to turn all off + m_verticalAlignActionGroup->setExclusive(false); /** not exclusive as may need to turn all off */ m_verticalAlignActionGroup->addAction(topAlignToolButton->defaultAction()); m_verticalAlignActionGroup->addAction(middleAlignToolButton->defaultAction()); m_verticalAlignActionGroup->addAction(bottomAlignToolButton->defaultAction()); @@ -160,9 +160,12 @@ void AnnotationTextAlignmentWidget::updateContent(std::vector& annotationTexts) { m_annotations.clear(); - m_annotations.insert(m_annotations.end(), - annotationTexts.begin(), - annotationTexts.end()); + m_annotations.reserve(annotationTexts.size()); + for (auto a : annotationTexts) { + if (a->testProperty(Annotation::Property::TEXT_ORIENTATION)) { + m_annotations.push_back(a); + } + } { /* @@ -174,8 +177,8 @@ AnnotationTextAlignmentWidget::updateContent(std::vector& annot * If multiple annotations are selected, the may have different alignments. */ std::set selectedAlignments; - for (std::vector::iterator iter = annotationTexts.begin(); - iter != annotationTexts.end(); + for (std::vector::iterator iter = m_annotations.begin(); + iter != m_annotations.end(); iter++) { const AnnotationText* annText = *iter; CaretAssert(annText); @@ -230,8 +233,8 @@ AnnotationTextAlignmentWidget::updateContent(std::vector& annot * If multiple annotations are selected, the may have different alignments. */ std::set selectedAlignments; - for (std::vector::iterator iter = annotationTexts.begin(); - iter != annotationTexts.end(); + for (std::vector::iterator iter = m_annotations.begin(); + iter != m_annotations.end(); iter++) { const AnnotationText* annText = *iter; CaretAssert(annText); @@ -277,7 +280,7 @@ AnnotationTextAlignmentWidget::updateContent(std::vector& annot m_verticalAlignActionGroup->blockSignals(false); } - setEnabled( ! annotationTexts.empty()); + setEnabled( ! m_annotations.empty()); } /** @@ -296,8 +299,10 @@ AnnotationTextAlignmentWidget::horizontalAlignmentActionSelected(QAction* action &valid); if (valid) { AnnotationRedoUndoCommand* undoCommand = new AnnotationRedoUndoCommand(); + std::vector annotations(m_annotations.begin(), + m_annotations.end()); undoCommand->setModeTextAlignmentHorizontal(actionAlign, - m_annotations); + annotations); AnnotationManager* annMan = GuiManager::get()->getBrain()->getAnnotationManager(); AString errorMessage; if ( ! annMan->applyCommand(undoCommand, @@ -328,8 +333,10 @@ AnnotationTextAlignmentWidget::verticalAlignmentActionSelected(QAction* action) &valid); if (valid) { AnnotationRedoUndoCommand* undoCommand = new AnnotationRedoUndoCommand(); + std::vector annotations(m_annotations.begin(), + m_annotations.end()); undoCommand->setModeTextAlignmentVertical(actionAlign, - m_annotations); + annotations); AnnotationManager* annMan = GuiManager::get()->getBrain()->getAnnotationManager(); AString errorMessage; if ( ! annMan->applyCommand(undoCommand, @@ -379,6 +386,7 @@ AnnotationTextAlignmentWidget::createHorizontalAlignmentToolButton(const Annotat action->setIcon(QIcon(pixmap)); toolButton->setDefaultAction(action); toolButton->setIconSize(pixmap.size()); + WuQtUtilities::setToolButtonStyleForQt5Mac(toolButton); return toolButton; } @@ -418,6 +426,7 @@ AnnotationTextAlignmentWidget::createVerticalAlignmentToolButton(const Annotatio action->setIcon(QIcon(pixmap)); toolButton->setDefaultAction(action); toolButton->setIconSize(pixmap.size()); + WuQtUtilities::setToolButtonStyleForQt5Mac(toolButton); return toolButton; } @@ -461,7 +470,7 @@ AnnotationTextAlignmentWidget::createHorizontalAlignmentPixmap(const QWidget* wi const qreal margin = width * 0.05; const qreal longLineLength = width - (margin * 2.0); const qreal shortLineLength = width / 2.0; - const qreal yStep = MathFunctions::round(height / (numLines + 1)); //6.0); + const qreal yStep = MathFunctions::round(height / (numLines + 1)); for (int32_t i = 1; i <= numLines; i++) { const qreal lineLength = (((i % 2) == 0) diff --git a/src/GuiQt/AnnotationTextAlignmentWidget.h b/src/GuiQt/AnnotationTextAlignmentWidget.h index 60d9377fb4d68cb113e6765a7343556967e3bfa6..71d618987ea0a7ff5cecc79a5b4aa3dec8d60d34 100644 --- a/src/GuiQt/AnnotationTextAlignmentWidget.h +++ b/src/GuiQt/AnnotationTextAlignmentWidget.h @@ -76,7 +76,7 @@ namespace caret { QActionGroup* m_verticalAlignActionGroup; - std::vector m_annotations; + std::vector m_annotations; bool m_smallLayoutFlag; diff --git a/src/GuiQt/AnnotationTextEditorWidget.cxx b/src/GuiQt/AnnotationTextEditorWidget.cxx index aa8c00018995c3bbb517b5da9be63aba7fad8f27..4e80d4441c8f15327150add06218b8fd1fd86d24 100644 --- a/src/GuiQt/AnnotationTextEditorWidget.cxx +++ b/src/GuiQt/AnnotationTextEditorWidget.cxx @@ -109,8 +109,11 @@ void AnnotationTextEditorWidget::updateContent(std::vector& annotationTexts) { m_annotationText = NULL; - if (annotationTexts.size() == 1) { - m_annotationText = annotationTexts[0]; + for (auto a : annotationTexts) { + if (a->testProperty(Annotation::Property::TEXT_EDIT)) { + m_annotationText = a; + break; + } } @@ -122,9 +125,9 @@ AnnotationTextEditorWidget::updateContent(std::vector& annotati QString text; + m_annotationTextConnectTypeEnumComboBox->getComboBox()->setEnabled(false); if (m_annotationText != NULL) { setEnabled(true); - if (m_annotationText->isConnectToBrainordinateValid()) { m_annotationTextConnectTypeEnumComboBox->getComboBox()->setEnabled(true); } diff --git a/src/GuiQt/AnnotationTextOrientationWidget.cxx b/src/GuiQt/AnnotationTextOrientationWidget.cxx index 3d4ae3ea862f51f033331381232953306140e898..3dbe7064a90a1771c7703ea9b75942dcab2e93c1 100644 --- a/src/GuiQt/AnnotationTextOrientationWidget.cxx +++ b/src/GuiQt/AnnotationTextOrientationWidget.cxx @@ -94,15 +94,18 @@ AnnotationTextOrientationWidget::~AnnotationTextOrientationWidget() /** * Update with the given annotation. * - * @param annotation. + * @param annotationTextsIn. */ void -AnnotationTextOrientationWidget::updateContent(std::vector& annotationTexts) +AnnotationTextOrientationWidget::updateContent(std::vector& annotationTextsIn) { m_annotations.clear(); - m_annotations.insert(m_annotations.end(), - annotationTexts.begin(), - annotationTexts.end()); + m_annotations.reserve(annotationTextsIn.size()); + for (auto a : annotationTextsIn) { + if (a->testProperty(Annotation::Property::TEXT_ORIENTATION)) { + m_annotations.push_back(a); + } + } { /* @@ -114,8 +117,8 @@ AnnotationTextOrientationWidget::updateContent(std::vector& ann * If multiple annotations are selected, the may have different orientation. */ std::set selectedOrientations; - for (std::vector::iterator iter = annotationTexts.begin(); - iter != annotationTexts.end(); + for (std::vector::iterator iter = m_annotations.begin(); + iter != m_annotations.end(); iter++) { const AnnotationText* annText = *iter; CaretAssert(annText); @@ -161,7 +164,7 @@ AnnotationTextOrientationWidget::updateContent(std::vector& ann m_orientationActionGroup->blockSignals(false); } - setEnabled( ! annotationTexts.empty()); + setEnabled( ! m_annotations.empty()); } /** @@ -180,8 +183,10 @@ AnnotationTextOrientationWidget::orientationActionSelected(QAction* action) &valid); if (valid) { AnnotationRedoUndoCommand* undoCommand = new AnnotationRedoUndoCommand(); + std::vector annotations(m_annotations.begin(), + m_annotations.end()); undoCommand->setModeTextOrientation(actionOrientation, - m_annotations); + annotations); AnnotationManager* annMan = GuiManager::get()->getBrain()->getAnnotationManager(); AString errorMessage; if ( ! annMan->applyCommand(undoCommand, @@ -228,6 +233,7 @@ AnnotationTextOrientationWidget::createOrientationToolButton(const AnnotationTex action->setIcon(QIcon(pixmap)); toolButton->setDefaultAction(action); toolButton->setIconSize(pixmap.size()); + WuQtUtilities::setToolButtonStyleForQt5Mac(toolButton); return toolButton; } @@ -254,7 +260,7 @@ AnnotationTextOrientationWidget::createHorizontalAlignmentPixmap(const QWidget* * the foreground color around the pixmap's perimeter. */ float width = 24.0; - float height = 24.0; + float height = 30.0; QPixmap pixmap(static_cast(width), static_cast(height)); QSharedPointer painter = WuQtUtilities::createPixmapWidgetPainter(widget, @@ -269,7 +275,7 @@ AnnotationTextOrientationWidget::createHorizontalAlignmentPixmap(const QWidget* break; case AnnotationTextOrientationEnum::STACKED: painter->drawText(pixmap.rect(), - (Qt::AlignHCenter | Qt::AlignTop), + (Qt::AlignCenter), "a\nb"); break; } diff --git a/src/GuiQt/AnnotationTextOrientationWidget.h b/src/GuiQt/AnnotationTextOrientationWidget.h index 8c674257c34a6268b38fb88fafa489bfd02a482a..12be1d04baaa2393c5163562ceb2be9e4cbc0199 100644 --- a/src/GuiQt/AnnotationTextOrientationWidget.h +++ b/src/GuiQt/AnnotationTextOrientationWidget.h @@ -64,7 +64,7 @@ namespace caret { const int32_t m_browserWindowIndex; - std::vector m_annotations; + std::vector m_annotations; QActionGroup* m_orientationActionGroup; diff --git a/src/GuiQt/AnnotationWidthHeightWidget.cxx b/src/GuiQt/AnnotationWidthHeightWidget.cxx index ac8ad91b06088f2c8f60a1461c385490530f1e97..0896ca317f66da268a18c3fcd48fc993d7fb36d8 100644 --- a/src/GuiQt/AnnotationWidthHeightWidget.cxx +++ b/src/GuiQt/AnnotationWidthHeightWidget.cxx @@ -79,14 +79,14 @@ m_browserWindowIndex(browserWindowIndex) break; } QLabel* widthLabel = new QLabel(widthLabelText); - m_widthSpinBox = WuQFactory::newDoubleSpinBoxWithMinMaxStepDecimalsSignalDouble(0.0, 100.0, 0.1, 1, + m_widthSpinBox = WuQFactory::newDoubleSpinBoxWithMinMaxStepDecimalsSignalDouble(0.0, 1000.0, 0.1, 1, this, SLOT(widthValueChanged(double))); m_widthSpinBox->setSuffix("%"); WuQtUtilities::setWordWrappedToolTip(m_widthSpinBox, "Percentage width of 2D Shapes (Box, Image, Oval)"); QLabel* heightLabel = new QLabel(heightLabelText); - m_heightSpinBox = WuQFactory::newDoubleSpinBoxWithMinMaxStepDecimalsSignalDouble(0.0, 100.0, 0.1, 1, + m_heightSpinBox = WuQFactory::newDoubleSpinBoxWithMinMaxStepDecimalsSignalDouble(0.0, 1000.0, 0.1, 1, this, SLOT(heightValueChanged(double))); m_heightSpinBox->setSuffix("%"); WuQtUtilities::setWordWrappedToolTip(m_heightSpinBox, diff --git a/src/GuiQt/BalsaDatabaseManager.cxx b/src/GuiQt/BalsaDatabaseManager.cxx index ed365deba969dc5b71983072dc14badbbfcad29b..51d76c53882a38b664812ba6f31ea69b07856647 100644 --- a/src/GuiQt/BalsaDatabaseManager.cxx +++ b/src/GuiQt/BalsaDatabaseManager.cxx @@ -19,22 +19,27 @@ */ /*LICENSE_END*/ +#include #include #define __BALSA_DATABASE_MANAGER_DECLARE__ #include "BalsaDatabaseManager.h" #undef __BALSA_DATABASE_MANAGER_DECLARE__ +#include + +#include "BalsaUserRoles.h" #include "CaretAssert.h" #include "CaretHttpManager.h" -#include "CaretJsonObject.h" #include "CaretLogger.h" #include "CommandOperationManager.h" #include "EventManager.h" #include "EventProgressUpdate.h" #include "FileInformation.h" +#include "JsonHelper.h" #include "OperationZipSceneFile.h" #include "ProgramParameters.h" +#include "Scene.h" #include "SceneFile.h" using namespace caret; @@ -54,7 +59,8 @@ BalsaDatabaseManager::BalsaDatabaseManager() : CaretObject() { m_debugFlag = false; -// EventManager::get()->addEventListener(this, EventTypeEnum::); + logout(); + //EventManager::get()->addEventListener(this, EventTypeEnum::); } /** @@ -78,8 +84,8 @@ BalsaDatabaseManager::toString() const /** * Login to the BALSA Database. * - * @param loginURL - * URL for logging in. + * @param databaseURL + * URL of the database for login * @param username * Name for login. * @param password @@ -90,17 +96,20 @@ BalsaDatabaseManager::toString() const * True if login is successful, else false. */ bool -BalsaDatabaseManager::login(const AString& loginURL, +BalsaDatabaseManager::login(const AString& databaseURL, const AString& username, const AString& password, AString& errorMessageOut) { errorMessageOut.clear(); + m_databaseURL = databaseURL; m_username = username.trimmed(); m_password = password.trimmed(); m_jSessionIdCookie = ""; + const AString loginURL(databaseURL + "/j_spring_security_check"); + CaretHttpRequest loginRequest; loginRequest.m_method = CaretHttpManager::POST_ARGUMENTS; loginRequest.m_url = loginURL; @@ -145,21 +154,47 @@ BalsaDatabaseManager::login(const AString& loginURL, if (loginResponse.m_responseCode == 200) { if (m_jSessionIdCookie.isEmpty()) { errorMessageOut = ("Login was successful but BALSA failed to provide a Session ID."); + logout(); return false; } return true; } + else if (loginResponse.m_responseCode == 302) { + errorMessageOut = ("Login failed.

" + "If you are unable to login, helpful links are located on the right side of the username and password boxes:" + "

    " + "
  • Register: Click to create a BALSA account" + "
  • Forgot Username: Click to get your username by entering your email address" + "
  • Forgot Password: Click to reset your password by entering your username" + "
"); + logout(); + return false; + } loginResponse.m_body.push_back('\0'); const AString responseContent(&loginResponse.m_body[0]); errorMessageOut = ("Login has failed.\n" "HTTP Code: " + AString::number(loginResponse.m_responseCode) + " Content: " + responseContent); + logout(); return false; } +/** + * Logout of the database + */ +void +BalsaDatabaseManager::logout() +{ + m_databaseURL.clear(); + m_username.clear(); + m_password.clear(); + m_jSessionIdCookie.clear(); +} + + /** * Upload file to the BALSA Database. * @@ -267,7 +302,7 @@ BalsaDatabaseManager::uploadFileWithCaretHttpManager(const AString& uploadURL, responseContentOut.append(&uploadResponse.m_body[0]); - return processUploadResponse(uploadResponse.m_headers, + return verifyUploadFileResponse(uploadResponse.m_headers, responseContentOut, uploadResponse.m_responseCode, errorMessageOut); @@ -288,11 +323,17 @@ BalsaDatabaseManager::uploadFileWithCaretHttpManager(const AString& uploadURL, * True if response shows upload was successful, else false. */ bool -BalsaDatabaseManager::processUploadResponse(const std::map& responseHeaders, +BalsaDatabaseManager::verifyUploadFileResponse(const std::map& responseHeaders, const AString& responseContent, const int32_t responseHttpCode, AString& errorMessageOut) const { + if (responseHttpCode != 200) { + errorMessageOut = ("Upload failed. Http Code=" + + AString::number(responseHttpCode)); + return false; + } + if (responseHeaders.empty()) { if (m_debugFlag) std::cout << "Response headers from upload are empty." << std::endl; } @@ -326,23 +367,61 @@ BalsaDatabaseManager::processUploadResponse(const std::map& re contentErrorMessage = "No Content-Type header received from file upload.\n"; } - CaretJsonObject json(responseContent); + if (m_debugFlag) { + std::cout << std::endl << "RESPONSE CONTENT: " << responseContent << std::endl << std::endl; + } + - if (responseHttpCode != 200) { - AString msg = json.value("statusText"); - if ( ! msg.isEmpty()) { - contentErrorMessage.insert(0, msg + "\n"); + + AString statusText; + if (haveJsonResponseContentFlag) { + if (responseContent.isEmpty()) { + errorMessageOut = "Process upload response content is empty."; + return false; } - errorMessageOut = ("Upload failed. Http Code=" - + AString::number(responseHttpCode) - + "\n" - " " + contentErrorMessage); - return false; + QJsonParseError jsonError; + QJsonDocument jsonDocument = QJsonDocument::fromJson(responseContent.toLatin1(), + &jsonError); + if (jsonDocument.isNull()) { + errorMessageOut = ("Process upload response failed. Failed to parse JSON, error:" + + jsonError.errorString() + + "\n\n" + "Response Content: " + + responseContent); + return false; + } + + QByteArray json = jsonDocument.toJson(); + if (m_debugFlag) { + std::cout << "Array?: " << jsonDocument.isArray() << std::endl; + std::cout << "Empty?: " << jsonDocument.isEmpty() << std::endl; + std::cout << "NULL?: " << jsonDocument.isNull() << std::endl; + std::cout << "Object?: " << jsonDocument.isObject() << std::endl; + std::cout << "JSON length: " << json.size() << std::endl; + AString str(json); + std::cout << ("Formatted JSON:\n" + str) << std::endl; + } + + if ( ! jsonDocument.isObject()) { + errorMessageOut = ("Process upload response failed. JSON content is not an object." + "JSON is displayed in terminal if logging level is warning or higher."); + CaretLogWarning("Study ID JSON is not Object\n" + + AString(jsonDocument.toJson())); + return false; + } + + QJsonObject responseObject = jsonDocument.object(); + const QJsonValue statusValue = responseObject.value("statusText"); + if (statusValue.type() == QJsonValue::String) { + statusText = statusValue.toString(); + } + if (m_debugFlag) { + std::cout << "Status Text: " << statusText << std::endl; + } } - - if ( ! haveJsonResponseContentFlag) { - errorMessageOut = (contentErrorMessage); + else { + errorMessageOut = contentErrorMessage; return false; } @@ -392,10 +471,44 @@ BalsaDatabaseManager::zipSceneAndDataFiles(const SceneFile* sceneFile, return false; } - AString baseDirectoryName; - if ( ! sceneFile->getBaseDirectory().isEmpty()) { - /* validate ? */ - baseDirectoryName = sceneFile->getBaseDirectory(); + AString basePathName; + + switch (sceneFile->getBasePathType()) { + case SceneFileBasePathTypeEnum::AUTOMATIC: + { + std::vector missingFileNames; + AString errorMessage; + const bool validBasePathFlag = sceneFile->findBaseDirectoryForDataFiles(basePathName, + missingFileNames, + errorMessage); + if ( ! validBasePathFlag) { + errorMessageOut.appendWithNewLine("Automatic Base Path Failed: " + + errorMessage); + return false; + } + + if ( ! FileInformation(basePathName).exists()) { + errorMessageOut = ("AUTOMATIC base path mode produced an invalid base path (directory does not exist). " + ". \"" + + basePathName + + "\"."); + return false; + } + } + break; + case SceneFileBasePathTypeEnum::CUSTOM: + basePathName = sceneFile->getBalsaCustomBaseDirectory(); + if (basePathName.isEmpty()) { + errorMessageOut = ("CUSTOM base path is empty"); + return false; + } + if ( ! FileInformation(basePathName).exists()) { + errorMessageOut = ("CUSTOM base path is invalid (directory does not exist): \"" + + basePathName + + "\""); + return false; + } + break; } bool successFlag = false; @@ -403,13 +516,20 @@ BalsaDatabaseManager::zipSceneAndDataFiles(const SceneFile* sceneFile, OperationZipSceneFile::createZipFile(sceneFileName, extractToDirectoryName, zipFileName, - baseDirectoryName, + basePathName, OperationZipSceneFile::PROGRESS_GUI_EVENT, NULL); successFlag = true; } catch (const CaretException& e) { - errorMessageOut = e.whatString(); + AString moreInfo("If there was an error opening or writing to the ZIP file, " + "it may caused by incorrect permissions or insufficient space in the " + "temporary directory used for creating the ZIP file. Use the ZIP File Directory Custom option " + "in the Advanced Tab to choose an alternative directory."); + errorMessageOut = ("Error: " + + e.whatString() + + "\n\n" + + moreInfo); } return successFlag; @@ -434,35 +554,37 @@ BalsaDatabaseManager::getJSessionIdCookie() const void BalsaDatabaseManager::receiveEvent(Event* /*event*/) { -// if (event->getEventType() == EventTypeEnum::) { -// eventName = dynamic_cast(event); -// CaretAssert(eventName); -// -// event->setEventProcessed(); -// } + // if (event->getEventType() == EventTypeEnum::) { + // eventName = dynamic_cast(event); + // CaretAssert(eventName); + // + // event->setEventProcessed(); + // } } /** * Request BALSA database process the file that was uploaded. * + * @param sceneFile + * Scene file that was uploaded. * @param processUploadURL * URL for uploading file. * @param httpContentTypeName * Type of content for upload (eg: application/zip, see http://www.freeformatter.com/mime-types-list.html) - * @param responseContentOut - * If successful, contains the response content received after successful upload. + * @param updateSceneIDsFromResponseFlag + * If true, update the Scene IDs using those in the response content. * @param errorMessageOut * Contains error information if upload failed. * @return * True if upload is successful, else false. */ bool -BalsaDatabaseManager::processUploadedFile(const AString& processUploadURL, - const AString& httpContentTypeName, - AString& responseContentOut, - AString& errorMessageOut) +BalsaDatabaseManager::processUploadedFile(SceneFile* sceneFile, + const AString& processUploadURL, + const AString& httpContentTypeName, + const bool updateSceneIDsFromResponseFlag, + AString& errorMessageOut) { - responseContentOut.clear(); errorMessageOut.clear(); if (httpContentTypeName.isEmpty()) { @@ -478,6 +600,8 @@ BalsaDatabaseManager::processUploadedFile(const AString& processUploadURL, httpContentTypeName)); uploadRequest.m_headers.insert(std::make_pair("Cookie", getJSessionIdCookie())); + uploadRequest.m_arguments.push_back(std::make_pair("type", + "json")); CaretHttpResponse uploadResponse; CaretHttpManager::httpRequest(uploadRequest, uploadResponse); @@ -486,42 +610,682 @@ BalsaDatabaseManager::processUploadedFile(const AString& processUploadURL, std::cout << "Process upload response Code: " << uploadResponse.m_responseCode << std::endl; } - for (std::map::iterator mapIter = uploadResponse.m_headers.begin(); - mapIter != uploadResponse.m_headers.end(); - mapIter++) { + if (uploadResponse.m_responseCode != 200) { + errorMessageOut = ("Process Upload failed code: " + + QString::number(uploadResponse.m_responseCode) + + "\n"); + + return false; + } + + /* + * As of 24 Oct 2017, Scene IDs are updated prior to uploading scene fiel + */ + if (updateSceneIDsFromResponseFlag) { + for (std::map::iterator mapIter = uploadResponse.m_headers.begin(); + mapIter != uploadResponse.m_headers.end(); + mapIter++) { + if (m_debugFlag) { + std::cout << " Process Upload Response Header: " << qPrintable(mapIter->first) + << " -> " << qPrintable(mapIter->second) << std::endl; + } + } + + uploadResponse.m_body.push_back('\0'); + AString responseContent(&uploadResponse.m_body[0]); if (m_debugFlag) { - std::cout << " Process Upload Response Header: " << qPrintable(mapIter->first) - << " -> " << qPrintable(mapIter->second) << std::endl; + std::cout << "Process Upload to BALSA reply body: " << responseContent << std::endl; + } + else { + CaretLogFine("Process Upload to BALSA reply body: " + + responseContent); + } + + + AString contentType = getHeaderValue(uploadResponse, "Content-Type"); + if (contentType.isNull()) { + errorMessageOut = ("Process Upload Content type returned by BALSA is unknown"); + return false; + } + + if ( ! contentType.toLower().startsWith("application/json")) { + errorMessageOut = ("Process Upload Content type return by BALSA should be JSON format but is " + + contentType + + "Scene File was uploaded but Scene IDs will not be updated"); + return false; + } + + if ( ! updateSceneIdsFromProcessUploadResponse(sceneFile, + responseContent, + errorMessageOut)) { + return false; } } - uploadResponse.m_body.push_back('\0'); - responseContentOut.append(&uploadResponse.m_body[0]); - CaretLogFine("Process Upload to BALSA reply body: " - + responseContentOut); + return true; +} + +/** + * Get the Scene IDs from the JSON content returned by + * the process upload command and update the scene file. + * + * @param sceneFile + * Scene file that was uploaded. + * @param jsonContent + * The JSON content. + * @param errorMessage + * Contains error information if there is a problem. + * @return + * true if successful, else false. + */ +bool +BalsaDatabaseManager::updateSceneIdsFromProcessUploadResponse(SceneFile* sceneFile, + const AString& jsonContent, + AString& errorMessageOut) +{ + QJsonParseError jsonError; + QJsonDocument jsonDocument = QJsonDocument::fromJson(jsonContent.toLatin1(), + &jsonError); + if (jsonDocument.isNull()) { + errorMessageOut = ("Failed to parse JSON response from procssess upload, error:" + + jsonError.errorString()); + return false; + } + if ( ! jsonDocument.isArray()) { + errorMessageOut = ("Unrecognized format of JSON response from process upload (not an array)"); + return false; + } + const AString mySceneFileName = sceneFile->getFileNameNoPath(); - if (uploadResponse.m_responseCode == 200) { - return true; + QJsonArray jsonArray = jsonDocument.array(); + const int32_t numFileAndSceneIds = jsonArray.count(); + if (numFileAndSceneIds <= 0) { + errorMessageOut = ("No Scene IDs were returned by BALSA. Content returned \"" + + JsonHelper::jsonArrayToString(jsonArray) + + "\""); + return false; + } + for (int32_t j = 0; j < numFileAndSceneIds; j++) { + SceneFileIdentifiers sceneFileIDs(m_debugFlag, + jsonArray.at(j)); + if ( ! sceneFileIDs.isValid()) { + errorMessageOut = ("Scene File IDs error: " + + sceneFileIDs.getErrorMessage()); + return false; + } + + if (sceneFileIDs.m_sceneFileName == mySceneFileName) { + for (const auto& indexAndID : sceneFileIDs.m_sceneIndexAndIDsMap) { + const int32_t sceneIndex = indexAndID.first; + const AString sceneID = indexAndID.second; + + if ((sceneIndex >= 0) + && (sceneIndex < sceneFile->getNumberOfScenes())) { + Scene* scene = sceneFile->getSceneAtIndex(sceneIndex); + CaretAssert(scene); + scene->setBalsaSceneID(sceneID); + } + else { + errorMessageOut = ("Invalid Scene Index from BALSA Scene IDs: " + + AString::number(sceneIndex)); + return false; + } + } + } + } + + return true; +} + +/** + * Get the value for the given header name using + * a case-insensitive string comparison. + * + * @param httpResponse + * The HTTP response. + * @param headerName + * The header name. + * @return + * Value for header name. If the header name is + * not found the returned string will be NULL + * (test with .isNull()). + */ +AString +BalsaDatabaseManager::getHeaderValue(const CaretHttpResponse& httpResponse, + const AString& headerName) const +{ + /* + * NULL string + */ + AString value; + CaretAssert(value.isNull()); + + const QString headerNameLower(headerName.toLower()); + + + for (auto iter : httpResponse.m_headers) { + if (iter.first.toLower() == headerNameLower) { + value = iter.second; + break; + } + } + + return value; +} + +/** + * Add Scene IDs to scenes in the given scene file that are missing Scene IDs + * + * @param sceneFile + * The scene file. + * @param errorMessageOut + * Output with error message + * @return + * True if success, else false. + */ +bool +BalsaDatabaseManager::updateSceneIDs(SceneFile* sceneFile, + AString& errorMessageOut) +{ + CaretAssert(sceneFile); + + std::vector scenesMissingID; + + const int32_t numScenes = sceneFile->getNumberOfScenes(); + for (int32_t i = 0; i < numScenes; i++) { + Scene* scene = sceneFile->getSceneAtIndex(i); + CaretAssert(scene); + if (scene->getBalsaSceneID().trimmed().isEmpty()) { + scenesMissingID.push_back(scene); + } + } + + const int32_t numberOfIDs = static_cast(scenesMissingID.size()); + if (numberOfIDs > 0) { + std::vector sceneIDs; + if ( ! getSceneIDs(numberOfIDs, + sceneIDs, + errorMessageOut)) { + return false; + } + + CaretAssert(scenesMissingID.size() == sceneIDs.size()); + + for (int32_t i = 0; i < numberOfIDs; i++) { + scenesMissingID[i]->setBalsaSceneID(sceneIDs[i]); + } + } + + return true; +} + + +/** + * Request the given number of scene IDs + * + * @param numberOfSceneIDs + * Number of scene IDs requested + * @param sceneIDsOut + * Output with requested number of Scene IDs + * @param errorMessageOut + * Output with error message + * @return + * True if success, else false. + */ +bool +BalsaDatabaseManager::getSceneIDs(const int32_t numberOfSceneIDs, + std::vector& sceneIDsOut, + AString& errorMessageOut) +{ + errorMessageOut.clear(); + sceneIDsOut.clear(); + + if (numberOfSceneIDs <= 0) { + errorMessageOut = "Zero or fewer Study IDs requested"; + return false; + } + + const AString studyIdURL(m_databaseURL + + "/scene/newIds/" + + AString::number(numberOfSceneIDs)); + + errorMessageOut.clear(); + + CaretHttpRequest caretRequest; + caretRequest.m_method = CaretHttpManager::POST_ARGUMENTS; + caretRequest.m_url = studyIdURL; + caretRequest.m_headers.insert(std::make_pair("Content-Type", + "application/x-www-form-urlencoded")); + caretRequest.m_headers.insert(std::make_pair("Cookie", + getJSessionIdCookie())); + caretRequest.m_arguments.push_back(std::make_pair("type", + "json")); + + CaretHttpResponse response; + CaretHttpManager::httpRequest(caretRequest, response); + + if (m_debugFlag) { + std::cout << "Request Scene IDs response Code: " << response.m_responseCode << std::endl; + } + + if (response.m_responseCode != 200) { + errorMessageOut = ("Requesting Scene IDs failed with HTTP code=" + + AString::number(response.m_responseCode) + + ". This error may be caused by failure to agree to data use terms."); + return false; + } + + response.m_body.push_back('\0'); + AString responseContent(&response.m_body[0]); + + if (m_debugFlag) { + std::cout << "Request Scene IDs body:\n" << responseContent << std::endl << std::endl; + } + + AString contentType = getHeaderValue(response, "Content-Type"); + if (contentType.isNull()) { + errorMessageOut = ("Requesting Scene IDs failed. Content type returned by BALSA is unknown"); + return false; + } + + if (contentType.toLower().startsWith("text/html")) { + /* + * Response from BALSA has Scene IDs separated by '
' + * Example: vp5B
Z02A
+ */ + QStringList sl = responseContent.split("
", + QString::SkipEmptyParts, + Qt::CaseInsensitive); + const int32_t receivedCount = sl.count(); + if (receivedCount != numberOfSceneIDs) { + errorMessageOut = ("Requested " + + AString::number(numberOfSceneIDs) + + " but received " + + receivedCount + + " IDs"); + return false; + } + for (int32_t i = 0; i < receivedCount; i++) { + sceneIDsOut.push_back(sl.at(i)); + } + } + else if (contentType.toLower().startsWith("application/json")) { + errorMessageOut = ("Requesting Scene IDS failed. Content type returned by BALSA is JSON but support for JSON has not been implemented in Workbench"); + return false; + } + else { + errorMessageOut = ("Requesting Scene IDS failed. Content type returned by BALSA should be either text/html or JSON format but is " + + contentType); + return false; } - errorMessageOut = ("Process Upload failed code: " - + QString::number(uploadResponse.m_responseCode) - + "\n" - + responseContentOut); + return true; +} + + +/** + * Get the study ID from a study Title + * + * @param databaseURL + * URL for database + * @param studyTitle + * Title of the study + * @param studyIDOut + * Output with the study ID + * @param errorMessageOut + * Output with error message + * @return + * True if success, else false. + */ +bool +BalsaDatabaseManager::requestStudyID(const AString& databaseURL, + const AString& studyTitle, + AString& studyIDOut, + AString& errorMessageOut) +{ + const AString studyIdURL(databaseURL + + "/study/save"); + + errorMessageOut.clear(); + + CaretHttpRequest caretRequest; + caretRequest.m_method = CaretHttpManager::POST_ARGUMENTS; + caretRequest.m_url = studyIdURL; + caretRequest.m_headers.insert(std::make_pair("Content-Type", + "application/x-www-form-urlencoded")); + caretRequest.m_headers.insert(std::make_pair("Cookie", + getJSessionIdCookie())); + caretRequest.m_arguments.push_back(std::make_pair("title", + studyTitle)); + caretRequest.m_arguments.push_back(std::make_pair("type", + "json")); + + CaretHttpResponse studyResponse; + CaretHttpManager::httpRequest(caretRequest, studyResponse); + + if (m_debugFlag) { + std::cout << "Request study ID response Code: " << studyResponse.m_responseCode << std::endl; + } + + if (studyResponse.m_responseCode != 200) { + errorMessageOut = ("Requesting study ID failed with HTTP code=" + + AString::number(studyResponse.m_responseCode) + + ". This error may be caused by failure to agree to data use terms."); + return false; + } + + AString contentType = getHeaderValue(studyResponse, "Content-Type"); + if (contentType.isNull()) { + errorMessageOut = ("Requesting study ID failed. Content type returned by BALSA is unknown"); + return false; + } + + if ( ! contentType.toLower().startsWith("application/json")) { + errorMessageOut = ("Requesting study ID failed. Content type return by BALSA should be JSON format but is " + + contentType); + return false; + } + + studyResponse.m_body.push_back('\0'); + AString responseContent(&studyResponse.m_body[0]); + + if (m_debugFlag) { + std::cout << "Request study ID body:\n" << responseContent << std::endl << std::endl; + } + + QJsonParseError jsonError; + QJsonDocument jsonDocument = QJsonDocument::fromJson(responseContent.toLatin1(), + &jsonError); + if (jsonDocument.isNull()) { + errorMessageOut = ("Requesting study ID failed. Failed to parse JSON, error:" + + jsonError.errorString()); + return false; + } + + QByteArray json = jsonDocument.toJson(); + if (m_debugFlag) { + std::cout << "Array?: " << jsonDocument.isArray() << std::endl; + std::cout << "Empty?: " << jsonDocument.isEmpty() << std::endl; + std::cout << "NULL?: " << jsonDocument.isNull() << std::endl; + std::cout << "Object?: " << jsonDocument.isObject() << std::endl; + std::cout << "JSON length: " << json.size() << std::endl; + AString str(json); + std::cout << ("Formatted JSON:\n" + str) << std::endl; + } + + if ( ! jsonDocument.isObject()) { + errorMessageOut = ("Requesting study ID failed. JSON content is not an object." + "JSON is displayed in terminal if logging level is warning or higher."); + CaretLogWarning("Study ID JSON is not Object\n" + + AString(jsonDocument.toJson())); + return false; + } + + BalsaStudyInformation bsi(jsonDocument.object()); + if ( ! bsi.getStudyID().isEmpty()) { + studyIDOut = bsi.getStudyID(); + return true; + } return false; } +/** + * Login to BALSA and request a study ID from a study title. + * + * @param studyTitle + * The study's titlee. + * @param studyIdOut + * Output containing the study ID. + * @param errorMessageOut + * Contains description of any error(s). + * @return + * True if processing was successful, else false. + */ +bool +BalsaDatabaseManager::getStudyIDFromStudyTitle(const AString& studyTitle, + AString& studyIdOut, + AString& errorMessageOut) +{ + if (studyTitle.isEmpty()) { + errorMessageOut = "The study title is empty."; + return false; + } + + if ( ! requestStudyID(m_databaseURL, + studyTitle, + studyIdOut, + errorMessageOut)) { + return false; + } + + return true; +} + +/** + * Get the User's Roles. + * + * @param userRolesOut + * Output with user's roles. + * @param errorMessageOut + * Contains description of any error(s). + * @return + * True if processing was successful, else false. + */ +bool +BalsaDatabaseManager::getUserRoles(BalsaUserRoles& userRolesOut, + AString& errorMessageOut) +{ + userRolesOut.resetToAllInvalid(); + + const AString studyIdURL(m_databaseURL + + "/user/roles"); + + errorMessageOut.clear(); + + CaretHttpRequest caretRequest; + caretRequest.m_method = CaretHttpManager::POST_ARGUMENTS; + caretRequest.m_url = studyIdURL; + caretRequest.m_headers.insert(std::make_pair("Content-Type", + "application/x-www-form-urlencoded")); + caretRequest.m_headers.insert(std::make_pair("Cookie", + getJSessionIdCookie())); + caretRequest.m_arguments.push_back(std::make_pair("type", + "json")); + + CaretHttpResponse studyResponse; + CaretHttpManager::httpRequest(caretRequest, studyResponse); + + if (m_debugFlag) { + std::cout << "Request rolese response Code: " << studyResponse.m_responseCode << std::endl; + } + + if (studyResponse.m_responseCode != 200) { + errorMessageOut = ("Requesting roles failed with HTTP code=" + + AString::number(studyResponse.m_responseCode)); + return false; + } + + AString contentType = getHeaderValue(studyResponse, "Content-Type"); + if (contentType.isNull()) { + errorMessageOut = ("Requesting roles failed. Content type returned by BALSA is unknown"); + return false; + } + + if ( ! contentType.toLower().startsWith("application/json")) { + errorMessageOut = ("Requesting rolesfailed. Content type return by BALSA should be JSON format but is " + + contentType); + return false; + } + + studyResponse.m_body.push_back('\0'); + AString responseContent(&studyResponse.m_body[0]); + + if (m_debugFlag) { + std::cout << "Request roles body:\n" << responseContent << std::endl << std::endl; + } + + QJsonParseError jsonError; + QJsonDocument jsonDocument = QJsonDocument::fromJson(responseContent.toLatin1(), + &jsonError); + if (jsonDocument.isNull()) { + errorMessageOut = ("Requesting roles failed. Failed to parse JSON, error:" + + jsonError.errorString()); + return false; + } + + QByteArray json = jsonDocument.toJson(); + if (m_debugFlag) { + std::cout << "Array?: " << jsonDocument.isArray() << std::endl; + std::cout << "Empty?: " << jsonDocument.isEmpty() << std::endl; + std::cout << "NULL?: " << jsonDocument.isNull() << std::endl; + std::cout << "Object?: " << jsonDocument.isObject() << std::endl; + std::cout << "JSON length: " << json.size() << std::endl; + AString str(json); + std::cout << ("Formatted JSON:\n" + str) << std::endl; + } + + if ( ! jsonDocument.isArray()) { + errorMessageOut = ("Requesting roles failed. JSON content is not an array." + "JSON is displayed in terminal if logging level is warning or higher."); + CaretLogWarning("Roles JSON is not Array\n" + + AString(jsonDocument.toJson())); + return false; + } + + userRolesOut.parseJson(jsonDocument.array()); + + if (m_debugFlag) { + std::cout << "**** Roles: " << userRolesOut.toString() << std::endl; + } + + return userRolesOut.isValid(); +} + + +/** + * Request study information. + * + * @param studyInformationOut + * Output containing information for all of the user's studies. + * @param errorMessageOut + * Contains description of any error(s). + * @return + * True if processing was successful, else false. + */ +bool +BalsaDatabaseManager::getAllStudyInformation(std::vector& studyInformationOut, + AString& errorMessageOut) +{ + studyInformationOut.clear(); + errorMessageOut = ""; + + const AString studyIdURL(m_databaseURL + + "/study/mine"); + + errorMessageOut.clear(); + + CaretHttpRequest caretRequest; + caretRequest.m_method = CaretHttpManager::POST_ARGUMENTS; + caretRequest.m_url = studyIdURL; + caretRequest.m_headers.insert(std::make_pair("Content-Type", + "application/x-www-form-urlencoded")); + caretRequest.m_headers.insert(std::make_pair("Cookie", + getJSessionIdCookie())); + caretRequest.m_arguments.push_back(std::make_pair("type", + "json")); + + CaretHttpResponse idResponse; + CaretHttpManager::httpRequest(caretRequest, idResponse); + + if (m_debugFlag) { + std::cout << "Request all studies response Code: " << idResponse.m_responseCode << std::endl; + } + + if (idResponse.m_responseCode != 200) { + errorMessageOut = ("Requesting all study information failed with HTTP code=" + + AString::number(idResponse.m_responseCode) + + ". This error may be caused by failure to agree to data use terms."); + return false; + } + + AString contentType = getHeaderValue(idResponse, "Content-Type"); + if (contentType.isNull()) { + errorMessageOut = ("Requesting all study information failed. Content type returned by BALSA is unknown"); + return false; + } + + if ( ! contentType.toLower().startsWith("application/json")) { + errorMessageOut = ("Requesting all study information failed. Content type return by BALSA should be JSON format but is " + + contentType); + return false; + } + + idResponse.m_body.push_back('\0'); + AString responseContent(&idResponse.m_body[0]); + + if (m_debugFlag) { + std::cout << "Request all studies reply body:\n" << responseContent << std::endl << std::endl; + } + + QJsonParseError jsonError; + QJsonDocument jsonDocument = QJsonDocument::fromJson(responseContent.toLatin1(), + &jsonError); + if (jsonDocument.isNull()) { + errorMessageOut = ("Requesting all study information failed. Failed to parse JSON, error:" + + jsonError.errorString()); + return false; + } + QByteArray json = jsonDocument.toJson(); + + if (m_debugFlag) { + std::cout << "Array?: " << jsonDocument.isArray() << std::endl; + std::cout << "Empty?: " << jsonDocument.isEmpty() << std::endl; + std::cout << "NULL?: " << jsonDocument.isNull() << std::endl; + std::cout << "Object?: " << jsonDocument.isObject() << std::endl; + std::cout << "JSON length: " << json.size() << std::endl; + AString str(json); + std::cout << ("Formatted JSON:\n" + str) << std::endl; + } + + if ( ! jsonDocument.isArray()) { + errorMessageOut = ("Requesting all study information failed. JSON content is not an array." + "JSON is displayed in terminal if logging level is warning or higher."); + CaretLogWarning("Study Information JSON is not Array\n" + + AString(jsonDocument.toJson())); + return false; + } + + QJsonArray jsonArray = jsonDocument.array(); + for (QJsonArray::iterator iter = jsonArray.begin(); + iter != jsonArray.end(); + iter++) { + QJsonValue jsonValue = *iter; + if ( ! jsonValue.isNull()) { + if (jsonValue.isObject()) { + QJsonObject studyInfo = jsonValue.toObject(); + + BalsaStudyInformation bsi(studyInfo); + if ( ! bsi.isEmpty()) { + studyInformationOut.push_back(bsi); + } + } + } + } + + if ( ! studyInformationOut.empty()) { + std::sort(studyInformationOut.begin(), + studyInformationOut.end()); + } + + return true; +} + + /** * Login to BALSA, zip the scene and data files, and upload the * ZIP file to BALSA. * - * @param databaseURL - * URL of the database. - * @param username - * Username for logging in. - * @param password - * Password for logging in. * @param sceneFile * Name of scene file. * @param zipFileName @@ -534,13 +1298,10 @@ BalsaDatabaseManager::processUploadedFile(const AString& processUploadURL, * True if processing was successful, else false. */ bool -BalsaDatabaseManager::uploadZippedSceneFile(const AString& databaseURL, - const AString& username, - const AString& password, - const SceneFile* sceneFile, - const AString& zipFileName, - const AString& extractToDirectoryName, - AString& errorMessageOut) +BalsaDatabaseManager::uploadZippedSceneFile(SceneFile* sceneFile, + const AString& zipFileName, + const AString& extractToDirectoryName, + AString& errorMessageOut) { errorMessageOut.clear(); @@ -590,22 +1351,6 @@ BalsaDatabaseManager::uploadZippedSceneFile(const AString& databaseURL, "Logging in..."); EventManager::get()->sendEvent(progressUpdate.getPointer()); - /* - * Login - */ - const AString loginURL(databaseURL - + "/j_spring_security_check"); - if ( ! login(loginURL, - username, - password, - errorMessageOut)) { - - return false; - } - - CaretLogInfo("SESSION ID from BALSA Login: " - + getJSessionIdCookie()); - progressUpdate.setProgress(PROGRESS_ZIPPING, "Zipping Scene and Data Files"); EventManager::get()->sendEvent(progressUpdate.getPointer()); @@ -619,7 +1364,7 @@ BalsaDatabaseManager::uploadZippedSceneFile(const AString& databaseURL, return false; } - if (m_debugFlag) std::cout << "Zip file has been created " << std::endl; + if (m_debugFlag) std::cout << "Zip file " << zipFileName << " has been created " << std::endl; progressUpdate.setProgress(PROGRESS_UPLOAD, "Uploading zip file"); EventManager::get()->sendEvent(progressUpdate.getPointer()); @@ -628,7 +1373,7 @@ BalsaDatabaseManager::uploadZippedSceneFile(const AString& databaseURL, * Upload the ZIP file */ AString uploadResultText; - const AString uploadURL(databaseURL + const AString uploadURL(m_databaseURL + "/study/handleUpload/" + sceneFile->getBalsaStudyID()); const bool uploadSuccessFlag = uploadFile(uploadURL, @@ -648,16 +1393,16 @@ BalsaDatabaseManager::uploadZippedSceneFile(const AString& databaseURL, /* * Process the uploaded file */ - progressUpdate.setProgress(PROGRESS_PROCESS_UPLOAD, "Processing uploaded zip file"); + progressUpdate.setProgress(PROGRESS_PROCESS_UPLOAD, "Processing uploaded zip file (this step may take a long time)"); EventManager::get()->sendEvent(progressUpdate.getPointer()); - const AString processUploadURL(databaseURL + const AString processUploadURL(m_databaseURL + "/study/processUpload/" + sceneFile->getBalsaStudyID()); - AString processUploadResultText; - const bool processUploadSuccessFlag = processUploadedFile(processUploadURL, + const bool processUploadSuccessFlag = processUploadedFile(sceneFile, + processUploadURL, "application/x-www-form-urlencoded", - processUploadResultText, + false, // do not look for Scene IDs in response content errorMessageOut); if (m_debugFlag) std::cout << "Result of processing the uploaded ZIP file" << AString::fromBool(processUploadSuccessFlag) << std::endl; @@ -678,7 +1423,133 @@ BalsaDatabaseManager::uploadZippedSceneFile(const AString& databaseURL, progressUpdate.setProgress(PROGRESS_DONE, "Finished."); EventManager::get()->sendEvent(progressUpdate.getPointer()); + return true; } +/** + * Constructor that parses JSON containing scene file name and identifiers. + * + * Example: ["FileName.scene",[[1,"55XX"],[0,"n51z"]]] + * + * @param debugFlag + * Debugging flag + * @param jsonValue + * The JSON value containing the array. + */ +BalsaDatabaseManager::SceneFileIdentifiers::SceneFileIdentifiers(const bool debugFlag, + const QJsonValue& jsonValue) +: m_debugFlag(debugFlag) +{ + if ( ! jsonValue.isArray()) { + m_errorMessage = ("Scene File Name/Identifier is not JSON array \"" + + JsonHelper::jsonValueToString(jsonValue) + + "\""); + return; + } + + if (m_debugFlag) { + std::cout << "Scene IDs Content: " << JsonHelper::jsonValueToString(jsonValue) << std::endl; + } + + + const QJsonArray jsonArray = jsonValue.toArray(); + if (jsonArray.count() != 2) { + m_errorMessage = ("Scene File Name/Identifier array should have two elements \"" + + JsonHelper::jsonValueToString(jsonValue) + + "\""); + return; + } + + const QJsonValue sceneFileNameJsonValue = jsonArray.at(0); + if ( ! sceneFileNameJsonValue.isString()) { + m_errorMessage = ("Scene File Name is not a string \"" + + JsonHelper::jsonValueToString(sceneFileNameJsonValue) + + "\""); + return; + } + m_sceneFileName = sceneFileNameJsonValue.toString(); + + const QJsonValue sceneIDsJsonValue = jsonArray.at(1); + if ( ! sceneIDsJsonValue.isArray()) { + m_errorMessage = ("Scene IDs is not an array \"" + + JsonHelper::jsonValueToString(sceneFileNameJsonValue) + + "\""); + + return; + } + + const QJsonArray sceneIDsArray = sceneIDsJsonValue.toArray(); + const int numSceneIDs = sceneIDsArray.count(); + for (int32_t i = 0; i < numSceneIDs; i++) { + const QJsonValue sceneIDJsonElementValue = sceneIDsArray.at(i); + if (m_debugFlag) { + std::cout << "Scene Index/ID: " << i << " " << JsonHelper::jsonValueToString(sceneIDJsonElementValue) << std::endl; + } + + if ( ! sceneIDJsonElementValue.isArray()) { + m_errorMessage = ("Scene Index/ID is not an array \"" + + JsonHelper::jsonValueToString(sceneIDJsonElementValue) + + "\""); + + return; + } + + const QJsonArray sceneIDJsonArray = sceneIDJsonElementValue.toArray(); + if (sceneIDJsonArray.count() != 2) { + m_errorMessage = ("Scene Index/ID array should have two elements \"" + + JsonHelper::jsonValueToString(sceneIDJsonArray) + + "\""); + return; + } + + const QJsonValue sceneIndexValue = sceneIDJsonArray.at(0); + if ( ! sceneIndexValue.isDouble()) { + m_errorMessage = ("Scene Index (first element) is not a number \"" + + JsonHelper::jsonValueToString(sceneIndexValue) + + "\""); + return; + } + + const QJsonValue sceneIDValue = sceneIDJsonArray.at(1); + if ( ! sceneIDValue.isString()) { + m_errorMessage = ("Scene ID (second element) is not a string \"" + + JsonHelper::jsonValueToString(sceneIDJsonArray) + + "\""); + return; + } + + const int32_t invalidIndex = -1; + const int32_t sceneIndex = sceneIndexValue.toInt(invalidIndex); + if (sceneIndex == invalidIndex) { + m_errorMessage = ("Scene Index (first element) is not an integer \"" + + JsonHelper::jsonValueToString(sceneIndexValue) + + "\""); + return; + } + + const AString sceneID = sceneIDValue.toString(); + + m_sceneIndexAndIDsMap.insert(std::make_pair(sceneIndex, + sceneID)); + } +} + +/** + * @return True if parsing was valid. + */ +bool +BalsaDatabaseManager::SceneFileIdentifiers::isValid() const +{ + return (m_errorMessage.isEmpty()); +} + +/** + * @return Error message describing parsing problem. + */ +AString +BalsaDatabaseManager::SceneFileIdentifiers::getErrorMessage() const +{ + return m_errorMessage; +} diff --git a/src/GuiQt/BalsaDatabaseManager.h b/src/GuiQt/BalsaDatabaseManager.h index 104ca6644dd1c5db51a40d457f455daeafe4aa3e..fd9a10cb4dfb9c92904b3a35a7dc95db259ab547 100644 --- a/src/GuiQt/BalsaDatabaseManager.h +++ b/src/GuiQt/BalsaDatabaseManager.h @@ -21,12 +21,18 @@ */ /*LICENSE_END*/ -#include "CaretObject.h" +#include +#include "BalsaStudyInformation.h" +#include "CaretObject.h" #include "EventListenerInterface.h" +class QJsonValue; + namespace caret { + class BalsaUserRoles; + struct CaretHttpResponse; class SceneFile; class BalsaDatabaseManager : public CaretObject, public EventListenerInterface { @@ -36,36 +42,39 @@ namespace caret { virtual ~BalsaDatabaseManager(); - bool uploadZippedSceneFile(const AString& databaseURL, + bool login(const AString& databaseURL, const AString& username, const AString& password, - const SceneFile* sceneFile, - const AString& zipFileName, - const AString& extractToDirectoryName, AString& errorMessageOut); - bool login(const AString& loginURL, - const AString& username, - const AString& password, - AString& errorMessageOut); - - AString getJSessionIdCookie() const; + void logout(); - bool uploadFile(const AString& uploadURL, - const AString& fileName, - const AString& httpContentTypeName, - AString& responseContentOut, - AString& errorMessageOut); + bool getAllStudyInformation(std::vector& studyInformationOut, + AString& errorMessageOut); - bool processUploadedFile(const AString& processUploadURL, - const AString& httpContentTypeName, - AString& responseContentOut, - AString& errorMessageOut); + bool getUserRoles(BalsaUserRoles& userRolesOut, + AString& errorMessageOut); + + bool getStudyIDFromStudyTitle(const AString& studyTitle, + AString& studyIdOut, + AString& errorMessageOut); + + bool getSceneIDs(const int32_t numberOfSceneIDs, + std::vector& sceneIDsOut, + AString& errorMessageOut); + + bool updateSceneIDs(SceneFile* sceneFile, + AString& errorMessageOut); + + bool uploadZippedSceneFile(SceneFile* sceneFile, + const AString& zipFileName, + const AString& extractToDirectoryName, + AString& errorMessageOut); static bool zipSceneAndDataFiles(const SceneFile* sceneFile, - const AString& extractDirectory, - const AString& zipFileName, - AString& errorMessageOut); + const AString& extractDirectory, + const AString& zipFileName, + AString& errorMessageOut); // ADD_NEW_METHODS_HERE @@ -74,27 +83,67 @@ namespace caret { virtual void receiveEvent(Event* event); private: + class SceneFileIdentifiers { + public: + SceneFileIdentifiers(const bool debugFlag, + const QJsonValue& jsonValue); + + bool isValid() const; + + AString getErrorMessage() const; + + const bool m_debugFlag; + + AString m_errorMessage; + + AString m_sceneFileName; + + std::map m_sceneIndexAndIDsMap; + }; + BalsaDatabaseManager(const BalsaDatabaseManager&); BalsaDatabaseManager& operator=(const BalsaDatabaseManager&); - bool uploadFileWithCaretHttpManager(const AString& uploadURL, + AString getJSessionIdCookie() const; + + bool uploadFile(const AString& uploadURL, const AString& fileName, const AString& httpContentTypeName, AString& responseContentOut, AString& errorMessageOut); - bool uploadFileWithHttpCommunicator(const AString& uploadURL, - const AString& fileName, - const AString& httpContentTypeName, - AString& responseContentOut, - AString& errorMessageOut); + bool processUploadedFile(SceneFile* sceneFile, + const AString& processUploadURL, + const AString& httpContentTypeName, + const bool updateSceneIDsFromResponseFlag, + AString& errorMessageOut); + + bool requestStudyID(const AString& databaseURL, + const AString& studyTitle, + AString& studyIDOut, + AString& errorMessageOut); + bool uploadFileWithCaretHttpManager(const AString& uploadURL, + const AString& fileName, + const AString& httpContentTypeName, + AString& responseContentOut, + AString& errorMessageOut); - bool processUploadResponse(const std::map& responseHeaders, + bool verifyUploadFileResponse(const std::map& responseHeaders, const AString& responseContent, const int32_t responseHttpCode, AString& errorMessageOut) const; + + AString getHeaderValue(const CaretHttpResponse& httpResponse, + const AString& headerName) const; + + bool updateSceneIdsFromProcessUploadResponse(SceneFile* sceneFile, + const AString& jsonContent, + AString& errorMessageOut); + + AString m_databaseURL; + AString m_username; AString m_password; diff --git a/src/GuiQt/BalsaDatabaseUploadSceneFileDialog.cxx b/src/GuiQt/BalsaDatabaseUploadSceneFileDialog.cxx index ccec6330b41175387c625aae408654b64e33398d..3ff8ee89fba94c3a4d6ca5e1324f41a4de770a1b 100644 --- a/src/GuiQt/BalsaDatabaseUploadSceneFileDialog.cxx +++ b/src/GuiQt/BalsaDatabaseUploadSceneFileDialog.cxx @@ -23,23 +23,45 @@ #include "BalsaDatabaseUploadSceneFileDialog.h" #undef __BALSA_DATABASE_UPLOAD_SCENE_FILE_DIALOG_DECLARE__ +#include +#include +#include +#include #include +#include #include #include +#include #include #include +#include +#include +#include +#include +#include #include "BalsaDatabaseManager.h" +#include "BalsaStudySelectionDialog.h" +#include "BalsaUserRoles.h" +#include "Brain.h" #include "CaretAssert.h" +#include "CaretFileDialog.h" #include "CaretLogger.h" #include "CaretPointer.h" +#include "CaretPreferences.h" #include "CursorDisplayScoped.h" +#include "DataFileException.h" #include "FileInformation.h" +#include "GuiManager.h" #include "ProgressReportingDialog.h" +#include "SceneBasePathWidget.h" #include "SceneFile.h" +#include "SessionManager.h" #include "SystemUtilities.h" +#include "WuQDataEntryDialog.h" #include "WuQMessageBox.h" #include "WuQtUtilities.h" +#include "WuQWidgetDisabler.h" using namespace caret; @@ -59,109 +81,448 @@ using namespace caret; * @param parent * Parent of this dialog. */ -BalsaDatabaseUploadSceneFileDialog::BalsaDatabaseUploadSceneFileDialog(const SceneFile* sceneFile, +BalsaDatabaseUploadSceneFileDialog::BalsaDatabaseUploadSceneFileDialog(SceneFile* sceneFile, QWidget* parent) : WuQDialogModal("Upload Scene File to BALSA", parent), m_sceneFile(sceneFile) { - QString defaultUserName = "balsaTest"; - QString defaultPassword = "@2password"; + CaretAssert(m_sceneFile); -#ifdef NDEBUG - defaultUserName = ""; - defaultPassword = ""; -#endif + if (s_username.isEmpty()) { + s_username = SessionManager::get()->getCaretPreferences()->getBalsaUserName(); + } - QLabel* databaseNameLabel = new QLabel("DataBase: "); - m_databaseComboBox = new QComboBox(); - m_databaseComboBox->setEditable(true); - m_databaseComboBox->addItem("https://balsa.wustl.edu"); - m_databaseComboBox->addItem("http://johnsdev.wustl.edu:8080"); - m_databaseComboBox->addItem("https://johnsdev.wustl.edu:8080"); + m_userRoles.reset(new BalsaUserRoles); + + QWidget* loginWidget = createLoginWidget(); + m_uploadWidget = createTabWidget(); + + QWidget* dialogWidget = new QWidget(); + QVBoxLayout* dialogLayout = new QVBoxLayout(dialogWidget); + dialogLayout->addWidget(loginWidget); + dialogLayout->addWidget(m_uploadWidget); + + setCentralWidget(dialogWidget, + WuQDialogModal::SCROLL_AREA_NEVER); + + loginInformationChanged(); + + setSizePolicy(sizePolicy().horizontalPolicy(), + QSizePolicy::Fixed); + + setOkButtonText("Upload"); + setCancelButtonText("Close"); + + m_basePathWidget->updateWithSceneFile(m_sceneFile); + updateUserRolesLabel(); +} + +/** + * Destructor. + */ +BalsaDatabaseUploadSceneFileDialog::~BalsaDatabaseUploadSceneFileDialog() +{ +} + +/** + * Called when the Cancel button (we have changed text + * to Close) is clicked. + */ +void +BalsaDatabaseUploadSceneFileDialog::cancelButtonClicked() +{ + s_username = m_usernameLineEdit->text(); + s_password = m_passwordLineEdit->text(); + + /* + * Will logout of database and disable part of dialog + */ + loginInformationChanged(); + + WuQDialogModal::cancelButtonClicked(); +} + + +/** + * @return The login widget + */ +QWidget* +BalsaDatabaseUploadSceneFileDialog::createLoginWidget() +{ + /* + * Create the BALSA database manager + */ + m_balsaDatabaseManager = std::unique_ptr(new BalsaDatabaseManager()); const int minimumLineEditWidth = 250; - QLabel* usernameLabel = new QLabel("Username: "); + /* + * Username + */ + m_usernameLabel = new QLabel(""); m_usernameLineEdit = new QLineEdit(); m_usernameLineEdit->setMinimumWidth(minimumLineEditWidth); - m_usernameLineEdit->setText(defaultUserName); + m_usernameLineEdit->setText(s_username); + m_usernameLineEdit->setValidator(createValidator(LabelName::LABEL_USERNAME)); + QObject::connect(m_usernameLineEdit, &QLineEdit::textEdited, + this, [=] { this->loginInformationChanged(); }); + QObject::connect(m_usernameLineEdit, &QLineEdit::returnPressed, + this, &BalsaDatabaseUploadSceneFileDialog::returnPressedUsernameOrPassword); - QLabel* passwordLabel = new QLabel("Password: "); + /* + * Password + */ + m_passwordLabel = new QLabel(""); m_passwordLineEdit = new QLineEdit(); m_passwordLineEdit->setMinimumWidth(minimumLineEditWidth); m_passwordLineEdit->setEchoMode(QLineEdit::Password); - m_passwordLineEdit->setText(defaultPassword); + m_passwordLineEdit->setText(s_password); + m_passwordLineEdit->setValidator(createValidator(LabelName::LABEL_PASSWORD)); + QObject::connect(m_passwordLineEdit, &QLineEdit::textEdited, + this, [=] { this->loginInformationChanged(); }); + QObject::connect(m_passwordLineEdit, &QLineEdit::returnPressed, + this, &BalsaDatabaseUploadSceneFileDialog::returnPressedUsernameOrPassword); + /* + * Forgot username label/link + */ QLabel* forgotUsernameLabel = new QLabel("" "Forgot Username" ""); QObject::connect(forgotUsernameLabel, SIGNAL(linkActivated(const QString&)), this, SLOT(labelHtmlLinkClicked(const QString&))); + /* + * Forgot password label/link + */ QLabel* forgotPasswordLabel = new QLabel("" "Forgot Password" ""); QObject::connect(forgotPasswordLabel, SIGNAL(linkActivated(const QString&)), this, SLOT(labelHtmlLinkClicked(const QString&))); + /* + * Register label/link + */ QLabel* registerLabel = new QLabel("" "Register" ""); QObject::connect(registerLabel, SIGNAL(linkActivated(const QString&)), this, SLOT(labelHtmlLinkClicked(const QString&))); + + /* + * Login push button + */ + m_loginPushButton = new QPushButton("Login"); + QObject::connect(m_loginPushButton, &QPushButton::clicked, + this, &BalsaDatabaseUploadSceneFileDialog::loginButtonClicked); + + /* + * Label for User Roles + */ + m_userRolesLabel = new QLabel(" "); + + int columnCounter = 0; + const int COLUMN_LABEL = columnCounter++; + const int COLUMN_DATA_WIDGET_ONE = columnCounter++; + const int COLUMN_DATA_WIDGET_TWO = columnCounter++; + const int COLUMN_BUTTON_ONE = columnCounter++; + const int COLUMN_BUTTON_TWO = columnCounter++; + + QGroupBox* groupBox = new QGroupBox("BALSA Login"); + QGridLayout* gridLayout = new QGridLayout(groupBox); + gridLayout->setSpacing(5); + gridLayout->setColumnStretch(COLUMN_LABEL, 0); + gridLayout->setColumnStretch(COLUMN_DATA_WIDGET_ONE, 0); + gridLayout->setColumnStretch(COLUMN_DATA_WIDGET_TWO, 100); + gridLayout->setColumnStretch(COLUMN_BUTTON_ONE, 0); + int row = 0; + gridLayout->addWidget(m_usernameLabel, row, COLUMN_LABEL, Qt::AlignRight); + gridLayout->addWidget(m_usernameLineEdit, row, COLUMN_DATA_WIDGET_ONE, 1, 2); + gridLayout->addWidget(registerLabel, row, COLUMN_BUTTON_ONE); + gridLayout->addWidget(forgotUsernameLabel, row, COLUMN_BUTTON_TWO); + row++; + gridLayout->addWidget(m_passwordLabel, row, COLUMN_LABEL, Qt::AlignRight); + gridLayout->addWidget(m_passwordLineEdit, row, COLUMN_DATA_WIDGET_ONE, 1, 2); + gridLayout->addWidget(forgotPasswordLabel, row, COLUMN_BUTTON_ONE); + row++; + gridLayout->setRowMinimumHeight(row, 10); // empty row + row++; + gridLayout->addWidget(m_loginPushButton, row, COLUMN_DATA_WIDGET_ONE); + gridLayout->addWidget(m_userRolesLabel, row, COLUMN_DATA_WIDGET_TWO, 1, Qt::AlignLeft); + + return groupBox; +} - const QString zipFileName = FileInformation::assembleFileComponents(SystemUtilities::getTempDirectory(), - sceneFile->getFileNameNoPathNoExtension() , - "zip"); +/** + * Called when login information (database, username, password) is changed + */ +void +BalsaDatabaseUploadSceneFileDialog::loginInformationChanged() +{ + m_balsaDatabaseManager->logout(); + setOkButtonEnabled(false); + m_selectStudyTitlePushButton->setEnabled(false); + m_userRoles->resetToAllInvalid(); + updateAllLabels(); +} - QLabel* zipFileNameLabel = new QLabel("Zip File Name"); - m_zipFileNameLineEdit = new QLineEdit; - m_zipFileNameLineEdit->setText(zipFileName); +/** + * @return The upload widget + */ +QWidget* +BalsaDatabaseUploadSceneFileDialog::createTabWidget() +{ + QTabWidget* tabWidget = new QTabWidget(); + tabWidget->addTab(createUploadTab(), "Upload"); + tabWidget->addTab(createAdvancedTab(), "Advanced"); - QLabel* extractDirectoryLabel = new QLabel("Extract to Directory"); + return tabWidget; +} + + +/** + * @return New instance of the upload tab. + */ +QWidget* +BalsaDatabaseUploadSceneFileDialog::createUploadTab() +{ + /* + * Extract to directory + */ + const AString defaultExtractDirectoryName = m_sceneFile->getDefaultExtractToDirectoryName(); + m_extractDirectoryNameLabel = new QLabel(""); m_extractDirectoryNameLineEdit = new QLineEdit(); m_extractDirectoryNameLineEdit->setText("ExtDir"); + m_extractDirectoryNameLineEdit->setValidator(createValidator(LabelName::LABEL_EXTRACT_DIRECTORY)); + m_extractDirectoryNameLineEdit->setToolTip("Directory that is created when user unzips the ZIP file"); + m_extractDirectoryNameLineEdit->setText(defaultExtractDirectoryName); + QObject::connect(m_extractDirectoryNameLineEdit, &QLineEdit::textEdited, + this, [=] { this->validateUploadData(); }); - const AString defaultDirName(SystemUtilities::getUserName() - + "_" - + m_sceneFile->getBalsaStudyID()); - m_extractDirectoryNameLineEdit->setText(defaultDirName); - QWidget* dialogWidget = new QWidget(); - QGridLayout* gridLayout = new QGridLayout(dialogWidget); - gridLayout->setColumnStretch(0, 0); - gridLayout->setColumnStretch(1, 100); + /* + * Scene BALSA Study ID + */ + m_balsaStudyIDLabel = new QLabel(""); + m_balsaStudyIDLineEdit = new QLineEdit(); + m_balsaStudyIDLineEdit->setReadOnly(true); + m_balsaStudyIDLineEdit->setToolTip("The Study ID is available from the BALSA Database; click the Get button to get a Study ID"); + m_balsaStudyIDLineEdit->setValidator(createValidator(LabelName::LABEL_STUDY_ID)); + m_balsaStudyIDLineEdit->setReadOnly(true); + QObject::connect(m_balsaStudyIDLineEdit, &QLineEdit::textEdited, + this, [=] { this->validateUploadData(); }); + + /* + * Scene BALSA Study Title + */ + m_balsaStudyTitleLabel = new QLabel(""); + m_balsaStudyTitleLineEdit = new QLineEdit(); + m_balsaStudyTitleLineEdit->setToolTip("Title for the study"); + m_balsaStudyTitleLineEdit->setValidator(createValidator(LabelName::LABEL_STUDY_TITLE)); + m_balsaStudyTitleLineEdit->setReadOnly(true); + QObject::connect(m_balsaStudyTitleLineEdit, &QLineEdit::textEdited, + this, [=] { this->validateUploadData(); }); + + /** + * Select Study Push Button + */ + m_selectStudyTitlePushButton = new QPushButton("Create/Select/Rename..."); + m_selectStudyTitlePushButton->setToolTip("Add new study, choose a study, or edit a study's title in BALSA Database"); + QObject::connect(m_selectStudyTitlePushButton, &QPushButton::clicked, + this, &BalsaDatabaseUploadSceneFileDialog::selectStudyTitleButtonClicked); + + /* + * Test button + */ + QPushButton* testPushButton = new QPushButton("Test..."); + testPushButton->setToolTip("Test some functionality"); + QObject::connect(testPushButton, &QPushButton::clicked, + this, &BalsaDatabaseUploadSceneFileDialog::testButtonClicked); + testPushButton->setVisible(false); + + /* + * Auto-save checkbox + */ + m_autoSaveSceneFileCheckBox = new QCheckBox("Auto-Save Scene File"); + m_autoSaveSceneFileCheckBox->setChecked(true); + QObject::connect(m_autoSaveSceneFileCheckBox, &QCheckBox::clicked, + this, &BalsaDatabaseUploadSceneFileDialog::autoSaveCheckBoxClicked); + + int columnCounter = 0; + const int COLUMN_LABEL = columnCounter++; + const int COLUMN_DATA_WIDGET = columnCounter++; + const int COLUMN_BUTTON_ONE = columnCounter++; + const int COLUMN_BUTTON_TWO = columnCounter++; + + QGridLayout* gridLayout = new QGridLayout(); + gridLayout->setSpacing(5); + gridLayout->setColumnMinimumWidth(COLUMN_DATA_WIDGET, 300); + gridLayout->setColumnStretch(COLUMN_LABEL, 0); + gridLayout->setColumnStretch(COLUMN_DATA_WIDGET, 100); + gridLayout->setColumnStretch(COLUMN_BUTTON_ONE, 0); + gridLayout->setColumnStretch(COLUMN_BUTTON_TWO, 0); int row = 0; - gridLayout->addWidget(databaseNameLabel, row, 0); - gridLayout->addWidget(m_databaseComboBox, row, 1); - gridLayout->addWidget(registerLabel, row, 2); + gridLayout->addWidget(m_balsaStudyTitleLabel, row, COLUMN_LABEL, Qt::AlignRight); + gridLayout->addWidget(m_balsaStudyTitleLineEdit, row, COLUMN_DATA_WIDGET); + gridLayout->addWidget(m_selectStudyTitlePushButton, row, COLUMN_BUTTON_ONE, 1, 2); row++; - gridLayout->addWidget(usernameLabel, row, 0); - gridLayout->addWidget(m_usernameLineEdit, row, 1); - gridLayout->addWidget(forgotUsernameLabel, row, 2); + gridLayout->addWidget(m_balsaStudyIDLabel, row, COLUMN_LABEL, Qt::AlignRight); + gridLayout->addWidget(m_balsaStudyIDLineEdit, row, COLUMN_DATA_WIDGET); + gridLayout->addWidget(testPushButton, row, COLUMN_BUTTON_ONE, 1, 2); row++; - gridLayout->addWidget(passwordLabel, row, 0); - gridLayout->addWidget(m_passwordLineEdit, row, 1); - gridLayout->addWidget(forgotPasswordLabel, row, 2); + gridLayout->addWidget(m_extractDirectoryNameLabel, row, COLUMN_LABEL, Qt::AlignRight); + gridLayout->addWidget(m_extractDirectoryNameLineEdit, row, COLUMN_DATA_WIDGET); row++; - gridLayout->addWidget(zipFileNameLabel, row, 0); - gridLayout->addWidget(m_zipFileNameLineEdit, row, 1, 1, 2); + gridLayout->addWidget(m_autoSaveSceneFileCheckBox, row, COLUMN_DATA_WIDGET, 1, 3, Qt::AlignLeft); row++; - gridLayout->addWidget(extractDirectoryLabel, row, 0); - gridLayout->addWidget(m_extractDirectoryNameLineEdit, row, 1, 1, 2); + gridLayout->setRowMinimumHeight(row, 15); // empty row row++; + m_balsaStudyIDLineEdit->setText(m_sceneFile->getBalsaStudyID()); + m_balsaStudyTitleLineEdit->setText(m_sceneFile->getBalsaStudyTitle()); + m_extractDirectoryNameLineEdit->setText(m_sceneFile->getBalsaExtractToDirectoryName()); - setCentralWidget(dialogWidget, - WuQDialogModal::SCROLL_AREA_NEVER); + if (m_extractDirectoryNameLineEdit->text().trimmed().isEmpty()) { + m_extractDirectoryNameLineEdit->setText(m_sceneFile->getDefaultExtractToDirectoryName()); + } + + QWidget* widget = new QWidget; + QVBoxLayout* layout = new QVBoxLayout(widget); + layout->addLayout(gridLayout); + layout->addStretch(); + return widget; } /** - * Destructor. + * @return A new BALSA Database selection widget */ -BalsaDatabaseUploadSceneFileDialog::~BalsaDatabaseUploadSceneFileDialog() +QWidget* +BalsaDatabaseUploadSceneFileDialog::createBalsaDatabaseSelectionWidget() +{ + QString hostName = SystemUtilities::getLocalHostName(); + const bool isWustlDomainFlag = hostName.endsWith(".wustl.edu"); + + /* + * Database selection + */ + m_databaseLabel = new QLabel(""); + m_databaseComboBox = new QComboBox(); + m_databaseComboBox->setEditable(true); + m_databaseComboBox->addItem("https://balsa.wustl.edu"); + if (isWustlDomainFlag) { + m_databaseComboBox->addItem("http://johnsdev.wustl.edu:8080"); + m_databaseComboBox->addItem("https://johnsdev.wustl.edu:8080"); + } + QObject::connect(m_databaseComboBox, static_cast(&QComboBox::activated), + this, [=] { this->loginInformationChanged(); }); + QObject::connect(m_databaseComboBox, &QComboBox::editTextChanged, + this, [=] { this->loginInformationChanged(); }); + + QPushButton* whatsThisPushButton = new QPushButton("What's this?"); + whatsThisPushButton->setSizePolicy(QSizePolicy::Fixed, whatsThisPushButton->sizePolicy().verticalPolicy()); + QObject::connect(whatsThisPushButton, &QPushButton::clicked, + this, &BalsaDatabaseUploadSceneFileDialog::whatsThisDatabase); + + int columnCounter = 0; + const int COLUMN_LABEL = columnCounter++; + const int COLUMN_WIDGET = columnCounter++; + + QGroupBox* groupBox = new QGroupBox("BALSA Database"); + QGridLayout* gridLayout = new QGridLayout(groupBox); + gridLayout->setSpacing(5); + gridLayout->setColumnStretch(COLUMN_LABEL, 0); + gridLayout->setColumnStretch(COLUMN_WIDGET, 100); + int row = 0; + gridLayout->addWidget(whatsThisPushButton, row, COLUMN_LABEL, 1, 2, Qt::AlignLeft); + row++; + gridLayout->addWidget(m_databaseLabel, row, COLUMN_LABEL, Qt::AlignRight); + gridLayout->addWidget(m_databaseComboBox, row, COLUMN_WIDGET); + row++; + + return groupBox; +} + +/** + * @return A new ZIP file directory widget + */ +QWidget* +BalsaDatabaseUploadSceneFileDialog::creatZipFileDirectoryWidget() +{ + m_zipFileTemporaryDirectoryRadioButton = new QRadioButton("System Temporary"); + m_zipFileCustomDirectoryRadioButton = new QRadioButton("Custom: "); + + QButtonGroup* buttGroup = new QButtonGroup(this); + buttGroup->addButton(m_zipFileTemporaryDirectoryRadioButton); + buttGroup->addButton(m_zipFileCustomDirectoryRadioButton); + m_zipFileTemporaryDirectoryRadioButton->setChecked(true); // do before signal + QObject::connect(buttGroup, static_cast(&QButtonGroup::buttonClicked), + [=](int id){ this->zipFileDirectoryRadioButtonClicked(id); }); + + m_zipFileAutomaticDirectoryLineEdit = new QLineEdit(); + m_zipFileAutomaticDirectoryLineEdit->setReadOnly(true); + m_zipFileAutomaticDirectoryLineEdit->setText(SystemUtilities::getTempDirectory()); + + m_zipFileCustomDirectoryLineEdit = new QLineEdit(); + + QPushButton* copyAutomaticDirectoryPushButton = new QPushButton("Copy"); + QObject::connect(copyAutomaticDirectoryPushButton, &QPushButton::clicked, + this, &BalsaDatabaseUploadSceneFileDialog::copyAutomaticDirectoryPushButtonClicked); + copyAutomaticDirectoryPushButton->setToolTip("Copy system temporary directory to clipboard"); + + QPushButton* browseCustomDirectoryPushButton = new QPushButton("Browse..."); + QObject::connect(browseCustomDirectoryPushButton, &QPushButton::clicked, + this, &BalsaDatabaseUploadSceneFileDialog::browseZipFileCustomDirectoryPushButtonClicked); + + QPushButton* whatsThisPushButton = new QPushButton("What's this?"); + whatsThisPushButton->setSizePolicy(QSizePolicy::Fixed, whatsThisPushButton->sizePolicy().verticalPolicy()); + QObject::connect(whatsThisPushButton, &QPushButton::clicked, + this, &BalsaDatabaseUploadSceneFileDialog::whatsThisZipFileDirectory); + + QGroupBox* zipDirectoryGroupBox = new QGroupBox("Zip File Directory"); + QGridLayout* gridLayout = new QGridLayout(zipDirectoryGroupBox); + gridLayout->setSpacing(2); + gridLayout->setColumnStretch(0, 0); + gridLayout->setColumnStretch(1, 100); + gridLayout->setColumnStretch(2, 0); + int row = 0; + gridLayout->addWidget(whatsThisPushButton, row, 0, 1, 3, Qt::AlignLeft); + row++; + gridLayout->addWidget(m_zipFileTemporaryDirectoryRadioButton, row, 0); + gridLayout->addWidget(m_zipFileAutomaticDirectoryLineEdit, row, 1); + gridLayout->addWidget(copyAutomaticDirectoryPushButton, row, 2); + row++; + gridLayout->addWidget(m_zipFileCustomDirectoryRadioButton, row, 0); + gridLayout->addWidget(m_zipFileCustomDirectoryLineEdit, row, 1); + gridLayout->addWidget(browseCustomDirectoryPushButton, row, 2); + row++; + + return zipDirectoryGroupBox; +} + +/** + * @return New instance of the advanced tab + */ +QWidget* +BalsaDatabaseUploadSceneFileDialog::createAdvancedTab() { + QWidget* databaseSelectionWidget = createBalsaDatabaseSelectionWidget(); + QWidget* zipDirectoryWidget = creatZipFileDirectoryWidget(); + m_basePathWidget = new SceneBasePathWidget(); + + QLabel* infoLabel = new QLabel("NOTE: In most instances, the default selections should be used."); + infoLabel->setWordWrap(true); + + QWidget* widget = new QWidget; + QVBoxLayout* layout = new QVBoxLayout(widget); + layout->addWidget(infoLabel); + layout->addWidget(m_basePathWidget); + layout->addWidget(zipDirectoryWidget); + layout->addWidget(databaseSelectionWidget); + layout->addStretch(); + + return widget; } /** @@ -193,36 +554,291 @@ BalsaDatabaseUploadSceneFileDialog::labelHtmlLinkClicked(const QString& linkPath } } +/** + * Called when a Zip File Directory radio button is clicked + */ +void +BalsaDatabaseUploadSceneFileDialog::zipFileDirectoryRadioButtonClicked(int) +{ + validateUploadData(); +} + +/** + * Get the ZIP file name. + * + * @param errorMessageOut + * Output if failure to create zip file name. + * + * @return + * Name of ZIP file. If empty, check errorMessageOut for explanation. + */ +AString +BalsaDatabaseUploadSceneFileDialog::getZipFileNameWithPath(AString& errorMessageOut) const +{ + errorMessageOut = ""; + + AString directoryName; + if (m_zipFileTemporaryDirectoryRadioButton->isChecked()) { + directoryName = SystemUtilities::getTempDirectory(); + if ( ! FileInformation(directoryName).exists()) { + errorMessageOut = "Default temporary directory is invalid. Please choose a Custom Zip File Directory on Advanced tab"; + return ""; + } + } + else if (m_zipFileCustomDirectoryRadioButton->isChecked()) { + directoryName = m_zipFileCustomDirectoryLineEdit->text().trimmed(); + if (directoryName.isEmpty()) { + errorMessageOut = "Zip File Custom directory is selected but is empty (see Advanced tab)."; + return ""; + } + if ( ! FileInformation(directoryName).exists()) { + errorMessageOut = "Zip File Custom directory is invalid. Please choose a Custom Zip File Directory on Advanced tab"; + return ""; + } + } + else { + CaretAssert(0); + } + + FileInformation zipFileName(directoryName, + "BalsaUpload_" + QString::number(QDateTime::currentMSecsSinceEpoch())+ ".zip"); + + return zipFileName.getAbsoluteFilePath(); +} /** - * Gets called when the OK button is clicked. + * Gets called when the login button is clicked. */ void -BalsaDatabaseUploadSceneFileDialog::okButtonClicked() +BalsaDatabaseUploadSceneFileDialog::loginButtonClicked() { + const AString username = m_usernameLineEdit->text().trimmed(); + if (username.isEmpty()) { + WuQMessageBox::errorOk(this, + "Username is empty"); + return; + } + const AString password = m_passwordLineEdit->text().trimmed(); + if (password.isEmpty()) { + WuQMessageBox::errorOk(this, + "Password is empty"); + return; + } + + setOkButtonEnabled(false); + m_selectStudyTitlePushButton->setEnabled(false); + m_userRoles->resetToAllInvalid(); + updateUserRolesLabel(); + CursorDisplayScoped cursor; cursor.showWaitCursor(); + + /* + * If operation takes a while + */ + WuQWidgetDisabler blocker(m_loginPushButton); + + AString errorMessage; + if ( ! m_balsaDatabaseManager->login(getDataBaseURL(), + username, + password, + errorMessage)) { + cursor.restoreCursor(); + WuQMessageBox::errorOk(this, + errorMessage); + return; + } + + if (m_balsaDatabaseManager->getUserRoles(*m_userRoles, + errorMessage)) { + if ( ! m_userRoles->isSubmitter()) { + cursor.restoreCursor(); + const AString msg("Login to BALSA was successful. However, your have not agreed " + "to the BALSA Submission Terms and Conditions that are required " + "to upload data to BALSA. You will need to login to BALSA " + "using your web browser and complete this agreement."); + const bool okPressedFlag = WuQMessageBox::warningAcceptReject(this, + msg, + "OK", + "OK, Go to BALSA in Web Browser"); + if ( ! okPressedFlag) { + labelHtmlLinkClicked("about/submission"); + } + return; + } + } + else { + m_userRoles->resetToAllInvalid(); + + cursor.restoreCursor(); + const AString msg("Unable to verify that you have agreed to the BALSA " + "Submission Terms and Conditions. If you have not " + "agreed to terms and conditions, uploading will fail.\n\n" + + errorMessage); + WuQMessageBox::warningOk(this, msg); + } + + updateUserRolesLabel(); + + setOkButtonEnabled(true); + m_selectStudyTitlePushButton->setEnabled(true); + + SessionManager::get()->getCaretPreferences()->setBalsaUserName(username); +} - ProgressReportingDialog progressDialog("Upload Scene File to BALSA", - "", - this); +/** + * Update roles label with user's valid roles + */ +void +BalsaDatabaseUploadSceneFileDialog::updateUserRolesLabel() +{ + m_userRolesLabel->setText(""); + + const bool showRolesFlag = false; + if (showRolesFlag) { + const AString rolesText = m_userRoles->getRolesForDisplayInGUI(); + if ( ! rolesText.isEmpty()) { + m_userRolesLabel->setText(" BALSA Roles: " + + rolesText); + } + } +} + + +/** + * Gets called when return is pressed in either username + * or password. If both contain text, try to login. + */ +void +BalsaDatabaseUploadSceneFileDialog::returnPressedUsernameOrPassword() +{ + if (m_usernameLineEdit->text().trimmed().isEmpty()) { + return; + } + if (m_passwordLineEdit->text().trimmed().isEmpty()) { + return; + } + + loginButtonClicked(); +} + + +/** + * Gets called when the Upload button is clicked. + */ +void +BalsaDatabaseUploadSceneFileDialog::okButtonClicked() +{ + CaretAssert(m_sceneFile); + + m_sceneFile->setBalsaStudyID(m_balsaStudyIDLineEdit->text().trimmed()); + m_sceneFile->setBalsaStudyTitle(m_balsaStudyTitleLineEdit->text().trimmed()); + AString zipFileErrorMessage; + const AString zipFileName = getZipFileNameWithPath(zipFileErrorMessage); + + m_sceneFile->setBalsaExtractToDirectoryName(m_extractDirectoryNameLineEdit->text().trimmed()); + + s_username = m_usernameLineEdit->text(); + s_password = m_passwordLineEdit->text(); + + AString msg; + if ( ! m_usernameLineEdit->hasAcceptableInput()) { + msg.appendWithNewLine("Username is missing. If you do not have an account, press the \"Register\" link. " + "If you have forgotten your username, press the \"Forgot Username\" link.

"); + } + if ( ! m_passwordLineEdit->hasAcceptableInput()) { + msg.appendWithNewLine("Password is missing. If you do not remember your password, press the \"Forgot Password\" link.

"); + } + if (zipFileName.isEmpty()) { + msg.appendWithNewLine(zipFileErrorMessage + "

"); + } + if ( ! m_extractDirectoryNameLineEdit->hasAcceptableInput()) { + msg.appendWithNewLine("Extract to Directory is invalid.

"); + } + + AString basePathErrorMessage; + if ( ! m_basePathWidget->isValid(basePathErrorMessage)) { + msg.appendWithNewLine(basePathErrorMessage); + } + else { + switch (m_sceneFile->getBasePathType()) { + case SceneFileBasePathTypeEnum::AUTOMATIC: + break; + case SceneFileBasePathTypeEnum::CUSTOM: + { + if (m_sceneFile->getBalsaCustomBaseDirectory().isEmpty()) { + msg.appendWithNewLine("CUSTOM Base Path (on Advanced Tab) is invalid. " + "Select Base Path or use AUTOMATIC.

"); + } + else { + if ( ! FileInformation(m_sceneFile->getBalsaCustomBaseDirectory()).exists()) { + msg.appendWithNewLine("CUSTOM Base Directory (on Advanced Tab) is not a valid directory on this computer. " + "Select a valid Base Path or use AUTOMATIC.

"); + } + } + } + break; + } + } + + bool invalidStudyFlag = false; + if ( ! m_balsaStudyTitleLineEdit->hasAcceptableInput()) { + invalidStudyFlag = true; + } + if ( ! m_balsaStudyIDLineEdit->hasAcceptableInput()) { + invalidStudyFlag = true; + } + if (invalidStudyFlag) { + msg.appendWithNewLine("Study ID and/or Title is invalid. Press the \"" + + m_selectStudyTitlePushButton->text() + + "\" button to choose or create a BALSA Study ID and Title."); + } + + if ( ! msg.isEmpty()) { + msg.insert(0, ""); + msg.append(""); + WuQMessageBox::errorOk(this, msg); + return; + } - progressDialog.setCancelButton((QPushButton*)0); // no cancel button const AString username = m_usernameLineEdit->text().trimmed(); const AString password = m_passwordLineEdit->text().trimmed(); - const AString zipFileName = m_zipFileNameLineEdit->text().trimmed(); + const AString extractToDirectoryName = m_extractDirectoryNameLineEdit->text().trimmed(); - + + AString saveMessage("The scene file is modified and must be saved before continuing."); AString errorMessage; - CaretPointer balsaDatabaseManager(new BalsaDatabaseManager()); - const bool successFlag = balsaDatabaseManager->uploadZippedSceneFile(getDataBaseURL(), - username, - password, - m_sceneFile, - zipFileName, - extractToDirectoryName, - errorMessage); + bool sceneFileModStatus = m_sceneFile->isModified(); + if (m_balsaDatabaseManager->updateSceneIDs(m_sceneFile, + errorMessage)) { + if ( ! sceneFileModStatus) { + if (m_sceneFile->isModified()) { + saveMessage = "The scene file was modified to add BALSA Scene Identifiers and must be saved before continuing."; + } + } + } + else { + return; + } + + if ( ! saveSceneFile(saveMessage)) { + return; + } + + CursorDisplayScoped cursor; + cursor.showWaitCursor(); + ProgressReportingDialog progressDialog("Upload Scene File to BALSA", + "", + this); + + progressDialog.setCancelButton((QPushButton*)0); // no cancel button + + const bool successFlag = m_balsaDatabaseManager->uploadZippedSceneFile(m_sceneFile, + zipFileName, + extractToDirectoryName, + errorMessage); + cursor.restoreCursor(); progressDialog.setValue(progressDialog.maximum()); @@ -236,6 +852,379 @@ BalsaDatabaseUploadSceneFileDialog::okButtonClicked() return; } - WuQDialogModal::okButtonClicked(); + WuQDialogModal::okButtonClicked(); +} + +/** + * Save the scene file if it is modified. If auto-save is OFF, the user is + * prompted to confirm saving. + * + * @param message + * Message containing reason for saving scene file. If empty, the + * default "file has been modified and must be saved" message is displayed. + * @return + * True if the scene file was saved successfully, else false. + */ +bool +BalsaDatabaseUploadSceneFileDialog::saveSceneFile(const AString& saveMessage) +{ + if (m_sceneFile->isModified()) { + if ( ! m_autoSaveSceneFileCheckBox->isChecked()) { + AString msg(saveMessage + + " Would you like to save the scene file using its current name and continue?"); + if ( ! WuQMessageBox::warningOkCancel(this, msg)) { + return false; + } + + } + try { + Brain* brain = GuiManager::get()->getBrain(); + brain->writeDataFile(m_sceneFile); + } + catch (const DataFileException& e) { + WuQMessageBox::errorOk(this, e.whatString()); + return false; + } + } + + return true; +} + +/** + * Gets called when auto save checkbox is checked. + * + * @param checked + * Check status + */ +void +BalsaDatabaseUploadSceneFileDialog::autoSaveCheckBoxClicked(bool /*checked*/) +{ + +} + +void +BalsaDatabaseUploadSceneFileDialog::testButtonClicked() +{ + const int32_t buttonIndex = WuQMessageBox::informationTwoButtons(this, + "You have not agreed to Submitter Terms", + "OK", + "OK, View Terms in Web Browser"); + switch (buttonIndex) { + case 1: + break; + case 2: + labelHtmlLinkClicked("about/submission"); + break; + default: + CaretAssert(0); + } + return; + + bool testRolesFlag = false; + bool testNewIDsFlag = true; + if (testRolesFlag) { + const AString username = m_usernameLineEdit->text().trimmed(); + const AString password = m_passwordLineEdit->text().trimmed(); + + AString roleNames; + AString errorMessage; + + BalsaUserRoles balsaUserRoles; + if ( ! m_balsaDatabaseManager->getUserRoles(balsaUserRoles, + errorMessage)) { + WuQMessageBox::errorOk(this, + errorMessage); + return; + } + + std::cout << "*** Role Names: " << balsaUserRoles.toString() << std::endl; + } + + if (testNewIDsFlag) { + int32_t numberOfSceneIDs = 2; + std::vector sceneIDs; + AString errorMessage; + if ( ! m_balsaDatabaseManager->getSceneIDs(numberOfSceneIDs, + sceneIDs, + errorMessage)) { + WuQMessageBox::errorOk(this, + errorMessage); + return; + } + for (auto id : sceneIDs) { + std::cout << "Scene ID " << id << std::endl; + } + } +} + +/** + * Select a Study Title from those user has in BALSA Database + */ +void +BalsaDatabaseUploadSceneFileDialog::selectStudyTitleButtonClicked() +{ + const AString username = m_usernameLineEdit->text().trimmed(); + const AString password = m_passwordLineEdit->text().trimmed(); + + std::vector studyInfo; + + CursorDisplayScoped cursor; + cursor.showWaitCursor(); + AString errorMessage; + if ( ! m_balsaDatabaseManager->getAllStudyInformation(studyInfo, + errorMessage)) { + cursor.restoreCursor(); + WuQMessageBox::errorOk(this, + errorMessage); + return; + } + + cursor.restoreCursor(); + + const AString selectedStudyName = m_balsaStudyTitleLineEdit->text().trimmed(); + + BalsaStudySelectionDialog dialog(studyInfo, + selectedStudyName, + m_balsaDatabaseManager.get(), + getDataBaseURL(), + username, + password, + this); + if (dialog.exec() == BalsaStudySelectionDialog::Accepted) { + BalsaStudyInformation bsi = dialog.getSelectedStudyInformation(); + if ( ! bsi.isEmpty()) { + m_balsaStudyIDLineEdit->setText(bsi.getStudyID()); + m_balsaStudyTitleLineEdit->setText(bsi.getStudyTitle()); + } + validateUploadData(); + } +} + +/** + * Copy automatic directory to clipboard + */ +void +BalsaDatabaseUploadSceneFileDialog::copyAutomaticDirectoryPushButtonClicked() +{ + const QString txt = m_zipFileAutomaticDirectoryLineEdit->text().trimmed(); + if ( ! txt.isEmpty()) { + QApplication::clipboard()->setText(txt, + QClipboard::Clipboard); + } +} + +/** + * Choose the custom directory + */ +void +BalsaDatabaseUploadSceneFileDialog::browseZipFileCustomDirectoryPushButtonClicked() +{ + CaretAssert(m_sceneFile); + + /* + * Let user choose directory path + */ + QString directoryName; + FileInformation fileInfo(m_zipFileCustomDirectoryLineEdit->text().trimmed()); + if (fileInfo.exists()) { + if (fileInfo.isDirectory()) { + directoryName = fileInfo.getAbsoluteFilePath(); + } + } + AString newDirectoryName = CaretFileDialog::getExistingDirectoryDialog(m_zipFileCustomDirectoryRadioButton, + "Choose Custom Zip File Directory", + directoryName); + /* + * If user cancels, return + */ + if (newDirectoryName.isEmpty()) { + return; + } + + /* + * Set name of new scene file and add to brain + */ + m_zipFileCustomDirectoryLineEdit->setText(newDirectoryName); + FileInformation newFileInfo(newDirectoryName); + if (newFileInfo.exists() + && newFileInfo.isDirectory()) { + m_zipFileCustomDirectoryRadioButton->setChecked(true); + } + + validateUploadData(); +} + +/** + * Create a regular expression validatory for the give label/data. + * + * @param labelName + * 'Name' of label. + */ +QRegularExpressionValidator* +BalsaDatabaseUploadSceneFileDialog::createValidator(const LabelName labelName) +{ + QRegularExpression regEx; + + switch (labelName) { + case LabelName::LABEL_DATABASE: + regEx.setPattern(".+"); + break; + case LabelName::LABEL_EXTRACT_DIRECTORY: + regEx.setPattern(".+"); + break; + case LabelName::LABEL_PASSWORD: + regEx.setPattern(".+"); + break; + case LabelName::LABEL_STUDY_ID: + { + const AString lowerCaseNoVowels("bcdfghjklmnpqrstvwxyz"); + const AString upperCaseNoVowels(lowerCaseNoVowels.toUpper()); + regEx.setPattern("[0-9" + lowerCaseNoVowels + upperCaseNoVowels + "]+"); + } + break; + case LabelName::LABEL_STUDY_TITLE: + regEx.setPattern(".+"); + break; + case LabelName::LABEL_USERNAME: + regEx.setPattern(".+"); + break; + } + CaretAssert(regEx.isValid()); + + QRegularExpressionValidator* validator = new QRegularExpressionValidator(regEx, + this); + return validator; +} + +/** + * Called by validators. + */ +void +BalsaDatabaseUploadSceneFileDialog::validateUploadData() +{ + updateAllLabels(); +} + +/** + * Update all of the labels. + */ +void +BalsaDatabaseUploadSceneFileDialog::updateAllLabels() +{ + setLabelText(LabelName::LABEL_DATABASE); + setLabelText(LabelName::LABEL_EXTRACT_DIRECTORY); + setLabelText(LabelName::LABEL_PASSWORD); + setLabelText(LabelName::LABEL_STUDY_ID); + setLabelText(LabelName::LABEL_STUDY_TITLE); + setLabelText(LabelName::LABEL_USERNAME); +} + + +/** + * Set the label's text. + * + * @param label + * The label + * @param labelName + * 'Name' of the label + */ +void +BalsaDatabaseUploadSceneFileDialog::setLabelText(const LabelName labelName) +{ + QLabel* label = NULL; + AString labelText; + bool validFlag = false; + switch (labelName) { + case LabelName::LABEL_DATABASE: + label = m_databaseLabel; + labelText = "Database"; + validFlag = true; + break; + case LabelName::LABEL_EXTRACT_DIRECTORY: + label = m_extractDirectoryNameLabel; + labelText = "Unzip into Directory"; + validFlag = m_extractDirectoryNameLineEdit->hasAcceptableInput(); + break; + case LabelName::LABEL_PASSWORD: + label = m_passwordLabel; + labelText = "Password"; + validFlag = m_passwordLineEdit->hasAcceptableInput(); + break; + case LabelName::LABEL_STUDY_ID: + label = m_balsaStudyIDLabel; + labelText = "Study ID"; + validFlag = m_balsaStudyIDLineEdit->hasAcceptableInput(); + break; + case LabelName::LABEL_STUDY_TITLE: + label = m_balsaStudyTitleLabel; + labelText = "Study Title"; + validFlag = m_balsaStudyTitleLineEdit->hasAcceptableInput(); + break; + case LabelName::LABEL_USERNAME: + label = m_usernameLabel; + labelText = "Username"; + validFlag = m_usernameLineEdit->hasAcceptableInput(); + break; + } + + const bool textRedIfInvalid = true; + + AString coloredText; + if (validFlag) { + if (textRedIfInvalid) { + coloredText = (labelText + + ": "); + } + else { + coloredText = (" " + + labelText + + ": "); + } + } + else { + if (textRedIfInvalid) { + coloredText = ("" + + labelText + + ": "); + } + else { + coloredText = ("" + + labelText + + "*" + ": "); + } + } + + CaretAssert(label); + label->setText(coloredText); +} + +/** + * Displays a dialog explaining the ZIP file directory options + */ +void +BalsaDatabaseUploadSceneFileDialog::whatsThisZipFileDirectory() +{ + const AString text("When a Scene File is uploaded to BALSA, the Scene File and all data files that it " + "references are placed into a ZIP file and the ZIP file is then uploaded to BALSA. " + "While unlikely, creation of the ZIP file in the system temporary directory could fail " + "if there is insufficient space. If this should happen, set a CUSTOM directory on a disk " + "with adequate space."); + WuQMessageBox::informationOk(this, + text); +} + +/** + * Displays a dialog explaining the database selection option + */ +void +BalsaDatabaseUploadSceneFileDialog::whatsThisDatabase() +{ + const AString text("This option is provided for developers of " + "Workbench (wb_view and wb_command) and BALSA Database software. " + "It allows the selection of develpment versions of the BALSA Database. " + "To add a database, type the new name into the combo box."); + WuQMessageBox::informationOk(this, + text); } diff --git a/src/GuiQt/BalsaDatabaseUploadSceneFileDialog.h b/src/GuiQt/BalsaDatabaseUploadSceneFileDialog.h index a31bcd51842e65e63d668d55a77801f5a917b067..8f372d7ed3fc132d487599f478bfc9cdca3babcd 100644 --- a/src/GuiQt/BalsaDatabaseUploadSceneFileDialog.h +++ b/src/GuiQt/BalsaDatabaseUploadSceneFileDialog.h @@ -21,17 +21,24 @@ */ /*LICENSE_END*/ +#include + +#include #include "WuQDialogModal.h" +class QCheckBox; class QComboBox; class QLabel; class QLineEdit; - - +class QPushButton; +class QRadioButton; +class QRegularExpressionValidator; namespace caret { - + class BalsaDatabaseManager; + class BalsaUserRoles; + class SceneBasePathWidget; class SceneFile; class BalsaDatabaseUploadSceneFileDialog : public WuQDialogModal { @@ -39,7 +46,7 @@ namespace caret { Q_OBJECT public: - BalsaDatabaseUploadSceneFileDialog(const SceneFile* sceneFile, + BalsaDatabaseUploadSceneFileDialog(SceneFile* sceneFile, QWidget* parent); virtual ~BalsaDatabaseUploadSceneFileDialog(); @@ -48,36 +55,130 @@ namespace caret { // ADD_NEW_METHODS_HERE private slots: + + void loginInformationChanged(); + + void loginButtonClicked(); + + void returnPressedUsernameOrPassword(); + void labelHtmlLinkClicked(const QString&); + void validateUploadData(); + + void selectStudyTitleButtonClicked(); + + void zipFileDirectoryRadioButtonClicked(int); + + void copyAutomaticDirectoryPushButtonClicked(); + + void browseZipFileCustomDirectoryPushButtonClicked(); + + void testButtonClicked(); + + void autoSaveCheckBoxClicked(bool checked); + + virtual void cancelButtonClicked(); + + void whatsThisZipFileDirectory(); + + void whatsThisDatabase(); + protected: virtual void okButtonClicked(); private: + enum class LabelName { + LABEL_DATABASE, + LABEL_EXTRACT_DIRECTORY, + LABEL_PASSWORD, + LABEL_STUDY_ID, + LABEL_STUDY_TITLE, + LABEL_USERNAME + }; + BalsaDatabaseUploadSceneFileDialog(const BalsaDatabaseUploadSceneFileDialog&); BalsaDatabaseUploadSceneFileDialog& operator=(const BalsaDatabaseUploadSceneFileDialog&); + QWidget* createLoginWidget(); + + QWidget* createTabWidget(); + + QWidget* createUploadTab(); + + QWidget* createAdvancedTab(); + + QWidget* createBalsaDatabaseSelectionWidget(); + + QWidget* creatZipFileDirectoryWidget(); + + void loadSceneFileMetaDataWidgets(); + AString getDataBaseURL() const; - const SceneFile* m_sceneFile; + QRegularExpressionValidator* createValidator(const LabelName labelName); + + AString getZipFileNameWithPath(AString& errorMessageOut) const; + + void updateAllLabels(); + + void setLabelText(const LabelName labelName); + + bool saveSceneFile(const AString& saveMesage); + + void updateUserRolesLabel(); + + SceneFile* m_sceneFile; + std::unique_ptr m_balsaDatabaseManager; + + QWidget* m_uploadWidget; + + QLabel* m_databaseLabel; QComboBox* m_databaseComboBox; + QLabel* m_usernameLabel; QLineEdit* m_usernameLineEdit; + QLabel* m_passwordLabel; QLineEdit* m_passwordLineEdit; - QLineEdit* m_zipFileNameLineEdit; + QPushButton* m_loginPushButton; + QLabel* m_userRolesLabel; + + std::unique_ptr m_userRoles; + + QRadioButton* m_zipFileTemporaryDirectoryRadioButton; + QRadioButton* m_zipFileCustomDirectoryRadioButton; + + QLineEdit* m_zipFileAutomaticDirectoryLineEdit; + QLineEdit* m_zipFileCustomDirectoryLineEdit; + QLabel* m_extractDirectoryNameLabel; QLineEdit* m_extractDirectoryNameLineEdit; + + QLabel* m_balsaStudyIDLabel; + QLineEdit* m_balsaStudyIDLineEdit; + + QLabel* m_balsaStudyTitleLabel; + QLineEdit* m_balsaStudyTitleLineEdit; + QPushButton* m_selectStudyTitlePushButton; + + QCheckBox* m_autoSaveSceneFileCheckBox; + + SceneBasePathWidget* m_basePathWidget; + + static AString s_username; + static AString s_password; // ADD_NEW_MEMBERS_HERE }; #ifdef __BALSA_DATABASE_UPLOAD_SCENE_FILE_DIALOG_DECLARE__ - // + AString BalsaDatabaseUploadSceneFileDialog::s_username; + AString BalsaDatabaseUploadSceneFileDialog::s_password; #endif // __BALSA_DATABASE_UPLOAD_SCENE_FILE_DIALOG_DECLARE__ } // namespace diff --git a/src/GuiQt/BalsaStudyInformation.cxx b/src/GuiQt/BalsaStudyInformation.cxx new file mode 100644 index 0000000000000000000000000000000000000000..e27fa2cdade8403700cd146ec5bc1152bdad7dcb --- /dev/null +++ b/src/GuiQt/BalsaStudyInformation.cxx @@ -0,0 +1,201 @@ + +/*LICENSE_START*/ +/* + * Copyright (C) 2017 Washington University School of Medicine + * + * 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 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ +/*LICENSE_END*/ + +#define __BALSA_STUDY_INFORMATION_DECLARE__ +#include "BalsaStudyInformation.h" +#undef __BALSA_STUDY_INFORMATION_DECLARE__ + +#include + +#include "CaretAssert.h" +using namespace caret; + + + +/** + * \class caret::BalsaStudyInformation + * \brief Information about a BALSA Study + * \ingroup GuiQt + */ + +/** + * Constructor with invalid ID and title. + */ +BalsaStudyInformation::BalsaStudyInformation() +: CaretObject() +{ + +} + +/** + * Constructor with ID and title. + * + * @param studyID + * The Study Identifier + * @param studyTitle + * The Study Title + */ +BalsaStudyInformation::BalsaStudyInformation(const AString& studyID, + const AString& studyTitle) +: CaretObject(), +m_studyID(studyID), +m_studyTitle(studyTitle) +{ +} + +/** + * Constructor that creates BALSA study information from a JSON object. + * + * @param jsonObject + * The JSON object. + */ +BalsaStudyInformation::BalsaStudyInformation(const QJsonObject& jsonObject) +{ + QJsonObject::const_iterator idIter = jsonObject.find("id"); + QJsonObject::const_iterator titleIter = jsonObject.find("title"); + if ((idIter != jsonObject.end()) + && (titleIter != jsonObject.end())) { + m_studyID = (*idIter).toString(); + m_studyTitle = (*titleIter).toString(); + } +} + +/** + * Destructor. + */ +BalsaStudyInformation::~BalsaStudyInformation() +{ +} + +/** + * Copy constructor. + * @param obj + * Object that is copied. + */ +BalsaStudyInformation::BalsaStudyInformation(const BalsaStudyInformation& obj) +: CaretObject(obj) +{ + this->copyHelperBalsaStudyInformation(obj); +} + +/** + * Assignment operator. + * @param obj + * Data copied from obj to this. + * @return + * Reference to this object. + */ +BalsaStudyInformation& +BalsaStudyInformation::operator=(const BalsaStudyInformation& obj) +{ + if (this != &obj) { + CaretObject::operator=(obj); + this->copyHelperBalsaStudyInformation(obj); + } + return *this; +} + +/** + * Comparison operator using study title. + * + * @param obj + * Other study information. + * @return + * True if "this" is less than obj. + */ +bool +BalsaStudyInformation::operator<(const BalsaStudyInformation& obj) const +{ + return m_studyTitle < obj.m_studyTitle; +} + + +/** + * Helps with copying an object of this type. + * @param obj + * Object that is copied. + */ +void +BalsaStudyInformation::copyHelperBalsaStudyInformation(const BalsaStudyInformation& obj) +{ + m_studyID = obj.m_studyID; + m_studyTitle = obj.m_studyTitle; +} + +/** + * @return True if ID is not empty (title may be empty). + */ +bool +BalsaStudyInformation::isEmpty() const +{ + return m_studyID.isEmpty(); +} + +/** + * @return study identifier generated by BALSA + */ +AString +BalsaStudyInformation::getStudyID() const +{ + return m_studyID; +} + +/** + * Set study identifier generated by BALSA + * @param studyID + * New value for study identifier generated by BALSA + */ +void +BalsaStudyInformation::setStudyID(const AString& studyID) +{ + m_studyID = studyID; +} + +/** + * @return study title + */ +AString +BalsaStudyInformation::getStudyTitle() const +{ + return m_studyTitle; +} + +/** + * Set study title + * @param studyTitle + * New value for study title + */ +void +BalsaStudyInformation::setStudyTitle(const AString& studyTitle) +{ + m_studyTitle = studyTitle; +} + +/** + * Get a description of this object's content. + * @return String describing this object's content. + */ +AString +BalsaStudyInformation::toString() const +{ + return "BalsaStudyInformation"; +} + diff --git a/src/GuiQt/BalsaStudyInformation.h b/src/GuiQt/BalsaStudyInformation.h new file mode 100644 index 0000000000000000000000000000000000000000..d0cca092440ebd4ab69306597aa83f87a32ca233 --- /dev/null +++ b/src/GuiQt/BalsaStudyInformation.h @@ -0,0 +1,81 @@ +#ifndef __BALSA_STUDY_INFORMATION_H__ +#define __BALSA_STUDY_INFORMATION_H__ + +/*LICENSE_START*/ +/* + * Copyright (C) 2017 Washington University School of Medicine + * + * 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 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ +/*LICENSE_END*/ + + +#include "CaretObject.h" + +class QJsonObject; + +namespace caret { + + class BalsaStudyInformation : public CaretObject { + + public: + BalsaStudyInformation(const QJsonObject& jsonObject); + + BalsaStudyInformation(); + + BalsaStudyInformation(const AString& studyID, + const AString& studyTitle); + + virtual ~BalsaStudyInformation(); + + BalsaStudyInformation(const BalsaStudyInformation& obj); + + BalsaStudyInformation& operator=(const BalsaStudyInformation& obj); + + bool operator<(const BalsaStudyInformation& obj) const; + + bool isEmpty() const; + + AString getStudyID() const; + + void setStudyID(const AString& studyID); + + AString getStudyTitle() const; + + void setStudyTitle(const AString& studyTitle); + + // ADD_NEW_METHODS_HERE + + virtual AString toString() const; + + private: + void copyHelperBalsaStudyInformation(const BalsaStudyInformation& obj); + + /** study identifier generated by BALSA*/ + AString m_studyID; + + /** study title*/ + AString m_studyTitle; + + // ADD_NEW_MEMBERS_HERE + + }; + +#ifdef __BALSA_STUDY_INFORMATION_DECLARE__ + // +#endif // __BALSA_STUDY_INFORMATION_DECLARE__ + +} // namespace +#endif //__BALSA_STUDY_INFORMATION_H__ diff --git a/src/GuiQt/BalsaStudySelectionDialog.cxx b/src/GuiQt/BalsaStudySelectionDialog.cxx new file mode 100644 index 0000000000000000000000000000000000000000..a2ea71b308a41a7e26dc70c0d5be8647694e51b8 --- /dev/null +++ b/src/GuiQt/BalsaStudySelectionDialog.cxx @@ -0,0 +1,295 @@ + +/*LICENSE_START*/ +/* + * Copyright (C) 2017 Washington University School of Medicine + * + * 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 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ +/*LICENSE_END*/ + +#define __BALSA_STUDY_SELECTION_DIALOG_DECLARE__ +#include "BalsaStudySelectionDialog.h" +#undef __BALSA_STUDY_SELECTION_DIALOG_DECLARE__ + +#include +#include +#include +#include +#include +#include + +#include "BalsaDatabaseManager.h" +#include "CaretAssert.h" +#include "WuQMessageBox.h" +#include "WuQtUtilities.h" + +using namespace caret; + + + +/** + * \class caret::BalsaStudySelectionDialog + * \brief Dialog for selection and getting study titles with BALSA Database + * \ingroup GuiQt + */ + +/** + * Constructor. + * + * @param studyInformation + * The study information. + * @param selectedStudyName + * Name of study information that is selected. + * @param balsaDatabaseManager + * Database manager. + * @param databaseURL + * URL for database requests. + * @param balsaUsername + * Username for BALSA Database + * @param balsaPassword + * Password for BALSA Database + * @param parent + * Parent dialog + */ +BalsaStudySelectionDialog::BalsaStudySelectionDialog(const std::vector& studyInformation, + const AString selectedStudyName, + BalsaDatabaseManager* balsaDatabaseManager, + const AString& databaseURL, + const AString& balsaUsername, + const AString& balsaPassword, + QWidget* parent) +: WuQDialogModal("BALSA Studies", + parent), +m_studyInformation(studyInformation), +m_balsaDatabaseManager(balsaDatabaseManager), +m_databaseURL(databaseURL), +m_balsaUsername(balsaUsername), +m_balsaPassword(balsaPassword) +{ + m_studyTableWidget = new QTableWidget(); + + QPushButton* newStudyTitlePushButton = new QPushButton("Create New Study..."); + newStudyTitlePushButton->setSizePolicy(QSizePolicy::Fixed, + newStudyTitlePushButton->sizePolicy().verticalPolicy()); + QObject::connect(newStudyTitlePushButton, &QPushButton::clicked, + this, &BalsaStudySelectionDialog::newStudyButtonClicked); + + m_editSelectedStudyPushButton = new QPushButton("Edit Selected Study Title..."); + m_editSelectedStudyPushButton->setSizePolicy(QSizePolicy::Fixed, + m_editSelectedStudyPushButton->sizePolicy().verticalPolicy()); + QObject::connect(m_editSelectedStudyPushButton, &QPushButton::clicked, + this, &BalsaStudySelectionDialog::editSelectedStudyButtonClicked); + + + QHBoxLayout* studyButtonLayout = new QHBoxLayout(); + studyButtonLayout->addWidget(newStudyTitlePushButton); + studyButtonLayout->addStretch(); + studyButtonLayout->addWidget(m_editSelectedStudyPushButton); + + QWidget* dialogWidget = new QWidget; + QVBoxLayout* dialogLayout = new QVBoxLayout(dialogWidget); + dialogLayout->addWidget(m_studyTableWidget, 100); + dialogLayout->addLayout(studyButtonLayout, 0); + + setCentralWidget(dialogWidget, SCROLL_AREA_NEVER); + + loadStudyTitleTableWidget(selectedStudyName); + + const QSize tableSize = WuQtUtilities::estimateTableWidgetSize(m_studyTableWidget); + + WuQtUtilities::resizeWindow(this, + std::min(tableSize.width() + 40, + 600), + std::min(tableSize.height() + 40, + 600)); +} + +/** + * Destructor. + */ +BalsaStudySelectionDialog::~BalsaStudySelectionDialog() +{ +} + +/** + * Load the list widget for the studies. + * + * @param selectedStudyName + * Name of study information that is selected. + */ +void +BalsaStudySelectionDialog::loadStudyTitleTableWidget(const AString selectedStudyNameIn) +{ + AString selectedStudyName = selectedStudyNameIn; + + const int numRows = static_cast(m_studyInformation.size()); + if (selectedStudyName.isEmpty()) { + const int32_t selectedRow = m_studyTableWidget->currentRow(); + if ((selectedRow >= 0) + && (selectedRow < numRows)) { + selectedStudyName = m_studyTableWidget->item(selectedRow, + COLUMN_TITLE)->text(); + } + } + + m_studyTableWidget->clear(); + m_studyTableWidget->setSelectionMode(QTableWidget::SingleSelection); + m_studyTableWidget->setSelectionBehavior(QTableWidget::SelectRows); + m_studyTableWidget->setRowCount(numRows); + m_studyTableWidget->setColumnCount(COLUMN_COUNT); + m_studyTableWidget->setHorizontalHeaderItem(COLUMN_ID, new QTableWidgetItem("ID")); + m_studyTableWidget->setHorizontalHeaderItem(COLUMN_TITLE, new QTableWidgetItem("Title")); + m_studyTableWidget->setShowGrid(false); + + std::sort(m_studyInformation.begin(), + m_studyInformation.end()); + + int32_t selectedRow = 0; + for (int32_t iRow = 0; iRow < numRows; iRow++) { + const AString studyTitle = m_studyInformation[iRow].getStudyTitle(); + if ( ! selectedStudyName.isEmpty()) { + if (selectedStudyName.toLower() == studyTitle.toLower()) { + selectedRow = iRow; + } + } + + CaretAssertVectorIndex(m_studyInformation, iRow); + QTableWidgetItem* idItem = new QTableWidgetItem(m_studyInformation[iRow].getStudyID()); + idItem->setFlags(idItem->flags() &= (~Qt::ItemIsEditable)); + m_studyTableWidget->setItem(iRow, COLUMN_ID, idItem); + + QTableWidgetItem* titleItem = new QTableWidgetItem(studyTitle); + titleItem->setFlags(idItem->flags() &= (~Qt::ItemIsEditable)); + m_studyTableWidget->setItem(iRow, COLUMN_TITLE, titleItem); + } + + if (numRows > 0) { + if (selectedRow >= 0) { + m_studyTableWidget->selectRow(selectedRow); + } + else { + m_studyTableWidget->selectRow(0); + } + const int32_t rowIndex = m_studyTableWidget->currentRow(); + if (rowIndex >= 0) { + QTableWidgetItem* item = m_studyTableWidget->item(rowIndex, 0); + if (item != NULL) { + m_studyTableWidget->scrollToItem(item); + } + } + + QHeaderView* horizontalHeader = m_studyTableWidget->horizontalHeader(); + for (int32_t i = 0; i < COLUMN_COUNT; i++) { + const int logicalIndex = horizontalHeader->logicalIndex(i); + horizontalHeader->setSectionResizeMode(logicalIndex, + QHeaderView::ResizeToContents); + } + } +} + +/** + * @return The selected study information. Will return + * and empty instance (test with isEmpty()) if no study information was selected. + */ +BalsaStudyInformation +BalsaStudySelectionDialog::getSelectedStudyInformation() const +{ + const int32_t selectedRow = m_studyTableWidget->currentRow(); + if (selectedRow < 0) { + return BalsaStudyInformation(); + } + + CaretAssertVectorIndex(m_studyInformation, selectedRow); + return m_studyInformation[selectedRow]; +} + +/** + * Called when the new study button is clicked. + */ +void +BalsaStudySelectionDialog::newStudyButtonClicked() +{ + AString title = QInputDialog::getText(this, + "Create New Study", + "Title of New Study"); + if ( ! title.isNull()) { + title = title.trimmed(); + AString studyID; + AString errorMessage; + const bool successFlag = m_balsaDatabaseManager->getStudyIDFromStudyTitle(title, + studyID, + errorMessage); + + if (successFlag) { + const AString titleLower = title.toLower(); + for (auto infoIter : m_studyInformation) { + if (infoIter.getStudyTitle().toLower() == titleLower) { + WuQMessageBox::errorOk(this, ("Duplicate study names are not allowed: \"" + + title + + "\" ")); + return; + } + } + m_studyInformation.emplace_back(studyID, + title); + loadStudyTitleTableWidget(title); + } + else { + WuQMessageBox::errorOk(this, + errorMessage); + } + } +} + +/** + * Called when edit selected study title button is clicked + */ +void +BalsaStudySelectionDialog::editSelectedStudyButtonClicked() +{ + const int32_t selectedRow = m_studyTableWidget->currentRow(); + if (selectedRow < 0) { + WuQMessageBox::errorOk(this, "No Study Title is selected"); + return; + } + + CaretAssertVectorIndex(m_studyInformation, selectedRow); + AString title = m_studyInformation[selectedRow].getStudyTitle(); + + title = QInputDialog::getText(this, + "Edit study title", + "Title", + QLineEdit::Normal, + title); + if ( ! title.isNull()) { + m_studyInformation[selectedRow].setStudyTitle(title); + loadStudyTitleTableWidget(title); + } +} + +/** + * Called when the OK button is clicked. + */ +void +BalsaStudySelectionDialog::okButtonClicked() +{ + if ( m_studyTableWidget->currentItem() == NULL) { + WuQMessageBox::errorOk(this, "No Study Title is selected"); + return; + } + + WuQDialogModal::okButtonClicked(); +} + diff --git a/src/GuiQt/BalsaStudySelectionDialog.h b/src/GuiQt/BalsaStudySelectionDialog.h new file mode 100644 index 0000000000000000000000000000000000000000..900004d175beaa1a37bf6861cb72989b8a71935f --- /dev/null +++ b/src/GuiQt/BalsaStudySelectionDialog.h @@ -0,0 +1,100 @@ +#ifndef __BALSA_STUDY_SELECTION_DIALOG_H__ +#define __BALSA_STUDY_SELECTION_DIALOG_H__ + +/*LICENSE_START*/ +/* + * Copyright (C) 2017 Washington University School of Medicine + * + * 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 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ +/*LICENSE_END*/ + + + +#include + +#include "BalsaStudyInformation.h" +#include "WuQDialogModal.h" + +class QPushButton; +class QTableWidget; + + +namespace caret { + class BalsaDatabaseManager; + + class BalsaStudySelectionDialog : public WuQDialogModal { + + Q_OBJECT + + public: + BalsaStudySelectionDialog(const std::vector& studyInformation, + const AString selectedStudyName, + BalsaDatabaseManager* balsaDatabaseManager, + const AString& databaseURL, + const AString& balsaUsername, + const AString& balsaPassword, + QWidget* parent); + + virtual ~BalsaStudySelectionDialog(); + + virtual void okButtonClicked() override; + + BalsaStudyInformation getSelectedStudyInformation() const; + + // ADD_NEW_METHODS_HERE + + private slots: + void newStudyButtonClicked(); + + void editSelectedStudyButtonClicked(); + + private: + const int32_t COLUMN_TITLE = 0; + + const int32_t COLUMN_ID = 1; + + const int32_t COLUMN_COUNT = 2; + + BalsaStudySelectionDialog(const BalsaStudySelectionDialog&); + + BalsaStudySelectionDialog& operator=(const BalsaStudySelectionDialog&); + + void loadStudyTitleTableWidget(const AString selectedStudyName); + + std::vector m_studyInformation; + + BalsaDatabaseManager* m_balsaDatabaseManager; + + const AString& m_databaseURL; + + const AString& m_balsaUsername; + + const AString& m_balsaPassword; + + QTableWidget* m_studyTableWidget; + + QPushButton* m_editSelectedStudyPushButton; + + // ADD_NEW_MEMBERS_HERE + + }; + +#ifdef __BALSA_STUDY_SELECTION_DIALOG_DECLARE__ + // +#endif // __BALSA_STUDY_SELECTION_DIALOG_DECLARE__ + +} // namespace +#endif //__BALSA_STUDY_SELECTION_DIALOG_H__ diff --git a/src/GuiQt/BalsaUserRoles.cxx b/src/GuiQt/BalsaUserRoles.cxx new file mode 100644 index 0000000000000000000000000000000000000000..1119015c69bcc79810b14b9b15588e18ab46baa4 --- /dev/null +++ b/src/GuiQt/BalsaUserRoles.cxx @@ -0,0 +1,254 @@ + +/*LICENSE_START*/ +/* + * Copyright (C) 2017 Washington University School of Medicine + * + * 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 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ +/*LICENSE_END*/ + +#define __BALSA_USER_ROLES_DECLARE__ +#include "BalsaUserRoles.h" +#undef __BALSA_USER_ROLES_DECLARE__ + +#include + +#include "CaretAssert.h" +#include "CaretLogger.h" + +using namespace caret; + + + +/** + * \class caret::BalsaUserRoles + * \brief Information about a BALSA User's Roles + * \ingroup GuiQt + */ + +/** + * Constructor with all roles invalid; + */ +BalsaUserRoles::BalsaUserRoles() +: CaretObject() +{ + resetToAllInvalid(); +} + +/** + * Constructor that creates BALSA user roles from a JSON array. + * + * @param jsonArray + * The JSON array. + */ +BalsaUserRoles::BalsaUserRoles(const QJsonArray& jsonArray) +{ + resetToAllInvalid(); + parseJson(jsonArray); +} + +/** + * Destructor. + */ +BalsaUserRoles::~BalsaUserRoles() +{ +} + +/** + * Copy constructor. + * @param obj + * Object that is copied. + */ +BalsaUserRoles::BalsaUserRoles(const BalsaUserRoles& obj) +: CaretObject(obj) +{ + this->copyHelperBalsaUserRoles(obj); +} + +/** + * Assignment operator. + * @param obj + * Data copied from obj to this. + * @return + * Reference to this object. + */ +BalsaUserRoles& +BalsaUserRoles::operator=(const BalsaUserRoles& obj) +{ + if (this != &obj) { + CaretObject::operator=(obj); + this->copyHelperBalsaUserRoles(obj); + } + return *this; +} + +/** + * Helps with copying an object of this type. + * @param obj + * Object that is copied. + */ +void +BalsaUserRoles::copyHelperBalsaUserRoles(const BalsaUserRoles& obj) +{ + m_validFlag = obj.m_validFlag; + m_adminFlag = obj.m_adminFlag; + m_curatorFlag = obj.m_curatorFlag; + m_submitterFlag = obj.m_submitterFlag; + m_userFlag = obj.m_userFlag; +} + +/** + * Reset the instance to all invalid roles. + */ +void +BalsaUserRoles::resetToAllInvalid() +{ + m_validFlag = false; + m_adminFlag = false; + m_curatorFlag = false; + m_submitterFlag = false; + m_userFlag = false; +} + +/** + * Parse JSON to find roles. + * + * @param jsonArray + * JSON array with roles. + * @return + * True if data was parsed, else false. + */ +bool +BalsaUserRoles::parseJson(const QJsonArray& jsonArray) +{ + resetToAllInvalid(); + + for (QJsonArray::const_iterator roleIter = jsonArray.begin(); + roleIter != jsonArray.end(); + roleIter++) { + QJsonValue value = *roleIter; + if (value.isString()) { + const QString roleName = value.toString().trimmed(); + if (roleName == "ROLE_ADMIN") { + m_adminFlag = true; + } + else if (roleName == "ROLE_CURATOR") { + m_curatorFlag = true; + } + else if (roleName == "ROLE_SUBMITTER") { + m_submitterFlag = true; + } + else if (roleName == "ROLE_USER") { + m_userFlag = true; + } + else { + CaretLogInfo("Unrecognized BALSA role found \"" + + roleName + + "\""); + } + } + + m_validFlag = true; + } + + /* + * Some roles imply other roles + */ + if (m_curatorFlag) { + m_submitterFlag = true; + } + + return isValid(); +} + +/** + * @return True if has admin role + */ +bool +BalsaUserRoles::isAdmin() const +{ + return m_adminFlag; +} + +/** + * @return True if has admin role + */ +bool +BalsaUserRoles::isCurator() const +{ + return m_curatorFlag; +} + +/** + * @return True if has submitter role + */ +bool +BalsaUserRoles::isSubmitter() const +{ + return m_submitterFlag; +} + +/** + * @return True if has user role + */ +bool +BalsaUserRoles::isUser() const +{ + return m_userFlag; +} + +/** + * Is the data valid (has parsed successfully) + */ +bool +BalsaUserRoles::isValid() const +{ + return m_validFlag; +} + +/** + * @return Roles formatted for display in GUI. + */ +AString +BalsaUserRoles::getRolesForDisplayInGUI() const +{ + AString s; + + if (m_validFlag) { + if (m_adminFlag) s.append("Admin; "); + if (m_curatorFlag) s.append("Curator; "); + if (m_submitterFlag) s.append("Submitter; "); + if (m_userFlag) s.append("User; "); + } + + return s; +} + +/** + * Get a description of this object's content. + * @return String describing this object's content. + */ +AString +BalsaUserRoles::toString() const +{ + AString msg("BalsaUserRoles: " + + AString("Data Valid - " + AString(m_validFlag ? "YES: " : "NO: ")) + + AString("Admin - " + AString(m_adminFlag ? "YES: " : "NO: ")) + + AString("Curator - " + AString(m_curatorFlag ? "YES: " : "NO: ")) + + AString("Submitter - " + AString(m_submitterFlag ? "YES: " : "NO: ")) + + AString("User - " + AString(m_userFlag ? "YES: " : "NO: "))); + return msg; +} + diff --git a/src/GuiQt/BalsaUserRoles.h b/src/GuiQt/BalsaUserRoles.h new file mode 100644 index 0000000000000000000000000000000000000000..701a013ae81811fe40192d69ede49ee55f24fe75 --- /dev/null +++ b/src/GuiQt/BalsaUserRoles.h @@ -0,0 +1,88 @@ +#ifndef __BALSA_USER_ROLES_H__ +#define __BALSA_USER_ROLES_H__ + +/*LICENSE_START*/ +/* + * Copyright (C) 2017 Washington University School of Medicine + * + * 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 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ +/*LICENSE_END*/ + + +#include "CaretObject.h" + +class QJsonArray; + +namespace caret { + + class BalsaUserRoles : public CaretObject { + + public: + BalsaUserRoles(const QJsonArray& jsonArray); + + BalsaUserRoles(); + + virtual ~BalsaUserRoles(); + + BalsaUserRoles(const BalsaUserRoles& obj); + + BalsaUserRoles& operator=(const BalsaUserRoles& obj); + + void resetToAllInvalid(); + + bool parseJson(const QJsonArray& jsonArray); + + bool isEmpty() const; + + bool isAdmin() const; + + bool isCurator() const; + + bool isSubmitter() const; + + bool isUser() const; + + bool isValid() const; + + // ADD_NEW_METHODS_HERE + + AString getRolesForDisplayInGUI() const; + + virtual AString toString() const; + + private: + void copyHelperBalsaUserRoles(const BalsaUserRoles& obj); + + bool m_adminFlag = false; + + bool m_curatorFlag = false; + + bool m_submitterFlag = false; + + bool m_userFlag = false; + + bool m_validFlag = false; + + // ADD_NEW_MEMBERS_HERE + + }; + +#ifdef __BALSA_USER_ROLES_DECLARE__ + // +#endif // __BALSA_USER_ROLES_DECLARE__ + +} // namespace +#endif //__BALSA_USER_ROLES_H__ diff --git a/src/GuiQt/BorderFileSplitDialog.cxx b/src/GuiQt/BorderFileSplitDialog.cxx index dd9199b3a058584b1f4465fb05ee2bed2618c16e..b99accf94503b8858207e62bf9f059b229eff360 100644 --- a/src/GuiQt/BorderFileSplitDialog.cxx +++ b/src/GuiQt/BorderFileSplitDialog.cxx @@ -73,8 +73,7 @@ BorderFileSplitDialog::BorderFileSplitDialog(QWidget* parent) QObject::connect(m_fileNameToolButtonSignalMapper, SIGNAL(mapped(int)), this, SLOT(fileNameToolButtonClicked(int))); - Brain* brain = GuiManager::get()->getBrain(); - m_fileSelectionModel.grabNew(CaretDataFileSelectionModel::newInstanceForMultiStructureBorderFiles(brain)); + m_fileSelectionModel.grabNew(CaretDataFileSelectionModel::newInstanceForMultiStructureBorderFiles()); QLabel* fileSelectionLabel = new QLabel("Multi Structure Border File: "); m_fileSelectionComboBox = new CaretDataFileSelectionComboBox(this); diff --git a/src/GuiQt/BorderOptimizeDialog.cxx b/src/GuiQt/BorderOptimizeDialog.cxx index 30fc452cf31393e8184140563879438f85c0be57..21a28ae93ab8359012287e6c2dcfbc4a7545880a 100644 --- a/src/GuiQt/BorderOptimizeDialog.cxx +++ b/src/GuiQt/BorderOptimizeDialog.cxx @@ -298,8 +298,7 @@ BorderOptimizeDialog::updateDialog(const int32_t browserTabIndex, if (m_vertexAreasMetricFileSelectionModel == NULL) { std::vector metricDataTypes; metricDataTypes.push_back(DataFileTypeEnum::METRIC); - m_vertexAreasMetricFileSelectionModel = CaretDataFileSelectionModel::newInstanceForCaretDataFileTypesInStructure(GuiManager::get()->getBrain(), - m_surfaceSelectionStructure, + m_vertexAreasMetricFileSelectionModel = CaretDataFileSelectionModel::newInstanceForCaretDataFileTypesInStructure(m_surfaceSelectionStructure, metricDataTypes); m_vertexAreasMetricFileComboBox->updateComboBox(m_vertexAreasMetricFileSelectionModel); } @@ -687,8 +686,7 @@ BorderOptimizeDialog::createBorderSelectionWidget() { m_borderPairCheckBox = new QCheckBox("Border Pair File"); m_borderPairCheckBox->setChecked(true); - m_borderPairFileSelectionModel = CaretDataFileSelectionModel::newInstanceForCaretDataFileType(GuiManager::get()->getBrain(), - DataFileTypeEnum::BORDER); + m_borderPairFileSelectionModel = CaretDataFileSelectionModel::newInstanceForCaretDataFileType(DataFileTypeEnum::BORDER); m_borderPairFileSelectionComboBox = new CaretDataFileSelectionComboBox(this); QHBoxLayout* borderPairLayout = new QHBoxLayout(); borderPairLayout->addWidget(m_borderPairCheckBox, 0); diff --git a/src/GuiQt/BorderOptimizeExecutor.cxx b/src/GuiQt/BorderOptimizeExecutor.cxx index b00bf5c86602c4f0e8e4e1831a380e391fb747b8..e542b88203dac0ec1b714813144d481752587b65 100644 --- a/src/GuiQt/BorderOptimizeExecutor.cxx +++ b/src/GuiQt/BorderOptimizeExecutor.cxx @@ -1107,6 +1107,7 @@ BorderOptimizeExecutor::printInputs(const InputData& inputData) std::cout << " Strength: " << dfi.m_weight << std::endl; std::cout << " Smoothing: " << dfi.m_smoothing << std::endl; std::cout << " Invert Gradient: " << AString::fromBool(dfi.m_invertGradientFlag) << std::endl; + std::cout << " Skip Gradient: " << AString::fromBool(dfi.m_skipGradient) << std::endl; } std::cout << "Vertex Areas Metric File: " << ((inputData.m_vertexAreasMetricFile != NULL) ? qPrintable(inputData.m_vertexAreasMetricFile->getFileNameNoPath()) diff --git a/src/GuiQt/BorderPropertiesEditorDialog.cxx b/src/GuiQt/BorderPropertiesEditorDialog.cxx index 7f7ccd2111f2735205ea615ef524aa5635d91133..ad510d1062b0128a8efc52e9064477878dc3bc39 100644 --- a/src/GuiQt/BorderPropertiesEditorDialog.cxx +++ b/src/GuiQt/BorderPropertiesEditorDialog.cxx @@ -172,6 +172,7 @@ BorderPropertiesEditorDialog::BorderPropertiesEditorDialog(const QString& title, SLOT(newBorderFileButtonClicked())); QToolButton* newFileToolButton = new QToolButton(); newFileToolButton->setDefaultAction(newFileAction); + WuQtUtilities::setToolButtonStyleForQt5Mac(newFileToolButton); /* * Completer for name @@ -192,6 +193,7 @@ BorderPropertiesEditorDialog::BorderPropertiesEditorDialog(const QString& title, SLOT(displayNameEditor())); QToolButton* displayNameColorEditorToolButton = new QToolButton(); displayNameColorEditorToolButton->setDefaultAction(displayNameColorEditorAction); + WuQtUtilities::setToolButtonStyleForQt5Mac(displayNameColorEditorToolButton); /* * Class @@ -209,6 +211,7 @@ BorderPropertiesEditorDialog::BorderPropertiesEditorDialog(const QString& title, SLOT(displayClassEditor())); QToolButton* displayClassEditorToolButton = new QToolButton(); displayClassEditorToolButton->setDefaultAction(displayClassEditorAction); + WuQtUtilities::setToolButtonStyleForQt5Mac(displayClassEditorToolButton); /* * Closed diff --git a/src/GuiQt/BorderSelectionViewController.cxx b/src/GuiQt/BorderSelectionViewController.cxx index ed9de06a5c33cdee9d0d0e37c7194137453a0642..d669b71ee529f42268f06357631e1dd3c25815b2 100644 --- a/src/GuiQt/BorderSelectionViewController.cxx +++ b/src/GuiQt/BorderSelectionViewController.cxx @@ -151,7 +151,9 @@ BorderSelectionViewController::createAttributesWidget() m_drawTypeComboBox->addItem(BorderDrawingTypeEnum::toGuiName(drawType), (int)drawType); } - m_drawTypeComboBox->setToolTip("Select the drawing style of borders"); + m_drawTypeComboBox->setToolTip("Select the drawing style of borders\n" + "Lines are faster but have limited width\n" + "Polylines are slower but have unlimited width"); QObject::connect(m_drawTypeComboBox, SIGNAL(activated(int)), this, SLOT(processAttributesChanges())); diff --git a/src/GuiQt/BrainBrowserWindow.cxx b/src/GuiQt/BrainBrowserWindow.cxx index 0bdbbe3c16f6ac2c60e75919184872c116640fcd..dae45623b0edee7cf00b37e6eda1c7f4709e8d60 100644 --- a/src/GuiQt/BrainBrowserWindow.cxx +++ b/src/GuiQt/BrainBrowserWindow.cxx @@ -22,6 +22,7 @@ #include #include #include +#include #include #include #include @@ -29,6 +30,7 @@ #include #include #include +#include #include #include @@ -48,6 +50,7 @@ #include "BrainOpenGLWidget.h" #include "BrainStructure.h" #include "BrowserTabContent.h" +#include "BrowserWindowContent.h" #include "CaretAssert.h" #include "CaretFileDialog.h" #include "CaretFileRemoteDialog.h" @@ -62,6 +65,7 @@ #include "ElapsedTimer.h" #include "EventGetViewportSize.h" #include "EventBrowserWindowCreateTabs.h" +#include "EventBrowserWindowContent.h" #include "EventDataFileRead.h" #include "EventMacDockMenuUpdate.h" #include "EventManager.h" @@ -74,7 +78,9 @@ #include "EventUserInterfaceUpdate.h" #include "FileInformation.h" #include "FociProjectionDialog.h" +#include "GapsAndMargins.h" #include "GuiManager.h" +#include "LockAspectWarningDialog.h" #include "ModelSurface.h" #include "ModelSurfaceMontage.h" #include "ModelWholeBrain.h" @@ -96,8 +102,10 @@ #include "SurfaceSelectionViewController.h" #include "TileTabsConfiguration.h" #include "WuQDataEntryDialog.h" +#include "WuQDoubleSpinBox.h" #include "WuQMessageBox.h" #include "WuQtUtilities.h" +#include "WuQTextEditorDialog.h" #include "VtkFileExporter.h" using namespace caret; @@ -120,40 +128,44 @@ BrainBrowserWindow::BrainBrowserWindow(const int browserWindowIndex, const CreateDefaultTabsMode createDefaultTabsMode, QWidget* parent, Qt::WindowFlags flags) -: QMainWindow(parent, flags) +: QMainWindow(parent, flags), +m_browserWindowIndex(browserWindowIndex) { m_developMenuAction = NULL; + std::unique_ptr bwc = EventBrowserWindowContent::newWindowContent(m_browserWindowIndex); + EventManager::get()->sendEvent(bwc->getPointer()); + + m_browserWindowContent = bwc->getBrowserWindowContent(); + CaretAssert(m_browserWindowContent); + if (BrainBrowserWindow::s_firstWindowFlag) { BrainBrowserWindow::s_firstWindowFlag = false; } - m_viewTileTabsSelected = false; - - m_aspectRatio = 1.0; - - m_sceneTileTabsConfigurationText = "From Scene: "; - m_sceneTileTabsConfiguration = new TileTabsConfiguration(); - m_sceneTileTabsConfiguration->setName(m_sceneTileTabsConfigurationText); m_defaultTileTabsConfiguration = new TileTabsConfiguration(); m_defaultTileTabsConfiguration->setDefaultConfiguration(true); m_defaultTileTabsConfiguration->setName("All Tabs (Default)"); m_selectedTileTabsConfigurationUniqueIdentifier = m_defaultTileTabsConfiguration->getUniqueIdentifier(); - GuiManager* guiManager = GuiManager::get(); - setAttribute(Qt::WA_DeleteOnClose); - m_browserWindowIndex = browserWindowIndex; - - setWindowTitle(guiManager->applicationName() + setWindowTitle(ApplicationInformation().getName() + " " + AString::number(m_browserWindowIndex + 1)); setObjectName(windowTitle()); + BrainOpenGLWidget* shareOpenGLContextWidget = NULL; + if ( ! s_brainBrowserWindows.empty()) { + std::set::iterator iter = s_brainBrowserWindows.begin(); + CaretAssert(iter != s_brainBrowserWindows.end()); + shareOpenGLContextWidget = (*iter)->m_openGLWidget; + CaretAssert(shareOpenGLContextWidget); + } m_openGLWidget = new BrainOpenGLWidget(this, - browserWindowIndex); - + shareOpenGLContextWidget, + browserWindowIndex); + const int openGLSizeX = 500; const int openGLSizeY = (WuQtUtilities::isSmallDisplay() ? 200 : 375); m_openGLWidget->setMinimumSize(openGLSizeX, @@ -179,13 +191,11 @@ BrainBrowserWindow::BrainBrowserWindow(const int browserWindowIndex, m_overlayActiveToolBox = m_overlayVerticalToolBox; addDockWidget(Qt::LeftDockWidgetArea, m_overlayVerticalToolBox); m_overlayHorizontalToolBox->setVisible(false); - //m_overlayHorizontalToolBox->toggleViewAction()->trigger(); } else { m_overlayActiveToolBox = m_overlayHorizontalToolBox; addDockWidget(Qt::BottomDockWidgetArea, m_overlayHorizontalToolBox); m_overlayVerticalToolBox->setVisible(false); - //m_overlayVerticalToolBox->toggleViewAction()->trigger(); } QObject::connect(m_overlayHorizontalToolBox, SIGNAL(visibilityChanged(bool)), @@ -213,8 +223,7 @@ BrainBrowserWindow::BrainBrowserWindow(const int browserWindowIndex, browserTabContent, m_overlayToolBoxAction, m_featuresToolBoxAction, - m_windowAspectRatioLockedAction, - m_tabAspectRatioLockedAction, + m_toolBarLockWindowAndAllTabAspectRatioButton, this); m_showToolBarAction = m_toolbar->toolBarToolButtonAction; addToolBar(m_toolbar); @@ -235,8 +244,6 @@ BrainBrowserWindow::BrainBrowserWindow(const int browserWindowIndex, } m_sceneAssistant = new SceneClassAssistant(); - m_sceneAssistant->add("m_aspectRatio", - &m_aspectRatio); m_defaultWindowComponentStatus.isFeaturesToolBoxDisplayed = m_featuresToolBoxAction->isChecked(); m_defaultWindowComponentStatus.isOverlayToolBoxDisplayed = m_overlayToolBoxAction->isChecked(); @@ -246,6 +253,32 @@ BrainBrowserWindow::BrainBrowserWindow(const int browserWindowIndex, EventManager::get()->addEventListener(this, EventTypeEnum::EVENT_GET_VIEWPORT_SIZE); EventManager::get()->addEventListener(this, EventTypeEnum::EVENT_GRAPHICS_UPDATE_ALL_WINDOWS); EventManager::get()->addEventListener(this, EventTypeEnum::EVENT_GRAPHICS_UPDATE_ONE_WINDOW); + +#if QT_VERSION >= 0x050600 + if (m_overlayHorizontalToolBox == m_overlayActiveToolBox) { + /* + * With Qt5, default height of overlay toolbox at bottom is + * way too tall. Qt 5.6 added a 'resizeDocks' method and + * it is used to initialize the height of the overlay toolbox. + */ + const int toolboxHeight = 150; + QList docks; + docks.push_back(m_overlayHorizontalToolBox); + QList dockSizes; + dockSizes.push_back(toolboxHeight); + + resizeDocks(docks, + dockSizes, + Qt::Vertical); + } +#endif + s_brainBrowserWindows.insert(this); + + GapsAndMargins* gapsAndMargins = GuiManager::get()->getBrain()->getGapsAndMargins(); + gapsAndMargins->setSurfaceMontageHorizontalGapForWindow(m_browserWindowIndex, 0.0f); + gapsAndMargins->setSurfaceMontageVerticalGapForWindow(m_browserWindowIndex, 0.0f); + gapsAndMargins->setVolumeMontageHorizontalGapForWindow(m_browserWindowIndex, 0.0f); + gapsAndMargins->setVolumeMontageVerticalGapForWindow(m_browserWindowIndex, 0.0f); } /** @@ -253,10 +286,18 @@ BrainBrowserWindow::BrainBrowserWindow(const int browserWindowIndex, */ BrainBrowserWindow::~BrainBrowserWindow() { + std::unique_ptr bwc = EventBrowserWindowContent::deleteWindowContent(m_browserWindowIndex); + EventManager::get()->sendEvent(bwc->getPointer()); + m_browserWindowContent = NULL; + EventManager::get()->removeAllEventsFromListener(this); + s_brainBrowserWindows.erase(this); + + if (s_brainBrowserWindows.empty()) { + } + delete m_defaultTileTabsConfiguration; - delete m_sceneTileTabsConfiguration; delete m_sceneAssistant; } @@ -425,131 +466,6 @@ BrainBrowserWindow::receiveEvent(Event* event) viewportSizeEvent->setViewportSize(viewport); } } -// else if (event->getEventType() == EventTypeEnum::EVENT_BROWSER_TAB_GET_VIEWPORT_SIZE) { -// EventBrowserTabGetViewportSize* viewportSizeEvent = dynamic_cast(event); -// CaretAssert(viewportSizeEvent); -// -// std::vector allTabContent; -// m_toolbar->getAllTabContent(allTabContent); -// -// const std::vector allViewportContent = m_openGLWidget->getViewportContent(); -// -// int32_t tabViewport[4] = { 0, 0, 0, 0 }; -// bool tabViewportValid = false; -// -// int32_t notBestTabViewport[4] = { 0, 0, 0, 0 }; -// bool notBestTabViewportValid = false; -// -// /* -// * Find the viewport content containing the specified tab by index -// */ -// for (std::vector::iterator tabIter = allTabContent.begin(); -// tabIter != allTabContent.end(); -// tabIter++) { -// const BrowserTabContent* btc = *tabIter; -// CaretAssert(btc); -// -// switch (viewportSizeEvent->getMode()) { -// case EventBrowserTabGetViewportSize::MODE_SURFACE_MONTAGE: -// { -// const Model* model = btc->getModelForDisplay(); -// if (model->getModelType() == ModelTypeEnum::MODEL_TYPE_SURFACE_MONTAGE) { -// for (std::vector::const_iterator vpIter = allViewportContent.begin(); -// vpIter != allViewportContent.end(); -// vpIter++) { -// const BrainOpenGLViewportContent* vpContent = *vpIter; -// if (vpContent->getTabIndex() == btc->getTabNumber()) { -// vpContent->getTabViewportBeforeApplyingMargins(tabViewport); -// tabViewportValid = true; -// break; -// } -// } -// } -// } -// break; -// case EventBrowserTabGetViewportSize::MODE_TAB_INDEX: -// if (btc->getTabNumber() == viewportSizeEvent->getIndex()) { -// for (std::vector::const_iterator vpIter = allViewportContent.begin(); -// vpIter != allViewportContent.end(); -// vpIter++) { -// const BrainOpenGLViewportContent* vpContent = *vpIter; -// if (vpContent->getTabIndex() == viewportSizeEvent->getIndex()) { -// vpContent->getTabViewportBeforeApplyingMargins(tabViewport); -// tabViewportValid = true; -// break; -// } -// } -// } -// break; -// case EventBrowserTabGetViewportSize::MODE_VOLUME_MONTAGE: -// { -// const Model* model = btc->getModelForDisplay(); -// if (model->getModelType() == ModelTypeEnum::MODEL_TYPE_VOLUME_SLICES) { -// for (std::vector::const_iterator vpIter = allViewportContent.begin(); -// vpIter != allViewportContent.end(); -// vpIter++) { -// const BrainOpenGLViewportContent* vpContent = *vpIter; -// if (vpContent->getTabIndex() == btc->getTabNumber()) { -// if (btc->getSliceDrawingType() == VolumeSliceDrawingTypeEnum::VOLUME_SLICE_DRAW_MONTAGE) { -// vpContent->getTabViewportBeforeApplyingMargins(tabViewport); -// tabViewportValid = true; -// break; -// } -// else { -// vpContent->getTabViewportBeforeApplyingMargins(notBestTabViewport); -// notBestTabViewportValid = true; -// } -// } -// } -// } -// } -// break; -// case EventBrowserTabGetViewportSize::MODE_WINDOW_INDEX: -// if (m_browserWindowIndex == viewportSizeEvent->getIndex()) { -// for (std::vector::const_iterator vpIter = allViewportContent.begin(); -// vpIter != allViewportContent.end(); -// vpIter++) { -// const BrainOpenGLViewportContent* vpContent = *vpIter; -// if (vpContent->getTabIndex() == viewportSizeEvent->getIndex()) { -// vpContent->getTabViewportBeforeApplyingMargins(tabViewport); -// tabViewportValid = true; -// break; -// } -// } -// } -// break; -// } -// -// if (tabViewportValid) { -// break; -// } -// } -// -// if ( ! tabViewportValid) { -// if (notBestTabViewportValid) { -// tabViewport[0] = notBestTabViewport[0]; -// tabViewport[1] = notBestTabViewport[1]; -// tabViewport[2] = notBestTabViewport[2]; -// tabViewport[3] = notBestTabViewport[3]; -// tabViewportValid = true; -// } -// else { -// /* -// * Tab is in this window but not the active tab. -// * So, use the active tab's viewport -// */ -// if ( ! allViewportContent.empty()) { -// CaretAssertVectorIndex(allViewportContent, 0); -// allViewportContent[0]->getTabViewportBeforeApplyingMargins(tabViewport); -// tabViewportValid = true; -// } -// } -// } -// -// if (tabViewportValid) { -// viewportSizeEvent->setViewportSize(tabViewport); -// } -// } else if ((event->getEventType() == EventTypeEnum::EVENT_GRAPHICS_UPDATE_ONE_WINDOW) || (event->getEventType() == EventTypeEnum::EVENT_GRAPHICS_UPDATE_ALL_WINDOWS)) { /* @@ -567,6 +483,18 @@ BrainBrowserWindow::receiveEvent(Event* event) } } +/** + * @return True if OpenGL Context Sharing is valid among + * multiple graphics windows. + * Note: If there is one window, we declare sharing valid. + */ +bool +BrainBrowserWindow::isOpenGLContextSharingValid() const +{ + CaretAssert(m_openGLWidget); + return m_openGLWidget->isOpenGLContextSharingValid(); +} + /** * Reset the graphics window size. * When the window is created, the graphics is set to a reasonable size @@ -591,47 +519,49 @@ BrainBrowserWindow::setGraphicsWidgetFixedSize(const int32_t width, /** * Get the graphics widget size. * - * @param xOut - * X-coord of graphics region (may be non-zero when lock aspect applied) - * @param yOut - * Y-coord of graphics region (may be non-zero when lock aspect applied) - * @param widthOut - * Width of the graphics widget. - * @param heightOut - * Height of the graphics widget. - * @param graphicsWidthOut - * True width of graphics region as if no aspect ratio was applied. - * @param graphicsHeightOut - * True height of graphics region as if no aspect ratio was applied. + * @param viewportXOut + * X-coord of viewport in graphics region (may be non-zero when lock aspect applied) + * @param viewportYOut + * Y-coord of viewport in graphics region (may be non-zero when lock aspect applied) + * @param viewportWidthOut + * Width of the viewport into which graphics are drawn (may be less than + * graphicsWidthOut when lock aspect is applied) + * @param viewportHeightOut + * Height of the viewport into which graphics are drawn (may be less than + * graphicsHeightOut when lock aspect is applied) + * @param openGLWidgetWidthOut + * True width of the OpenGL Widget. + * @param openGLWidgetHeightOut + * True height of the OpenGL Widget. * @param applyLockedAspectRatiosFlag * True if locked tab or window aspect ratio should be applied for - * graphics region size. + * viewport but only if lock aspect is enabled by the user. */ void -BrainBrowserWindow::getGraphicsWidgetSize(int32_t& xOut, - int32_t& yOut, - int32_t& widthOut, - int32_t& heightOut, - int32_t& graphicsWidthOut, - int32_t& graphicsHeightOut, +BrainBrowserWindow::getGraphicsWidgetSize(int32_t& viewportXOut, + int32_t& viewportYOut, + int32_t& viewportWidthOut, + int32_t& viewportHeightOut, + int32_t& openGLWidgetWidthOut, + int32_t& openGLWidgetHeightOut, const bool applyLockedAspectRatiosFlag) const { - xOut = 0; - yOut = 0; - graphicsWidthOut = m_openGLWidget->width(); - graphicsHeightOut = m_openGLWidget->height(); - widthOut = m_openGLWidget->width(); - heightOut = m_openGLWidget->height(); + viewportXOut = 0; + viewportYOut = 0; + openGLWidgetWidthOut = m_openGLWidget->width(); + openGLWidgetHeightOut = m_openGLWidget->height(); + viewportWidthOut = m_openGLWidget->width(); + viewportHeightOut = m_openGLWidget->height(); int aspectViewport[4] = { 0, 0, - widthOut, - heightOut + openGLWidgetWidthOut, + openGLWidgetHeightOut }; if (isTileTabsSelected()) { - if (isAspectRatioLocked()) { + if (isWindowAspectRatioLocked()) { BrainOpenGLViewportContent::adjustViewportForAspectRatio(aspectViewport, getAspectRatio()); } @@ -643,7 +573,7 @@ BrainBrowserWindow::getGraphicsWidgetSize(int32_t& xOut, BrainOpenGLViewportContent::adjustViewportForAspectRatio(aspectViewport, btc->getAspectRatio()); } - else if (isAspectRatioLocked()) { + else if (isWindowAspectRatioLocked()) { BrainOpenGLViewportContent::adjustViewportForAspectRatio(aspectViewport, getAspectRatio()); } @@ -662,30 +592,15 @@ BrainBrowserWindow::getGraphicsWidgetSize(int32_t& xOut, if ((windowWidth > 0) && (windowHeight > 0)) { - if ((windowWidth != widthOut) - || (windowHeight != heightOut)) { - xOut = windowViewport[0]; - yOut = windowViewport[1]; - widthOut = windowWidth; - heightOut = windowHeight; + if ((windowWidth != viewportWidthOut) + || (windowHeight != viewportHeightOut)) { + viewportXOut = windowViewport[0]; + viewportYOut = windowViewport[1]; + viewportWidthOut = windowWidth; + viewportHeightOut = windowHeight; } } } - -// float aspectLockedWidth = aspectViewport[2]; -// float aspectLockedHeight = aspectViewport[3]; -// if ((aspectLockedWidth > 0) -// && (aspectLockedHeight > 0)) { -// if ((widthOut != aspectLockedWidth) -// || (heightOut != aspectLockedHeight)) { -// -// std::cout << "Viewport adjusted from << " << AString::number(widthOut) << ", " << AString::number(heightOut) -// << " to " << aspectLockedWidth << ", " << aspectLockedHeight << std::endl; -// -// widthOut = aspectLockedWidth; -// heightOut = aspectLockedHeight; -// } -// } } } @@ -697,7 +612,7 @@ BrainBrowserWindow::getGraphicsWidgetSize(int32_t& xOut, bool BrainBrowserWindow::isTileTabsSelected() const { - return m_viewTileTabsSelected; + return m_browserWindowContent->isTileTabsEnabled(); } /** @@ -825,232 +740,366 @@ BrainBrowserWindow::createActionsUsedByToolBar() m_featuresToolBoxAction->setIconText("LT"); } - m_windowAspectRatioLockedAction = new QAction(this); - m_windowAspectRatioLockedAction->setCheckable(true); - m_windowAspectRatioLockedAction->setChecked(false); - m_windowAspectRatioLockedAction->setText("Lock Window Aspect"); - m_windowAspectRatioLockedAction->setToolTip("Lock aspect ratio of window"); - m_windowAspectRatioLockedAction->setShortcut(Qt::SHIFT + Qt::CTRL+Qt::Key_K); - QObject::connect(m_windowAspectRatioLockedAction, SIGNAL(toggled(bool)), - this, SLOT(processWindowAspectRatioLockedToggled(bool))); - - m_tabAspectRatioLockedAction = new QAction(this); - m_tabAspectRatioLockedAction->setCheckable(true); - m_tabAspectRatioLockedAction->setText("Lock Tab Aspect"); - m_tabAspectRatioLockedAction->setToolTip("Lock aspect ratio of selected tab"); - m_tabAspectRatioLockedAction->setShortcut(Qt::CTRL+Qt::Key_K); - QObject::connect(m_tabAspectRatioLockedAction, SIGNAL(toggled(bool)), - this, SLOT(processTabAspectRatioLockedToggled(bool))); - - m_lockAllTabsAspectRatioAction = new QAction(this); - m_lockAllTabsAspectRatioAction->setText("Lock Aspect Ratio for All Tabs"); - m_lockAllTabsAspectRatioAction->setToolTip("Lock aspect ratio of all tabs in this window"); - QObject::connect(m_lockAllTabsAspectRatioAction, SIGNAL(triggered(bool)), - this, SLOT(processLockAllTabsAspectRatioTriggered())); - - m_unlockAllTabsAspectRatioAction = new QAction(this); - m_unlockAllTabsAspectRatioAction->setText("Unlock Aspect Ratio for All Tabs"); - m_unlockAllTabsAspectRatioAction->setToolTip("Unlock aspect ratio of all tabs in this window"); - QObject::connect(m_unlockAllTabsAspectRatioAction, SIGNAL(triggered(bool)), - this, SLOT(processUnlockAllTabsAspectRatioTriggered())); + + + m_windowMenuLockWindowAspectRatioAction = new QAction(this); + m_windowMenuLockWindowAspectRatioAction->setCheckable(true); + m_windowMenuLockWindowAspectRatioAction->setChecked(m_browserWindowContent->isWindowAspectLocked()); + m_windowMenuLockWindowAspectRatioAction->setText("Lock Window Aspect Ratio"); + m_windowMenuLockWindowAspectRatioAction->setShortcut(Qt::SHIFT + Qt::CTRL+Qt::Key_K); + QObject::connect(m_windowMenuLockWindowAspectRatioAction, &QAction::triggered, + this, &BrainBrowserWindow::processWindowMenuLockWindowAspectRatioTriggered); + + m_windowMenuLockAllTabAspectRatioAction = new QAction(this); + m_windowMenuLockAllTabAspectRatioAction->setCheckable(true); + m_windowMenuLockAllTabAspectRatioAction->setChecked(false); + m_windowMenuLockAllTabAspectRatioAction->setText("Lock All Tab Aspect Ratios"); + m_windowMenuLockAllTabAspectRatioAction->setShortcut(Qt::CTRL+Qt::Key_K); + QObject::connect(m_windowMenuLockAllTabAspectRatioAction, &QAction::triggered, + this, &BrainBrowserWindow::processWindowMenuLockAllTabAspectRatioTriggered); + + const QString aspectButtonToolTipText("" + "Lock the aspect ratio of the window and all tabs. " + "Aspect ratios should be locked prior to creating " + "annotations and remain locked while annotations are " + "present." + "

" + "Advanced users can right-click (control-click on Mac) " + "to manually adjust the aspect " + "ratio for the selected tab or the window." + ""); + + m_toolBarLockWindowAndAllTabAspectRatioAction = new QAction(); + m_toolBarLockWindowAndAllTabAspectRatioAction->setCheckable(true); + m_toolBarLockWindowAndAllTabAspectRatioAction->setChecked(false); + m_toolBarLockWindowAndAllTabAspectRatioAction->setText("Lock Aspect"); + m_toolBarLockWindowAndAllTabAspectRatioAction->setToolTip(aspectButtonToolTipText); + QObject::connect(m_toolBarLockWindowAndAllTabAspectRatioAction, &QAction::triggered, + this, &BrainBrowserWindow::processToolBarLockWindowAndAllTabAspectTriggered); + + /* + * Button for locking aspect is passed to ToolBar's constructor + * and is displayed on the toolbar + */ + m_toolBarLockWindowAndAllTabAspectRatioButton = new QToolButton(); + m_toolBarLockWindowAndAllTabAspectRatioButton->setDefaultAction(m_toolBarLockWindowAndAllTabAspectRatioAction); + WuQtUtilities::setToolButtonStyleForQt5Mac(m_toolBarLockWindowAndAllTabAspectRatioButton); + QObject::connect(m_toolBarLockWindowAndAllTabAspectRatioButton, &QToolButton::customContextMenuRequested, + this, &BrainBrowserWindow::processToolBarLockWindowAndAllTabAspectMenu); + m_toolBarLockWindowAndAllTabAspectRatioButton->setContextMenuPolicy(Qt::CustomContextMenu); } /** - * Called when Lock Aspect Ratio for All Tabs is selected. + * Is called by toolbar when the user changes input mode to annotations. + * + * @return + * True if it is okay to change to annotations mode, else false. + */ +bool +BrainBrowserWindow::changeInputModeToAnnotationsWarningDialog() +{ + LockAspectWarningDialog::Result result = LockAspectWarningDialog::runDialog(m_browserWindowIndex); + + bool okFlag = true; + + switch (result) { + case LockAspectWarningDialog::Result::CANCEL: + okFlag = false; + break; + case LockAspectWarningDialog::Result::LOCK_ASPECT: + processToolBarLockWindowAndAllTabAspectTriggered(true); + break; + case LockAspectWarningDialog::Result::NO_CHANGES: + break; + } + + return okFlag; +} + +/** + * Called when the window menu's lock window aspect is triggered by user. + * + * @param checked + * True if checked, false if unchecked */ void -BrainBrowserWindow::processLockAllTabsAspectRatioTriggered() +BrainBrowserWindow::processWindowMenuLockWindowAspectRatioTriggered(bool checked) { + lockWindowAspectRatio(checked); + updateActionsForLockingAspectRatios(); +} + +/** + * Called when the window menu's lock all tab aspect is triggered by user + * + * @param checked + * True if checked, false if unchecked + */ +void +BrainBrowserWindow::processWindowMenuLockAllTabAspectRatioTriggered(bool checked) +{ + lockAllTabAspectRatios(checked); + updateActionsForLockingAspectRatios(); +} + +/** + * Lock the window's aspect ratio and if it transitions from + * unlocked to locked, set the aspect ratio. + */ +void +BrainBrowserWindow::lockWindowAspectRatio(const bool checked) +{ + const bool oldCheckedStatus = m_browserWindowContent->isWindowAspectLocked(); + m_browserWindowContent->setWindowAspectLocked(checked); - std::vector vpContent = m_openGLWidget->getViewportContent(); + if (checked) { + if ( ! oldCheckedStatus) { + m_browserWindowContent->setWindowAspectLockedRatio(getOpenGLWidgetAspectRatio()); + } + } +} - if (isTileTabsSelected()) { +/** + * Lock the window's aspect ratio and if it transitions from + * unlocked to locked, set the aspect ratio. + */ +void +BrainBrowserWindow::lockAllTabAspectRatios(const bool checked) +{ + m_browserWindowContent->setAllTabsInWindowAspectRatioLocked(checked); + + if (checked) { /* - * When tile tabs is on, set the aspect ratio for each tab that does not - * already have its aspect ratio locked + * Locking takes place in BrainOpenGLViewportContent + * when the tab is drawn as the size of the viewport + * is needed. */ - for (std::vector::iterator vpIter = vpContent.begin(); - vpIter != vpContent.end(); - vpIter++) { - const BrainOpenGLViewportContent* vp = *vpIter; - CaretAssert(vp); - - BrowserTabContent* tabContent = vp->getBrowserTabContent(); - if (tabContent != NULL) { - if ( ! tabContent->isAspectRatioLocked()) { - int32_t tabViewport[4]; - vp->getTabViewportBeforeApplyingMargins(tabViewport); - - const float width = tabViewport[2]; - if (width > 0.0) { - const float aspectRatio = tabViewport[3] / width; - - tabContent->setAspectRatioLocked(true); - tabContent->setAspectRatio(aspectRatio); - } - } - } - } } else { /* - * Set the aspect ratio for each tab that does not already have its - * aspect ratio locked. Since only one tab is displayed, use the - * size of the graphics region for the aspect ratio. + * Turn off lock aspect for all tabs */ + std::vector allTabContent; + m_toolbar->getAllTabContent(allTabContent); - if ( ! vpContent.empty()) { - CaretAssertVectorIndex(vpContent, 0); - const BrainOpenGLViewportContent* vp = vpContent[0]; - - int32_t tabViewport[4]; - vp->getTabViewportBeforeApplyingMargins(tabViewport); - - const float width = tabViewport[2]; - if (width > 0.0) { - const float aspectRatio = tabViewport[3] / width; - - std::vector allTabContent; - getAllTabContent(allTabContent); - - for (std::vector::iterator iter = allTabContent.begin(); - iter != allTabContent.end(); - iter++) { - BrowserTabContent* tabContent = *iter; - CaretAssert(tabContent); - if ( ! tabContent->isAspectRatioLocked()) { - tabContent->setAspectRatioLocked(true); - tabContent->setAspectRatio(aspectRatio); - } - } - } + for (auto tab : allTabContent) { + tab->setAspectRatioLocked(false); } } - - EventManager::get()->sendEvent(EventGraphicsUpdateOneWindow(getBrowserWindowIndex()).getPointer()); - m_toolbar->updateToolBar(); } /** - * Called when Unlock Aspect Ratio for All Tabs is selected. + * Called when the toolbar's lock window and all tab aspect is triggered + * + * @param checked + * True if checked, false if unchecked */ void -BrainBrowserWindow::processUnlockAllTabsAspectRatioTriggered() +BrainBrowserWindow::processToolBarLockWindowAndAllTabAspectTriggered(bool checked) { - std::vector allTabContent; - getAllTabContent(allTabContent); - - for (std::vector::iterator iter = allTabContent.begin(); - iter != allTabContent.end(); - iter++) { - BrowserTabContent* tabContent = *iter; - CaretAssert(tabContent); - tabContent->setAspectRatioLocked(false); - } - - EventManager::get()->sendEvent(EventGraphicsUpdateOneWindow(getBrowserWindowIndex()).getPointer()); - m_toolbar->updateToolBar(); + lockWindowAspectRatio(checked); + lockAllTabAspectRatios(checked); + updateActionsForLockingAspectRatios(); } /** - * Called when the Tab aspect ratio action it toggled. + * Called when an item is selected from the lock aspect action menu * - * @param new tab locked status + * @param pos + * Location of click. */ void -BrainBrowserWindow::processTabAspectRatioLockedToggled(bool checked) -{ - BrowserTabContent* selectedTab = m_toolbar->getTabContentFromSelectedTab(); - if (selectedTab != NULL) { - std::vector vpContent = m_openGLWidget->getViewportContent(); - for (std::vector::iterator vpIter = vpContent.begin(); - vpIter != vpContent.end(); - vpIter++) { - const BrainOpenGLViewportContent* vp = *vpIter; - CaretAssert(vp); +BrainBrowserWindow::processToolBarLockWindowAndAllTabAspectMenu(const QPoint& pos) +{ + BrowserTabContent* tabContent = getBrowserTabContent(); + if (tabContent != NULL) { + QMenu menu; + QAction* tabAspectAction = menu.addAction("Set Selected Tab Aspect Ratio..."); + QAction* windowAspectAction = menu.addAction("Set Window Aspect Ratio..."); + + QAction* selectedAction = menu.exec(m_toolBarLockWindowAndAllTabAspectRatioButton->parentWidget()->mapToGlobal(pos)); + + if (selectedAction == windowAspectAction) { + float aspectRatio = getAspectRatioFromDialog(AspectRatioMode::WINDOW, + ("Window " + + AString::number(m_browserWindowIndex + 1) + + " Aspect Ratio"), + getAspectRatio(), + this); + if (aspectRatio > 0.0f) { + aspectRatioDialogUpdateForWindow(aspectRatio); + } + } + else if (selectedAction == tabAspectAction) { + float aspectRatio = tabContent->getAspectRatio(); - BrowserTabContent* tabContent = vp->getBrowserTabContent(); - if (selectedTab == tabContent) { - int32_t tabViewport[4]; - vp->getTabViewportBeforeApplyingMargins(tabViewport); + if ( ! tabContent->isAspectRatioLocked()) { + /* + * When aspect is NOT locked, need to get current aspect ratio + */ + aspectRatio = getOpenGLWidgetAspectRatio(); - const float width = tabViewport[2]; - if (width > 0.0) { - const float aspectRatio = tabViewport[3] / width; - - tabContent->setAspectRatioLocked(checked); - if (checked) { - tabContent->setAspectRatio(aspectRatio); + if (isTileTabsSelected()) { + /* + * When tile tabs is enabled, find the tab to get its aspect ratio + */ + std::vector allViewportContent = m_openGLWidget->getViewportContent(); + for (auto vc : allViewportContent) { + if (vc->getTabIndex() == tabContent->getTabNumber()) { + int viewport[4]; + vc->getModelViewport(viewport); + if (viewport[3] > 0) { + aspectRatio = (static_cast(viewport[3]) + / static_cast(viewport[2])); + break; + } + } } } } + + aspectRatio = getAspectRatioFromDialog(AspectRatioMode::TAB, + ("Tab " + + tabContent->getName() + + " Aspect Ratio"), + aspectRatio, + this); + if (aspectRatio > 0.0f) { + aspectRatioDialogUpdateForTab(aspectRatio); + } } } +} + +/** + * Get the new aspect ratio using a dialog. + * + * @param aspectRatioMode + * Mode (tab or window) + * @param title + * Title for dialog. + * @param originalAspectRatio + * Default value for aspect ratio + * @param parent + * Parent for the dialog. + */ +float +BrainBrowserWindow::getAspectRatioFromDialog(const AspectRatioMode aspectRatioMode, + const QString& title, + const float aspectRatio, + QWidget* parent) const +{ + float aspectRatioOut = -1.0; + + WuQDataEntryDialog ded("Set Aspect Ratio", + parent); + ded.setCancelButtonText(""); + QDoubleSpinBox* ratioSpinBox = ded.addDoubleSpinBox(title, + aspectRatio); + ratioSpinBox->setSingleStep(0.01); + ratioSpinBox->setRange(ratioSpinBox->singleStep(), 100.0); + ratioSpinBox->setDecimals(3); + ratioSpinBox->setKeyboardTracking(true); + + switch (aspectRatioMode) { + case AspectRatioMode::TAB: + QObject::connect(ratioSpinBox, static_cast(&QDoubleSpinBox::valueChanged), + this, &BrainBrowserWindow::aspectRatioDialogUpdateForTab); + break; + case AspectRatioMode::WINDOW: + QObject::connect(ratioSpinBox, static_cast(&QDoubleSpinBox::valueChanged), + this, &BrainBrowserWindow::aspectRatioDialogUpdateForWindow); + break; + } + if (ded.exec() == WuQDataEntryDialog::Accepted) { + aspectRatioOut = ratioSpinBox->value(); + } - EventManager::get()->sendEvent(EventGraphicsUpdateOneWindow(getBrowserWindowIndex()).getPointer()); + return aspectRatioOut; } /** - * Called when the Window aspect ratio action it toggled. + * Called when the user manually adjust aspect ratio for all tabs. * - * @param new tab locked status + * @param aspectRatio + * New aspect ratio. */ void -BrainBrowserWindow::processWindowAspectRatioLockedToggled(bool checked) +BrainBrowserWindow::aspectRatioDialogUpdateForTab(const double aspectRatio) { - setAspectRatioLocked(checked); - if (checked) { - const float aspectRatio = getOpenGLWidgetAspectRatio(); - if (aspectRatio > 0) { - setAspectRatio(aspectRatio); - } + BrowserTabContent* tabContent = getBrowserTabContent(); + + if (aspectRatio > 0.0f) { + lockAllTabAspectRatios(true); + tabContent->setAspectRatio(aspectRatio); + tabContent->setAspectRatioLocked(true); } + updateActionsForLockingAspectRatios(); EventManager::get()->sendEvent(EventGraphicsUpdateOneWindow(getBrowserWindowIndex()).getPointer()); } /** - * @return Is the aspect ratio locked? + * Called when the user manually adjust aspect ratio for window. + * + * @param aspectRatio + * New aspect ratio. */ -bool -BrainBrowserWindow::isAspectRatioLocked() const +void +BrainBrowserWindow::aspectRatioDialogUpdateForWindow(const double aspectRatio) { - return m_windowAspectRatioLockedAction->isChecked(); + if (aspectRatio > 0.0f) { + lockWindowAspectRatio(true); + m_browserWindowContent->setWindowAspectLockedRatio(aspectRatio); + } + + updateActionsForLockingAspectRatios(); + EventManager::get()->sendEvent(EventGraphicsUpdateOneWindow(getBrowserWindowIndex()).getPointer()); } /** - * Set the aspect ratio locked status. - * - * @param locked - * New status. + * Update the actions for window menu's lock window aspect, window menu's lock + * all tab aspect, and the toolbar's lock window and all tab aspect. */ void -BrainBrowserWindow::setAspectRatioLocked(const bool locked) +BrainBrowserWindow::updateActionsForLockingAspectRatios() { - m_windowAspectRatioLockedAction->blockSignals(true); - m_windowAspectRatioLockedAction->setChecked(locked); - m_windowAspectRatioLockedAction->blockSignals(false); + QSignalBlocker windowAspectBlocker(m_windowMenuLockWindowAspectRatioAction); + m_windowMenuLockWindowAspectRatioAction->setChecked(m_browserWindowContent->isWindowAspectLocked()); + + QSignalBlocker tabAspectBlocker(m_windowMenuLockAllTabAspectRatioAction); + m_windowMenuLockAllTabAspectRatioAction->setChecked(m_browserWindowContent->isAllTabsInWindowAspectRatioLocked()); + + QSignalBlocker toolbarLockAllBlocker(m_toolBarLockWindowAndAllTabAspectRatioAction); + m_toolBarLockWindowAndAllTabAspectRatioAction->setChecked(m_browserWindowContent->isWindowAspectLocked() + && m_browserWindowContent->isAllTabsInWindowAspectRatioLocked()); + + EventManager::get()->sendEvent(EventGraphicsUpdateOneWindow(getBrowserWindowIndex()).getPointer()); } /** - * @return The aspect ratio. + * @return Is the aspect ratio locked? */ -float -BrainBrowserWindow::getAspectRatio() const +bool +BrainBrowserWindow::isWindowAspectRatioLocked() const { - return m_aspectRatio; + return m_browserWindowContent->isWindowAspectLocked(); } /** - * Set the aspect ratio. - * - * @param aspectRatio - * New value for aspect ratio. + * @return The aspect ratio. */ -void -BrainBrowserWindow::setAspectRatio(const float aspectRatio) +float +BrainBrowserWindow::getAspectRatio() const { - m_aspectRatio = aspectRatio; + if ( ! m_browserWindowContent->isWindowAspectLocked()) { + /* + * When aspect NOT locked, use the OpenGL widget's aspect ratio + */ + m_browserWindowContent->setWindowAspectLockedRatio(getOpenGLWidgetAspectRatio()); + } + + return m_browserWindowContent->getWindowAspectLockedRatio(); } /** @@ -1525,7 +1574,6 @@ BrainBrowserWindow::createMenuFile() QObject::connect(m_recentSceneFileMenu, SIGNAL(triggered(QAction*)), this, SLOT(processRecentSceneFileMenuSelection(QAction*))); - //menu->addAction(m_openFileViaSpecFileAction); menu->addAction(m_manageFilesAction); menu->addAction(m_closeSpecFileAction); menu->addSeparator(); @@ -1956,8 +2004,6 @@ BrainBrowserWindow::loadRecentSpecFileMenu(QMenu* recentSpecFileMenu) void BrainBrowserWindow::processRecentSpecFileMenuSelection(QAction* itemAction) { - //AString errorMessages; - const AString specFileName = itemAction->data().toString(); if (specFileName == "CLEAR_CLEAR") { CaretPreferences* prefs = SessionManager::get()->getCaretPreferences(); @@ -1989,7 +2035,6 @@ BrainBrowserWindow::processRecentSpecFileMenuSelection(QAction* itemAction) } } catch (const DataFileException& e) { - //errorMessages += e.whatString(); QMessageBox::critical(this, "ERROR", e.whatString()); @@ -2082,8 +2127,6 @@ BrainBrowserWindow::loadRecentSceneFileMenu(QMenu* recentSceneFileMenu) void BrainBrowserWindow::processRecentSceneFileMenuSelection(QAction* itemAction) { - //AString errorMessages; - const AString sceneFileName = itemAction->data().toString(); if (sceneFileName == "CLEAR_CLEAR") { CaretPreferences* prefs = SessionManager::get()->getCaretPreferences(); @@ -2237,10 +2280,9 @@ BrainBrowserWindow::processTileTabsMenuAboutToBeDisplayed() /* * Add the scene configuration */ - QAction* sceneAction = m_tileTabsMenu->addAction(m_sceneTileTabsConfiguration->getName()); + QAction* sceneAction = m_tileTabsMenu->addAction(m_browserWindowContent->getSceneTileTabsConfiguration()->getName()); sceneAction->setCheckable(true); - sceneAction->setData(QVariant(m_sceneTileTabsConfiguration->getUniqueIdentifier())); - //sceneAction->setEnabled(false); + sceneAction->setData(QVariant(m_browserWindowContent->getSceneTileTabsConfiguration()->getUniqueIdentifier())); bool haveSelectedTileTabsConfiguration = false; @@ -2262,7 +2304,7 @@ BrainBrowserWindow::processTileTabsMenuAboutToBeDisplayed() } } - if (m_selectedTileTabsConfigurationUniqueIdentifier == m_sceneTileTabsConfiguration->getUniqueIdentifier()) { + if (m_selectedTileTabsConfigurationUniqueIdentifier == m_browserWindowContent->getSceneTileTabsConfiguration()->getUniqueIdentifier()) { haveSelectedTileTabsConfiguration = true; sceneAction->setChecked(true); } @@ -2333,11 +2375,11 @@ BrainBrowserWindow::getSelectedTileTabsConfiguration() */ return m_defaultTileTabsConfiguration; } - else if (m_selectedTileTabsConfigurationUniqueIdentifier == m_sceneTileTabsConfiguration->getUniqueIdentifier()) { + else if (m_selectedTileTabsConfigurationUniqueIdentifier == m_browserWindowContent->getSceneTileTabsConfiguration()->getUniqueIdentifier()) { /* * Scene's configuration is selected */ - return m_sceneTileTabsConfiguration; + return m_browserWindowContent->getSceneTileTabsConfiguration(); } else { /* @@ -2532,8 +2574,6 @@ BrainBrowserWindow::processSurfaceMenuInformation() QMenu* BrainBrowserWindow::createMenuVolume() { -// QMenu* menu = new QMenu("Volume", this); -// return menu; return NULL; } @@ -2551,11 +2591,9 @@ BrainBrowserWindow::createMenuWindow() this, SLOT(processMoveSelectedTabToWindowMenuSelection(QAction*))); QMenu* menu = new QMenu("Window", this); - - menu->addAction(m_windowAspectRatioLockedAction); - menu->addAction(m_tabAspectRatioLockedAction); - menu->addAction(m_lockAllTabsAspectRatioAction); - menu->addAction(m_unlockAllTabsAspectRatioAction); + + menu->addAction(m_windowMenuLockWindowAspectRatioAction); + menu->addAction(m_windowMenuLockAllTabAspectRatioAction); menu->addSeparator(); menu->addAction(m_nextTabAction); @@ -2709,7 +2747,7 @@ BrainBrowserWindow::processDevelopExportVtkFile() "Export to VTK File", GuiManager::get()->getBrain()->getCurrentDirectory(), vtkSurfaceFileFilter); - cfd.selectFilter(vtkSurfaceFileFilter); + cfd.selectNameFilter(vtkSurfaceFileFilter); cfd.setAcceptMode(QFileDialog::AcceptSave); cfd.setFileMode(CaretFileDialog::AnyFile); if (previousVtkFileName.isEmpty() == false) { @@ -2768,7 +2806,6 @@ BrainBrowserWindow::processSplitBorderFiles() void BrainBrowserWindow::processCaptureImage() { - //std::cout << "Toolbar height " << m_toolbar->height() << std::endl; GuiManager::get()->processShowImageCaptureDialog(this); } @@ -2997,12 +3034,32 @@ BrainBrowserWindow::loadFilesFromCommandLine(const std::vector& filenam { std::vector dataFileTypesDummyNotUsed; + AString userName; + AString password; + switch (loadSpecFileMode) { + case LOAD_SPEC_FILE_CONTENTS_VIA_COMMAND_LINE: + { + /* + * Spec file might contain a file on the network so try + * using the saved username and password. + */ + CaretPreferences* prefs = SessionManager::get()->getCaretPreferences(); + prefs->getRemoteFileUserNameAndPassword(userName, + password); + } + break; + case LOAD_SPEC_FILE_WITH_DIALOG: + break; + case LOAD_SPEC_FILE_WITH_DIALOG_VIA_COMMAND_LINE: + break; + } + loadFiles(this, filenames, dataFileTypesDummyNotUsed, loadSpecFileMode, - "", - ""); + userName, + password); } /** @@ -3031,14 +3088,15 @@ BrainBrowserWindow::loadSceneFromCommandLine(const AString& sceneFileName, const int32_t numSceneFiles = brain->getNumberOfSceneFiles(); for (int32_t i = 0; i < numSceneFiles; i++) { SceneFile* sf = brain->getSceneFile(i); - if (sf->getFileName().contains(sceneFileName)) { + FileInformation fileInfo(sceneFileName); + if (sf->getFileName().contains(fileInfo.getFileName())) { haveSceneFileError = false; Scene* scene = sf->getSceneWithName(sceneNameOrNumber); if (scene == NULL) { bool isValidNumber = false; int sceneNumber = sceneNameOrNumber.toInt(&isValidNumber); if (isValidNumber) { - sceneNumber--; // convert to index (numbers start at one) + sceneNumber--; /* convert to index (numbers start at one) */ if ((sceneNumber >= 0) && (sceneNumber < sf->getNumberOfScenes())) { scene = sf->getSceneAtIndex(sceneNumber); @@ -3068,6 +3126,8 @@ BrainBrowserWindow::loadSceneFromCommandLine(const AString& sceneFileName, + "\" found in scene file."); WuQMessageBox::errorOk(this, msg); } + + /* NOTE: File warning dialog is performed by scene dialog */ } @@ -3230,7 +3290,7 @@ BrainBrowserWindow::loadFiles(QWidget* parentForDialogs, switch (loadSpecFileMode) { case LOAD_SPEC_FILE_CONTENTS_VIA_COMMAND_LINE: { - timer.reset(); // resets timer + timer.reset(); /* resets timer */ specFileTimeStart = timer.getElapsedTimeSeconds(); /* @@ -3412,6 +3472,8 @@ BrainBrowserWindow::loadFiles(QWidget* parentForDialogs, EventManager::get()->sendEvent(EventMacDockMenuUpdate().getPointer()); + showDataFileReadWarningsDialog(); + return successFlag; } @@ -3496,7 +3558,7 @@ BrainBrowserWindow::processViewFullScreenSelected() void BrainBrowserWindow::processViewTileTabs() { - const bool toggledStatus = (! m_viewTileTabsSelected); + const bool toggledStatus = (! isTileTabsSelected()); setViewTileTabs(toggledStatus); } @@ -3507,7 +3569,7 @@ BrainBrowserWindow::processViewTileTabs() void BrainBrowserWindow::setViewTileTabs(const bool newStatus) { - m_viewTileTabsSelected = newStatus; + m_browserWindowContent->setTileTabsEnabled(newStatus); EventManager::get()->sendEvent(EventUserInterfaceUpdate().setWindowIndex(m_browserWindowIndex).addToolBar().getPointer()); EventManager::get()->sendEvent(EventGraphicsUpdateOneWindow(m_browserWindowIndex).getPointer()); } @@ -3608,7 +3670,7 @@ BrainBrowserWindow::saveWindowComponentStatus(WindowComponentStatus& wcs) /** * Adds a new tab to the window. */ -void +void BrainBrowserWindow::processNewTab() { m_toolbar->addNewTab(); @@ -3621,7 +3683,7 @@ void BrainBrowserWindow::processDuplicateTab() { BrowserTabContent* previousTabContent = getBrowserTabContent(); - m_toolbar->addNewTabCloneContent(previousTabContent); + m_toolbar->addNewDuplicatedTab(previousTabContent); } /** @@ -3975,6 +4037,23 @@ BrainBrowserWindow::getAllTabContentIndices(std::vector& allTabContentI } } +/** + * @return Browser window's content. + */ +BrowserWindowContent* +BrainBrowserWindow::getBrowerWindowContent() +{ + return m_browserWindowContent; +} + +/** + * @return Browser window's content (const method). + */ +const BrowserWindowContent* +BrainBrowserWindow::getBrowerWindowContent() const +{ + return m_browserWindowContent; +} /** * Returns a popup menu for the main window. @@ -4050,6 +4129,36 @@ BrainBrowserWindow::getOpenGLWidgetAspectRatio() const return aspectRatio; } +/** + * BrowserWindowContent is owned and saved to and restored from + * Scenes by SessionManager. Since SessionManager is save to + * Scene before instances of this class, update content + * of BrowserWindowContent. + */ +void +BrainBrowserWindow::saveBrowserWindowContentForScene() +{ + std::vector allTabIndices; + getAllTabContentIndices(allTabIndices); + m_browserWindowContent->setSceneWindowTabIndices(allTabIndices); + + const BrowserTabContent* activeTabContent = getBrowserTabContent(); + if (activeTabContent != NULL) { + m_browserWindowContent->setSceneSelectedTabIndex(activeTabContent->getTabNumber()); + } + + if (isTileTabsSelected()) { + const TileTabsConfiguration* tileTabsConfig = getSelectedTileTabsConfiguration(); + if (tileTabsConfig != NULL) { + m_browserWindowContent->copyTileTabsConfigurationForSavingScene(tileTabsConfig); + } + } + + m_browserWindowContent->setSceneGraphicsWidth(m_openGLWidget->width()); + m_browserWindowContent->setSceneGraphicsHeight(m_openGLWidget->height()); +} + + /** * Create a scene for an instance of a class. * @@ -4068,37 +4177,15 @@ BrainBrowserWindow::saveToScene(const SceneAttributes* sceneAttributes, { SceneClass* sceneClass = new SceneClass(instanceName, "BrainBrowserWindow", - 1); + 2); /* Version 2: 07 feb 2018 */ m_sceneAssistant->saveMembers(sceneAttributes, sceneClass); - + + /* m_browserWindowIndex used with wb_command -show-scene */ sceneClass->addInteger("m_browserWindowIndex", m_browserWindowIndex); - sceneClass->addBoolean("m_windowAspectRatioLockedAction", - m_windowAspectRatioLockedAction->isChecked()); - /* - * Save the selected tile tabs configuration as the scene configuration - */ - if (m_viewTileTabsSelected) { - /* - * Note: The number of rows and columns in the default tile tabs - * configuration is updated each time the graphics region of the - * window is drawn in BrainOpenGLWidget::paintGL(). - */ - const TileTabsConfiguration* tileTabs = getSelectedTileTabsConfiguration(); - if (tileTabs != NULL) { - TileTabsConfiguration writeConfig(*tileTabs); - writeConfig.setName(QUuid::createUuid().toString()); - sceneClass->addString("m_sceneTileTabsConfiguration", - writeConfig.encodeInXML()); - -// sceneClass->addString("m_sceneTileTabsConfiguration", -// tileTabs->encodeInXML()); - } - } - /* * Save toolbar */ @@ -4166,11 +4253,9 @@ BrainBrowserWindow::saveToScene(const SceneAttributes* sceneAttributes, isFullScreen()); sceneClass->addBoolean("isMaximized", isMaximized()); - sceneClass->addBoolean("m_viewTileTabsAction", - m_viewTileTabsSelected); /* - * Graphics position and size + * Graphics position and size, used with wb_command -show-scene */ SceneWindowGeometry openGLGeometry(m_openGLWidget); sceneClass->addClass(openGLGeometry.saveToScene(sceneAttributes, @@ -4198,6 +4283,8 @@ BrainBrowserWindow::restoreFromScene(const SceneAttributes* sceneAttributes, return; } + const int32_t sceneVersion = sceneClass->getVersionNumber(); + /* * Restore toolbar */ @@ -4210,11 +4297,11 @@ BrainBrowserWindow::restoreFromScene(const SceneAttributes* sceneAttributes, m_sceneAssistant->restoreMembers(sceneAttributes, sceneClass); - const bool aspectRatioLocked = sceneClass->getBooleanValue("m_windowAspectRatioLockedAction", - false); - m_windowAspectRatioLockedAction->blockSignals(true); - m_windowAspectRatioLockedAction->setChecked(aspectRatioLocked); - m_windowAspectRatioLockedAction->blockSignals(false); + if ( ! m_browserWindowContent->isValid()) { + sceneAttributes->addToErrorMessage("Scene Error: Browser window content is invalid for window " + + AString::number(m_browserWindowIndex + 1)); + return; + } /* * Restore Unique ID of selected tile tabs configuration. @@ -4265,21 +4352,21 @@ BrainBrowserWindow::restoreFromScene(const SceneAttributes* sceneAttributes, else { restoreToFullScreen = sceneClass->getBooleanValue("isFullScreen", false); - restoreToTabTiles = sceneClass->getBooleanValue("m_viewTileTabsAction", - false); + + if (sceneVersion >= 2) { + restoreToTabTiles = m_browserWindowContent->isTileTabsEnabled(); + } + else { + restoreToTabTiles = sceneClass->getBooleanValue("m_viewTileTabsAction", + false); + } + setViewTileTabs(restoreToTabTiles); /* * If tile tabs was saved to the scene, restore it as the scenes tile tabs configuration */ if (restoreToTabTiles) { - const AString tileTabsConfigString = sceneClass->getStringValue("m_sceneTileTabsConfiguration"); - if ( ! tileTabsConfigString.isEmpty()) { - m_sceneTileTabsConfiguration->decodeFromXML(tileTabsConfigString); - m_sceneTileTabsConfiguration->setName(m_sceneTileTabsConfigurationText - + " " - + sceneAttributes->getSceneName()); - m_selectedTileTabsConfigurationUniqueIdentifier = m_sceneTileTabsConfiguration->getUniqueIdentifier(); - } + m_selectedTileTabsConfigurationUniqueIdentifier = m_browserWindowContent->getSceneTileTabsConfiguration()->getUniqueIdentifier(); } } @@ -4287,8 +4374,6 @@ BrainBrowserWindow::restoreFromScene(const SceneAttributes* sceneAttributes, processViewFullScreen(restoreToFullScreen, false); - setViewTileTabs(restoreToTabTiles); - /* * Position and size */ @@ -4323,6 +4408,28 @@ BrainBrowserWindow::restoreFromScene(const SceneAttributes* sceneAttributes, m_featuresToolBoxAction->trigger(); m_featuresToolBox->restoreFromScene(sceneAttributes, sceneClass->getClass("m_featuresToolBox")); + +#if QT_VERSION >= 0x050600 + /* + * Toolboxes were not restoring to correct size in Qt5. + * Qt5.6 adds a new method, QMainWindow::resizeDocks() that + * resizes a QDockWidget in one dimension. Use it to resize + * the toolbox. + */ + const SceneClass* featureToolBoxClass = sceneClass->getClass("m_featuresToolBox"); + if (featureToolBoxClass != NULL) { + const int w = featureToolBoxClass->getIntegerValue("toolboxWidth", -1); + const int h = featureToolBoxClass->getIntegerValue("toolboxHeight", -1); + if ((w > 0) + && (h > 0)) { + QList dockList; + dockList.append(m_featuresToolBox); + QList sizeList; + sizeList.append(w); + resizeDocks(dockList, sizeList, Qt::Horizontal); + } + } +#endif } /* @@ -4348,6 +4455,36 @@ BrainBrowserWindow::restoreFromScene(const SceneAttributes* sceneAttributes, processShowOverlayToolBox(toolBoxVisible); m_overlayActiveToolBox->restoreFromScene(sceneAttributes, sceneClass->getClass("m_overlayActiveToolBox")); + + +#if QT_VERSION >= 0x050600 + /* + * Toolboxes were not restoring to correct size in Qt5. + * Qt5 adds a new method, QMainWindow::resizeDocks() that + * resizes a QDockWidget in one dimension. Use it to resize + * the toolbox. + */ + const SceneClass* activeToolBoxClass = sceneClass->getClass("m_overlayActiveToolBox"); + if (activeToolBoxClass != NULL) { + const int w = activeToolBoxClass->getIntegerValue("toolboxWidth", -1); + const int h = activeToolBoxClass->getIntegerValue("toolboxHeight", -1); + if ((w > 0) + && (h > 0)) { + QList dockList; + dockList.append(m_overlayActiveToolBox); + if (orientationName == "horizontal") { + QList sizeList; + sizeList.append(h); + resizeDocks(dockList, sizeList, Qt::Vertical); + } + else { + QList sizeList; + sizeList.append(w); + resizeDocks(dockList, sizeList, Qt::Horizontal); + } + } + } +#endif } } @@ -4363,21 +4500,35 @@ BrainBrowserWindow::restoreFromScene(const SceneAttributes* sceneAttributes, if (maximizedWindow) { showMaximized(); } -} + + /* prevent "failed to restore" messages */ + sceneClass->getIntegerValue("m_browserWindowIndex"); + const SceneClass* openglGeomClass = sceneClass->getClass("openGLWidgetGeometry"); + if (openglGeomClass != NULL) { + openglGeomClass->setDescendantsRestored(true); + } + + if (sceneVersion >= 2) { + + } + else { + /* + * Before version 2, each tab was allowed its own aspect locking status + */ + std::vector allTabContent; + m_toolbar->getAllTabContent(allTabContent); + + const int32_t lockedCount = std::count_if(allTabContent.begin(), + allTabContent.end(), + [](BrowserTabContent* btc) { return btc->isAspectRatioLocked(); }); -///** -// * Get the viewport size for the window. -// * -// * @param w -// * Output width. -// * @param h -// * Output height. -// */ -//void -//BrainBrowserWindow::getViewportSize(int &w, int &h) -//{ -// m_openGLWidget->getViewPortSize(w,h); -//} + EventManager::get()->sendEvent(EventGraphicsUpdateOneWindow(getBrowserWindowIndex()).getPointer()); + + lockAllTabAspectRatios(lockedCount > 0); + } + + updateActionsForLockingAspectRatios(); +} /** * @return A string describing the object's content. @@ -4434,6 +4585,53 @@ BrainBrowserWindow::getDescriptionOfContent(PlainTextStringBuilder& descriptionO descriptionOut.popIndentation(); } +/** + * @return Has valid OpenGL. + */ +bool +BrainBrowserWindow::hasValidOpenGL() +{ + return m_openGLWidget->isValid(); +} +/** + * Show the data file read warnings dialog but only + * if warnings are found in any files. + */ +void +BrainBrowserWindow::showDataFileReadWarningsDialog() +{ + Brain* brain = GuiManager::get()->getBrain(); + if (brain == NULL) { + return; + } + + std::vector dataFiles; + brain->getAllDataFiles(dataFiles); + + AString messages; + for (auto file : dataFiles) { + const AString msg = file->getFileReadWarnings(); + if ( ! msg.isEmpty()) { + messages.append("

  • " + + file->getFileName() + + "
    " + + msg + + "
  • "); + } + } + + if ( ! messages.isEmpty()) { + messages.insert(0, + "
      "); + messages.append("
    "); + + WuQTextEditorDialog::runNonModal("Data File Warnings", + messages, + WuQTextEditorDialog::TextMode::HTML, + WuQTextEditorDialog::WrapMode::NO, + this); + } +} diff --git a/src/GuiQt/BrainBrowserWindow.h b/src/GuiQt/BrainBrowserWindow.h index fcc5e2c437d3b7de9c742b255ffbde2a6f9c534d..f53afc2da2bd6930fc635bf564ff530897e78a5a 100644 --- a/src/GuiQt/BrainBrowserWindow.h +++ b/src/GuiQt/BrainBrowserWindow.h @@ -22,6 +22,7 @@ */ /*LICENSE_END*/ +#include #include #include @@ -36,11 +37,13 @@ class QAction; class QActionGroup; class QDockWidget; class QMenu; +class QToolButton; namespace caret { class BrainBrowserWindowToolBar; class BrainBrowserWindowOrientedToolBox; class BrainOpenGLWidget; + class BrowserWindowContent; class BrowserTabContent; class PlainTextStringBuilder; class SceneClassAssistant; @@ -66,6 +69,9 @@ namespace caret { BrowserTabContent* getBrowserTabContent(int tabIndex); + BrowserWindowContent* getBrowerWindowContent(); + + const BrowserWindowContent* getBrowerWindowContent() const; QMenu* createPopupMenu(); @@ -109,8 +115,6 @@ namespace caret { virtual void restoreFromScene(const SceneAttributes* sceneAttributes, const SceneClass* sceneClass); -// void getViewportSize(int &w, int &h); - TileTabsConfiguration* getSelectedTileTabsConfiguration(); void setSelectedTileTabsConfiguration(TileTabsConfiguration* configuration); @@ -138,14 +142,16 @@ namespace caret { float getOpenGLWidgetAspectRatio() const; - bool isAspectRatioLocked() const; + bool changeInputModeToAnnotationsWarningDialog(); - void setAspectRatioLocked(const bool locked); + bool isWindowAspectRatioLocked() const; float getAspectRatio() const; - void setAspectRatio(const float aspectRatio); + bool hasValidOpenGL(); + bool isOpenGLContextSharingValid() const; + protected: void closeEvent(QCloseEvent* event); void keyPressEvent(QKeyEvent* event); @@ -223,16 +229,20 @@ namespace caret { void processProjectFoci(); void processSplitBorderFiles(); - void processTabAspectRatioLockedToggled(bool checked); - void processWindowAspectRatioLockedToggled(bool checked); - void processLockAllTabsAspectRatioTriggered(); - void processUnlockAllTabsAspectRatioTriggered(); + void processWindowMenuLockWindowAspectRatioTriggered(bool checked); + void processWindowMenuLockAllTabAspectRatioTriggered(bool checked); + void processToolBarLockWindowAndAllTabAspectTriggered(bool checked); + void processToolBarLockWindowAndAllTabAspectMenu(const QPoint& pos); + void processEditMenuItemTriggered(QAction* action); void processEditMenuAboutToShow(); + void aspectRatioDialogUpdateForTab(const double aspectRatio); + void aspectRatioDialogUpdateForWindow(const double aspectRatio); + private: - // Contains status of components such as enter/exit full screen + /** Contains status of components such as enter/exit full screen */ struct WindowComponentStatus { bool isFeaturesToolBoxDisplayed; bool isOverlayToolBoxDisplayed; @@ -247,6 +257,11 @@ namespace caret { CREATE_DEFAULT_TABS_NO }; + enum class AspectRatioMode { + TAB, + WINDOW + }; + BrainBrowserWindow(const int browserWindowIndex, BrowserTabContent* browserTabContent, const CreateDefaultTabsMode createDefaultTabsMode, @@ -295,8 +310,23 @@ namespace caret { bool isMacOptionKeyDown() const; + void showDataFileReadWarningsDialog(); + + void lockWindowAspectRatio(const bool checked); + void lockAllTabAspectRatios(const bool checked); + void updateActionsForLockingAspectRatios(); + + float getAspectRatioFromDialog(const AspectRatioMode aspectRatioMode, + const QString& title, + const float aspectRatio, + QWidget* parent) const; + + void saveBrowserWindowContentForScene(); + /** Index of this window */ - int32_t m_browserWindowIndex; + const int32_t m_browserWindowIndex; + + BrowserWindowContent* m_browserWindowContent = NULL; BrainOpenGLWidget* m_openGLWidget; @@ -340,7 +370,6 @@ namespace caret { QAction* m_viewFullScreenAction; QAction* m_viewTileTabsAction; - bool m_viewTileTabsSelected; QMenu* m_tileTabsMenu; QAction* m_createAndEditTileTabsAction; @@ -376,10 +405,10 @@ namespace caret { QAction* m_overlayToolBoxAction; - QAction* m_lockAllTabsAspectRatioAction; - QAction* m_unlockAllTabsAspectRatioAction; - QAction* m_tabAspectRatioLockedAction; - QAction* m_windowAspectRatioLockedAction; + QAction* m_windowMenuLockWindowAspectRatioAction; + QAction* m_windowMenuLockAllTabAspectRatioAction; + QAction* m_toolBarLockWindowAndAllTabAspectRatioAction; + QToolButton* m_toolBarLockWindowAndAllTabAspectRatioButton; QAction* m_featuresToolBoxAction; @@ -405,8 +434,6 @@ namespace caret { AString m_selectedTileTabsConfigurationUniqueIdentifier; TileTabsConfiguration* m_defaultTileTabsConfiguration; - TileTabsConfiguration* m_sceneTileTabsConfiguration; - AString m_sceneTileTabsConfigurationText; static AString s_previousOpenFileNameFilter; static AString s_previousOpenFileDirectory; @@ -415,8 +442,6 @@ namespace caret { WindowComponentStatus m_defaultWindowComponentStatus; WindowComponentStatus m_normalWindowComponentStatus; - float m_aspectRatio; - static bool s_firstWindowFlag; friend class BrainBrowserWindowToolBar; @@ -430,8 +455,10 @@ namespace caret { /** Y position from scene file for first window */ static int32_t s_sceneFileFirstWindowY; + static std::set s_brainBrowserWindows; }; #ifdef __BRAIN_BROWSER_WINDOW_DECLARE__ + std::set BrainBrowserWindow::s_brainBrowserWindows; AString BrainBrowserWindow::s_previousOpenFileNameFilter; AString BrainBrowserWindow::s_previousOpenFileDirectory; QByteArray BrainBrowserWindow::s_previousOpenFileGeometry; diff --git a/src/GuiQt/BrainBrowserWindowOrientedToolBox.cxx b/src/GuiQt/BrainBrowserWindowOrientedToolBox.cxx index 95e71b4916fab205cf7e075538c89f5c6c7333f6..3a2cbbc0611d48840d8af47d8508ad889bf5ab57 100644 --- a/src/GuiQt/BrainBrowserWindowOrientedToolBox.cxx +++ b/src/GuiQt/BrainBrowserWindowOrientedToolBox.cxx @@ -38,10 +38,12 @@ #include "CaretDataFile.h" #include "CaretPreferences.h" #include "ChartableLineSeriesBrainordinateInterface.h" +#include "ChartTwoOverlaySetViewController.h" #include "ChartableMatrixInterface.h" #include "ChartToolBoxViewController.h" #include "CiftiConnectivityMatrixViewController.h" -#include "EventBrowserWindowContentGet.h" +#include "DeveloperFlagsEnum.h" +#include "EventBrowserWindowDrawingContent.h" #include "EventManager.h" #include "EventUserInterfaceUpdate.h" #include "FiberOrientationSelectionViewController.h" @@ -116,6 +118,7 @@ BrainBrowserWindowOrientedToolBox::BrainBrowserWindowOrientedToolBox(const int32 m_annotationViewController = NULL; m_borderSelectionViewController = NULL; + m_chartOverlaySetViewController = NULL; m_chartToolBoxViewController = NULL; m_connectivityMatrixViewController = NULL; m_fiberOrientationViewController = NULL; @@ -129,6 +132,7 @@ BrainBrowserWindowOrientedToolBox::BrainBrowserWindowOrientedToolBox(const int32 m_annotationTabIndex = -1; m_borderTabIndex = -1; + m_chartOverlayTabIndex = -1; m_chartTabIndex = -1; m_connectivityTabIndex = -1; m_fiberOrientationTabIndex = -1; @@ -145,6 +149,13 @@ BrainBrowserWindowOrientedToolBox::BrainBrowserWindowOrientedToolBox(const int32 m_overlayTabIndex = addToTabWidget(m_overlaySetViewController, "Layers"); } + if (isOverlayToolBox) { + m_chartOverlaySetViewController = new ChartTwoOverlaySetViewController(orientation, + browserWindowIndex, + this); + m_chartOverlayTabIndex = addToTabWidget(m_chartOverlaySetViewController, + "Chart Layers"); + } if (isOverlayToolBox) { m_chartToolBoxViewController = new ChartToolBoxViewController(orientation, browserWindowIndex, @@ -462,7 +473,15 @@ BrainBrowserWindowOrientedToolBox::restoreFromScene(const SceneAttributes* scene sceneClass->getClass("geometry")); } else { + const SceneClass* geometryClass = sceneClass->getClass("geometry"); + if (geometryClass != NULL) { + /* is restored only when floating and visible. */ + geometryClass->setDescendantsRestored(true); + } +#if QT_VERSION < 0x050000 /* + * Do this for Qt4 only. Qt5 restores size in BrainBrowserWindow. + * * From http://stackoverflow.com/questions/2722939/c-resize-a-docked-qt-qdockwidget-programmatically * * Set the minimum and maximum sizes and restore them later. @@ -487,6 +506,7 @@ BrainBrowserWindowOrientedToolBox::restoreFromScene(const SceneAttributes* scene this, SLOT(restoreMinimumAndMaximumSizesAfterSceneRestored())); } +#endif } } @@ -630,8 +650,9 @@ BrainBrowserWindowOrientedToolBox::receiveEvent(Event* event) int defaultTabIndex = -1; bool enableLayers = true; bool enableVolumeSurfaceOutline = false; - bool enableCharts = false; - EventBrowserWindowContentGet browserContentEvent(m_browserWindowIndex); + bool enableChartOne = false; + bool enableChartTwo = false; + EventBrowserWindowDrawingContent browserContentEvent(m_browserWindowIndex); EventManager::get()->sendEvent(browserContentEvent.getPointer()); BrowserTabContent* windowContent = browserContentEvent.getSelectedBrowserTabContent(); if (windowContent != NULL) { @@ -656,13 +677,23 @@ BrainBrowserWindowOrientedToolBox::receiveEvent(Event* event) break; case ModelTypeEnum::MODEL_TYPE_CHART: defaultTabIndex = m_chartTabIndex; + enableChartOne = true; + enableLayers = false; + enableVolumeSurfaceOutline = false; + haveBorders = false; + haveFibers = false; + haveFoci = false; + haveLabels = false; + break; + case ModelTypeEnum::MODEL_TYPE_CHART_TWO: + defaultTabIndex = m_chartOverlayTabIndex; + enableChartTwo = true; enableLayers = false; enableVolumeSurfaceOutline = false; haveBorders = false; haveFibers = false; haveFoci = false; haveLabels = false; - enableCharts = true; break; } } @@ -680,7 +711,8 @@ BrainBrowserWindowOrientedToolBox::receiveEvent(Event* event) * NOTE: Order is important so that overlay tab is * automatically selected. */ - if (m_chartTabIndex >= 0) m_tabWidget->setTabEnabled(m_chartTabIndex, enableCharts); + if (m_chartTabIndex >= 0) m_tabWidget->setTabEnabled(m_chartTabIndex, enableChartOne); + if (m_chartOverlayTabIndex >= 0) m_tabWidget->setTabEnabled(m_chartOverlayTabIndex, enableChartTwo); if (m_connectivityTabIndex >= 0) m_tabWidget->setTabEnabled(m_connectivityTabIndex, haveConnFiles); if (m_volumeSurfaceOutlineTabIndex >= 0) m_tabWidget->setTabEnabled(m_volumeSurfaceOutlineTabIndex, enableVolumeSurfaceOutline); diff --git a/src/GuiQt/BrainBrowserWindowOrientedToolBox.h b/src/GuiQt/BrainBrowserWindowOrientedToolBox.h index fc443448dc13163ac700b760607659d7514392d7..727f5b9e48d97dd57840659975f511bf1fac9f3d 100644 --- a/src/GuiQt/BrainBrowserWindowOrientedToolBox.h +++ b/src/GuiQt/BrainBrowserWindowOrientedToolBox.h @@ -33,6 +33,7 @@ class QTabWidget; namespace caret { class AnnotationSelectionViewController; class BorderSelectionViewController; + class ChartTwoOverlaySetViewController; class ChartToolBoxViewController; class CiftiConnectivityMatrixViewController; class FiberOrientationSelectionViewController; @@ -85,6 +86,8 @@ namespace caret { BorderSelectionViewController* m_borderSelectionViewController; + ChartTwoOverlaySetViewController* m_chartOverlaySetViewController; + CiftiConnectivityMatrixViewController* m_connectivityMatrixViewController; ChartToolBoxViewController* m_chartToolBoxViewController; @@ -104,7 +107,9 @@ namespace caret { QString m_toolBoxTitle; int32_t m_browserWindowIndex; - + + int32_t m_chartOverlayTabIndex; + int32_t m_overlayTabIndex; int32_t m_annotationTabIndex; diff --git a/src/GuiQt/BrainBrowserWindowToolBar.cxx b/src/GuiQt/BrainBrowserWindowToolBar.cxx index c630757c6345ab926eb31105d15e43bdfa8c9392..c2d8f04d7949432d1694ec0ceac564665c38cddc 100644 --- a/src/GuiQt/BrainBrowserWindowToolBar.cxx +++ b/src/GuiQt/BrainBrowserWindowToolBar.cxx @@ -28,7 +28,10 @@ #include #include #include +#if QT_VERSION >= 0x050000 +#else // QT_VERSION #include +#endif // QT_VERSION #include #include #include @@ -40,6 +43,7 @@ #include #include #include +#include #include #include #include @@ -51,12 +55,18 @@ #include "BrainBrowserWindowToolBar.h" #include "BrainBrowserWindowToolBarChartAttributes.h" #include "BrainBrowserWindowToolBarChartAxes.h" +#include "BrainBrowserWindowToolBarChartTwoAttributes.h" +#include "BrainBrowserWindowToolBarChartTwoAxes.h" +#include "BrainBrowserWindowToolBarChartTwoOrientation.h" +#include "BrainBrowserWindowToolBarChartTwoTitle.h" +#include "BrainBrowserWindowToolBarChartTwoType.h" #include "BrainBrowserWindowToolBarChartType.h" #include "BrainBrowserWindowToolBarClipping.h" #include "BrainBrowserWindowToolBarSlicePlane.h" #include "BrainBrowserWindowToolBarSliceSelection.h" #include "BrainBrowserWindowToolBarSurfaceMontage.h" #include "BrainBrowserWindowToolBarTab.h" +#include "BrainBrowserWindowToolBarTabPopUpMenu.h" #include "BrainBrowserWindowToolBarVolumeMontage.h" #include "BrainStructure.h" #include "BrowserTabContent.h" @@ -65,15 +75,17 @@ #include "CaretLogger.h" #include "CaretPreferences.h" #include "CursorDisplayScoped.h" +#include "DeveloperFlagsEnum.h" #include "DisplayPropertiesBorders.h" #include "EventBrowserTabDelete.h" #include "EventBrowserTabGet.h" #include "EventBrowserTabGetAll.h" #include "EventBrowserTabGetAllViewed.h" #include "EventBrowserTabNew.h" -#include "EventBrowserWindowContentGet.h" +#include "EventBrowserWindowDrawingContent.h" #include "EventBrowserWindowCreateTabs.h" #include "EventBrowserWindowNew.h" +#include "EventBrowserWindowTileTabOperation.h" #include "EventGetOrSetUserInputModeProcessor.h" #include "EventGraphicsUpdateOneWindow.h" #include "EventGraphicsUpdateAllWindows.h" @@ -83,8 +95,10 @@ #include "EventSurfaceColoringInvalidate.h" #include "EventUpdateYokedWindows.h" #include "GuiManager.h" +#include "LockAspectWarningDialog.h" #include "Model.h" #include "ModelChart.h" +#include "ModelChartTwo.h" #include "ModelSurface.h" #include "ModelSurfaceMontage.h" #include "ModelSurfaceSelector.h" @@ -101,6 +115,7 @@ #include "SurfaceSelectionModel.h" #include "SurfaceSelectionViewController.h" #include "StructureSurfaceSelectionControl.h" +#include "TileTabsConfiguration.h" #include "UserInputModeAbstract.h" #include "VolumeFile.h" #include "VolumeSliceViewPlaneEnum.h" @@ -126,10 +141,8 @@ using namespace caret; * Action to show overlay tool box. * @param layersToolBoxAction * Action to show layers tool box. - * @param windowAspectRatioLockedAction - * Action to lock window's aspect ratio. - * @param tabAspectRatioLockedAction - * Action to lock tab's aspect ratio. + * @param toolBarLockWindowAndAllTabAspectRatioButton + * Button to lock window's aspect ratio and aspect ratio of all tabs. * @param parent * Parent for this toolbar. */ @@ -137,13 +150,11 @@ BrainBrowserWindowToolBar::BrainBrowserWindowToolBar(const int32_t browserWindow BrowserTabContent* initialBrowserTabContent, QAction* overlayToolBoxAction, QAction* layersToolBoxAction, - QAction* windowAspectRatioLockedAction, - QAction* tabAspectRatioLockedAction, + QToolButton* toolBarLockWindowAndAllTabAspectRatioButton, BrainBrowserWindow* parentBrainBrowserWindow) : QToolBar(parentBrainBrowserWindow) { this->browserWindowIndex = browserWindowIndex; - this->updateCounter = 0; m_tabIndexForTileTabsHighlighting = -1; this->isContructorFinished = false; @@ -196,9 +207,23 @@ BrainBrowserWindowToolBar::BrainBrowserWindowToolBar(const int32_t browserWindow * The style must remain valid until the destruction * of this instance. It cannot be declared statically. */ +#if QT_VERSION >= 0x050000 + /* + * "Cleanlooks Style" removed in Qt5. Fusion style + * seems to have same effect on toolbar so that + * tabs do not get shrunk too small and so the + * scroll tabs arrows are available. + */ + QStyle* fusionStyle = QStyleFactory::create("Fusion"); + if (fusionStyle != NULL) { + fusionStyle->setParent(this); + this->tabBar->setStyle(fusionStyle); + } +#else // QT_VERSION QCleanlooksStyle* cleanLooksStyle = new QCleanlooksStyle(); cleanLooksStyle->setParent(this); this->tabBar->setStyle(cleanLooksStyle); +#endif // QT_VERSION #endif // Q_OS_MACX QObject::connect(this->tabBar, SIGNAL(currentChanged(int)), this, SLOT(selectedTabChanged(int))); @@ -207,6 +232,17 @@ BrainBrowserWindowToolBar::BrainBrowserWindowToolBar(const int32_t browserWindow QObject::connect(this->tabBar, SIGNAL(tabMoved(int,int)), this, SLOT(tabMoved(int,int))); + + /* + * Add context menu + * From the position, use QTabBar::tabAt(const QPoint& position) to get the + * tab that the user has clicked and if over a tab, pop-up a menu + * for the tab with Create New Tab and Place Right, Duplicate and Place on Right + */ + QObject::connect(this->tabBar, &QTabBar::customContextMenuRequested, + this, &BrainBrowserWindowToolBar::showTabMenu); + this->tabBar->setContextMenuPolicy(Qt::CustomContextMenu); + /* * Custom view action */ @@ -281,6 +317,14 @@ BrainBrowserWindowToolBar::BrainBrowserWindowToolBar(const int32_t browserWindow overlayToolBoxToolButton, layersToolBoxToolButton); + WuQtUtilities::setToolButtonStyleForQt5Mac(helpDialogToolButton); + WuQtUtilities::setToolButtonStyleForQt5Mac(informationDialogToolButton); + WuQtUtilities::setToolButtonStyleForQt5Mac(identifyDialogToolButton); + WuQtUtilities::setToolButtonStyleForQt5Mac(sceneDialogToolButton); + WuQtUtilities::setToolButtonStyleForQt5Mac(toolBarToolButton); + WuQtUtilities::setToolButtonStyleForQt5Mac(overlayToolBoxToolButton); + WuQtUtilities::setToolButtonStyleForQt5Mac(layersToolBoxToolButton); + /* * Tab bar and controls at far right side of toolbar */ @@ -303,12 +347,16 @@ BrainBrowserWindowToolBar::BrainBrowserWindowToolBar(const int32_t browserWindow this->orientationWidget = this->createOrientationWidget(); this->chartAxesWidget = createChartAxesWidget(); this->chartAttributesWidget = createChartAttributesWidget(); + this->chartTwoOrientationWidget = createChartTwoOrientationWidget(); + this->chartTwoAttributesWidget = createChartTwoAttributesWidget(); + this->chartTwoAxesWidget = createChartTwoAxesWidget(); + this->chartTwoTitleWidget = createChartTwoTitleWidget(); this->chartTypeWidget = createChartTypeWidget(); + this->chartTypeTwoWidget = createChartTypeTwoWidget(); this->wholeBrainSurfaceOptionsWidget = this->createWholeBrainSurfaceOptionsWidget(); this->volumeIndicesWidget = this->createVolumeIndicesWidget(); this->modeWidget = this->createModeWidget(); - this->windowWidget = this->createTabOptionsWidget(windowAspectRatioLockedAction, - tabAspectRatioLockedAction); + this->windowWidget = this->createTabOptionsWidget(toolBarLockWindowAndAllTabAspectRatioButton); this->singleSurfaceSelectionWidget = this->createSingleSurfaceOptionsWidget(); this->surfaceMontageSelectionWidget = this->createSurfaceMontageOptionsWidget(); m_clippingOptionsWidget = createClippingOptionsWidget(); @@ -340,8 +388,18 @@ BrainBrowserWindowToolBar::BrainBrowserWindowToolBar(const int32_t browserWindow this->toolbarWidgetLayout->addWidget(this->chartTypeWidget, 0, Qt::AlignLeft); + this->toolbarWidgetLayout->addWidget(this->chartTypeTwoWidget, 0, Qt::AlignLeft); + this->toolbarWidgetLayout->addWidget(this->chartAxesWidget, 0, Qt::AlignLeft); + this->toolbarWidgetLayout->addWidget(this->chartTwoOrientationWidget, 0, Qt::AlignLeft); + + this->toolbarWidgetLayout->addWidget(this->chartTwoAttributesWidget, 0, Qt::AlignLeft); + + this->toolbarWidgetLayout->addWidget(this->chartTwoAxesWidget, 0, Qt::AlignLeft); + + this->toolbarWidgetLayout->addWidget(this->chartTwoTitleWidget, 0, Qt::AlignLeft); + this->toolbarWidgetLayout->addWidget(this->chartAttributesWidget, 0, Qt::AlignLeft); this->toolbarWidgetLayout->addWidget(this->modeWidget, 0, Qt::AlignLeft); @@ -378,21 +436,29 @@ BrainBrowserWindowToolBar::BrainBrowserWindowToolBar(const int32_t browserWindow this->addWidget(w); if (initialBrowserTabContent != NULL) { - this->addNewTabWithContent(initialBrowserTabContent); + insertTabContentPrivate(InsertTabMode::APPEND, + initialBrowserTabContent, + -1); } else { AString errorMessage; - this->createNewTab(errorMessage); + BrowserTabContent* tabContent = this->createNewTab(errorMessage); + if (tabContent != NULL) { + insertTabContentPrivate(InsertTabMode::APPEND, + tabContent, + -1); + } } this->updateToolBar(); this->isContructorFinished = true; + m_tileTabsHighlightingTimerEnabledFlag = true; EventManager::get()->addEventListener(this, EventTypeEnum::EVENT_BROWSER_TAB_GET_ALL_VIEWED); - EventManager::get()->addEventListener(this, EventTypeEnum::EVENT_BROWSER_WINDOW_CONTENT_GET); + EventManager::get()->addEventListener(this, EventTypeEnum::EVENT_BROWSER_WINDOW_DRAWING_CONTENT_GET); EventManager::get()->addEventListener(this, EventTypeEnum::EVENT_BROWSER_WINDOW_CREATE_TABS); - EventManager::get()->addEventListener(this, EventTypeEnum::EVENT_UPDATE_YOKED_WINDOWS); + EventManager::get()->addEventListener(this, EventTypeEnum::EVENT_BROWSER_WINDOW_TILE_TAB_OPERATION); EventManager::get()->addEventListener(this, EventTypeEnum::EVENT_USER_INTERFACE_UPDATE); } @@ -468,7 +534,8 @@ BrainBrowserWindowToolBar::~BrainBrowserWindowToolBar() } /** - * Create a new tab. + * Create a new tab. NOTE: This method only creates + * a new tab, it DOES NOT add the tab to the toolbar. * @param errorMessage * If fails to create new tab, it will contain a message * describing the error. @@ -493,7 +560,6 @@ BrainBrowserWindowToolBar::createNewTab(AString& errorMessage) Brain* brain = GuiManager::get()->getBrain(); tabContent->getVolumeSurfaceOutlineSet()->selectSurfacesAfterSpecFileLoaded(brain, false); - this->addNewTabWithContent(tabContent); return tabContent; } @@ -505,8 +571,28 @@ BrainBrowserWindowToolBar::createNewTab(AString& errorMessage) * Tab Content that is to be cloned into the new tab. */ void -BrainBrowserWindowToolBar::addNewTabCloneContent(BrowserTabContent* browserTabContentToBeCloned) +BrainBrowserWindowToolBar::addNewDuplicatedTab(BrowserTabContent* browserTabContentToBeCloned) { + insertAndCloneTabContentAtTabBarIndex(browserTabContentToBeCloned, + -1); +} + +/** + * Clone and insert the cloned tab at the given index in the tab bar. + * + * @param browserTabContentToBeCloned + * Tab Content that is to be cloned into the new tab. + * @param tabBarIndex + * Index in the tab bar. If invalid, tab is appended to the end of the toolbar. + */ +void +BrainBrowserWindowToolBar::insertAndCloneTabContentAtTabBarIndex(const BrowserTabContent* browserTabContentToBeCloned, + const int32_t tabBarIndex) +{ + if ( ! allowAddingNewTab()) { + return; + } + /* * Wait cursor */ @@ -523,11 +609,22 @@ BrainBrowserWindowToolBar::addNewTabCloneContent(BrowserTabContent* browserTabCo return; } + if (tabBarIndex >= 0){ + insertTabContentPrivate(InsertTabMode::AT_TAB_BAR_INDEX, + tabContent, + tabBarIndex); + } + else { + insertTabContentPrivate(InsertTabMode::APPEND, + tabContent, + -1); + } + if (browserTabContentToBeCloned != NULL) { /* * New tab is clone of tab that was displayed when the new tab was created. */ - tabContent->cloneBrowserTabContent(browserTabContentToBeCloned); + tabContent->cloneBrowserTabContent(const_cast(browserTabContentToBeCloned)); } this->updateToolBar(); @@ -545,8 +642,9 @@ BrainBrowserWindowToolBar::addNewTabCloneContent(BrowserTabContent* browserTabCo void BrainBrowserWindowToolBar::addNewTabWithContent(BrowserTabContent* tabContent) { - addOrInsertNewTab(tabContent, - -1); + insertTabContentPrivate(InsertTabMode::APPEND, + tabContent, + -1); } /** @@ -555,6 +653,22 @@ BrainBrowserWindowToolBar::addNewTabWithContent(BrowserTabContent* tabContent) void BrainBrowserWindowToolBar::addNewTab() { + insertNewTabAtTabBarIndex(-1); +} + +/** + * Insert a new tab at the given index in the tab bar. + * + * @param tabBarIndex + * Index in the tab bar. If invalid, tab is appended to the end of the toolbar. + */ +void +BrainBrowserWindowToolBar::insertNewTabAtTabBarIndex(const int32_t tabBarIndex) +{ + if ( ! allowAddingNewTab()) { + return; + } + /* * Wait cursor */ @@ -570,7 +684,18 @@ BrainBrowserWindowToolBar::addNewTab() errorMessage); return; } - + + if (tabBarIndex >= 0){ + insertTabContentPrivate(InsertTabMode::AT_TAB_BAR_INDEX, + tabContent, + tabBarIndex); + } + else { + insertTabContentPrivate(InsertTabMode::APPEND, + tabContent, + -1); + } + this->updateToolBar(); EventManager::get()->sendEvent(EventSurfaceColoringInvalidate().getPointer()); @@ -580,76 +705,69 @@ BrainBrowserWindowToolBar::addNewTab() /** - * Insert the tab content at the given index. - * - * @param browserTabContent - * Content of the tab. - * @param insertAtIndex - * Insert the tab at the given index in the tab bar. Must be greater than or - * equal to zero. - */ -void -BrainBrowserWindowToolBar::insertTabAtIndex(BrowserTabContent* browserTabContent, - const int32_t insertAtIndex) -{ - CaretAssert(insertAtIndex >= 0); - - addOrInsertNewTab(browserTabContent, - insertAtIndex); -} - -/** - * Add or Insert the tab content at the given index. + * Insert tab content into a tab in the toolbar. * + * @param insertTabMode + * Mode for inserting the tab. * @param browserTabContent - * Content of the tab. - * @param insertAtIndex - * If greater than or equal to zero, insert the tab at this index. - * Otherwise, use the index from the tab content or just append. + * The tab content. + * @param tabBarIndex + * Index of tab bar of where tab is inserted for mode AT_TAB_BAR_INDEX */ void -BrainBrowserWindowToolBar::addOrInsertNewTab(BrowserTabContent* browserTabContent, - const int32_t insertAtIndex) +BrainBrowserWindowToolBar::insertTabContentPrivate(const InsertTabMode insertTabMode, + BrowserTabContent* browserTabContent, + const int32_t tabBarIndex) { CaretAssert(browserTabContent); - + this->tabBar->blockSignals(true); - + int32_t newTabIndex = -1; - if (insertAtIndex >= 0) { - newTabIndex = this->tabBar->insertTab(insertAtIndex, - "NewTab"); - } - else { - const int32_t tabContentIndex = browserTabContent->getTabNumber(); - - const int32_t numTabs = this->tabBar->count(); - if (numTabs <= 0) { + + switch (insertTabMode) { + case InsertTabMode::APPEND: newTabIndex = this->tabBar->addTab("NewTab"); - } - else { - int insertIndex = 0; - for (int32_t i = 0; i < numTabs; i++) { - if (tabContentIndex > this->getTabContentFromTab(i)->getTabNumber()) { - insertIndex = i + 1; - } + break; + case InsertTabMode::AT_TAB_BAR_INDEX: + if ((tabBarIndex >= 0) && (tabBarIndex <= this->tabBar->count())) { + newTabIndex = this->tabBar->insertTab(tabBarIndex, + "NewTab"); } - if (insertIndex >= numTabs) { + break; + case InsertTabMode::AT_TAB_CONTENTS_INDEX: + { + const int32_t tabContentIndex = browserTabContent->getTabNumber(); + + const int32_t numTabs = this->tabBar->count(); + if (numTabs <= 0) { newTabIndex = this->tabBar->addTab("NewTab"); } else { - this->tabBar->insertTab(insertIndex, "NewTab"); - newTabIndex = insertIndex; + int insertIndex = 0; + for (int32_t i = 0; i < numTabs; i++) { + if (tabContentIndex > this->getTabContentFromTab(i)->getTabNumber()) { + insertIndex = i + 1; + } + } + if (insertIndex >= numTabs) { + newTabIndex = this->tabBar->addTab("NewTab"); + } + else { + this->tabBar->insertTab(insertIndex, "NewTab"); + newTabIndex = insertIndex; + } } } + break; } - + this->tabBar->setTabData(newTabIndex, qVariantFromValue((void*)browserTabContent)); - + const int32_t numOpenTabs = this->tabBar->count(); this->tabBar->setTabsClosable(numOpenTabs > 1); - + this->updateTabName(newTabIndex); this->tabBar->setCurrentIndex(newTabIndex); @@ -657,6 +775,78 @@ BrainBrowserWindowToolBar::addOrInsertNewTab(BrowserTabContent* browserTabConten this->tabBar->blockSignals(false); } +/** + * When adding a new tab or duplicating a tab, the tab + * may not be visible when Tile Tabs is enabled and there + * is no available space in the tile tabs configuration. + * + * @return + * True if new tab should be created, else false. + */ +bool +BrainBrowserWindowToolBar::allowAddingNewTab() +{ + static bool m_doNotShowDialogAgainFlag = false; + + if (m_doNotShowDialogAgainFlag) { + return true; + } + + BrainBrowserWindow* browserWindow = GuiManager::get()->getBrowserWindowByWindowIndex(this->browserWindowIndex); + CaretAssert(browserWindow); + + /* + * Is tile tabs off? + */ + if ( ! browserWindow->isTileTabsSelected()) { + return true; + } + + const TileTabsConfiguration* tileTabs = browserWindow->getSelectedTileTabsConfiguration(); + if (tileTabs == NULL) { + return true; + } + + /* + * Default configuration always shows all tabs + */ + if (tileTabs->isDefaultConfiguration()) { + return true; + } + + /* + * Is there space in the current configuration? + */ + const int32_t tileTabsCount = tileTabs->getNumberOfColumns() * tileTabs->getNumberOfRows(); + if (getNumberOfTabs() < tileTabsCount) { + return true; + } + + /* + * Selected configuration is full, all user to continue/cancel + */ + const AString doNotShowAgainMsg("Remember my choice and do not show this dialog again"); + + const AString msg("" + "Tile tabs is enabled and there is no space in the selected tile " + "tabs configuration for the new tab. If you choose to continue, " + "either the new tab will not be visible or an existing tab will " + "become invisible until the tile tabs configuration is changed." + "

    " + "Do you want to create the new tab?" + "
    " + ""); + + + if (WuQMessageBox::warningYesNoWithDoNotShowAgain(this, + "NewTabNotDisplayedDueToTileTabs", + msg)) { + return true; + } + + return false; +} + /** * Shows/hides the toolbar. @@ -709,60 +899,66 @@ BrainBrowserWindowToolBar::addDefaultTabsAfterLoadingSpecFile() int32_t cerebellumSurfaceTypeCode = 1000000; ModelChart* chartModel = NULL; + ModelChartTwo* chartTwoModel = NULL; ModelSurfaceMontage* surfaceMontageModel = NULL; ModelVolume* volumeModel = NULL; ModelWholeBrain* wholeBrainModel = NULL; + const bool includeSingleSurfacesFlag = false; + const bool includeOldChartFlag = false; + for (std::vector::const_iterator iter = allModels.begin(); iter != allModels.end(); iter++) { ModelSurface* surfaceModel = dynamic_cast(*iter); if (surfaceModel != NULL) { - Surface* surface = surfaceModel->getSurface(); - StructureEnum::Enum structure = surface->getStructure(); - SurfaceTypeEnum::Enum surfaceType = surface->getSurfaceType(); - const int32_t surfaceTypeCode = SurfaceTypeEnum::toIntegerCode(surfaceType); - - switch (structure) { - case StructureEnum::CEREBELLUM: - if (surfaceTypeCode < cerebellumSurfaceTypeCode) { - cerebellumSurfaceModel = surfaceModel; - cerebellumSurfaceTypeCode = surfaceTypeCode; - } - if (surfaceType == SurfaceTypeEnum::INFLATED) { - cerebellumSurfaceInflated = surfaceModel; - } - else if (surfaceType == SurfaceTypeEnum::VERY_INFLATED) { - cerebellumSurfaceVeryInflated = surfaceModel; - } - break; - case StructureEnum::CORTEX_LEFT: - if (surfaceTypeCode < leftSurfaceTypeCode) { - leftSurfaceModel = surfaceModel; - leftSurfaceTypeCode = surfaceTypeCode; - } - if (surfaceType == SurfaceTypeEnum::INFLATED) { - leftSurfaceInflated = surfaceModel; - } - else if (surfaceType == SurfaceTypeEnum::VERY_INFLATED) { - leftSurfaceVeryInflated = surfaceModel; - } - break; - case StructureEnum::CORTEX_RIGHT: - if (surfaceTypeCode < rightSurfaceTypeCode) { - rightSurfaceModel = surfaceModel; - rightSurfaceTypeCode = surfaceTypeCode; - } - if (surfaceType == SurfaceTypeEnum::INFLATED) { - rightSurfaceInflated = surfaceModel; - } - else if (surfaceType == SurfaceTypeEnum::VERY_INFLATED) { - rightSurfaceVeryInflated = surfaceModel; - } - break; - default: - break; + if (includeSingleSurfacesFlag) { + Surface* surface = surfaceModel->getSurface(); + StructureEnum::Enum structure = surface->getStructure(); + SurfaceTypeEnum::Enum surfaceType = surface->getSurfaceType(); + const int32_t surfaceTypeCode = SurfaceTypeEnum::toIntegerCode(surfaceType); + + switch (structure) { + case StructureEnum::CEREBELLUM: + if (surfaceTypeCode < cerebellumSurfaceTypeCode) { + cerebellumSurfaceModel = surfaceModel; + cerebellumSurfaceTypeCode = surfaceTypeCode; + } + if (surfaceType == SurfaceTypeEnum::INFLATED) { + cerebellumSurfaceInflated = surfaceModel; + } + else if (surfaceType == SurfaceTypeEnum::VERY_INFLATED) { + cerebellumSurfaceVeryInflated = surfaceModel; + } + break; + case StructureEnum::CORTEX_LEFT: + if (surfaceTypeCode < leftSurfaceTypeCode) { + leftSurfaceModel = surfaceModel; + leftSurfaceTypeCode = surfaceTypeCode; + } + if (surfaceType == SurfaceTypeEnum::INFLATED) { + leftSurfaceInflated = surfaceModel; + } + else if (surfaceType == SurfaceTypeEnum::VERY_INFLATED) { + leftSurfaceVeryInflated = surfaceModel; + } + break; + case StructureEnum::CORTEX_RIGHT: + if (surfaceTypeCode < rightSurfaceTypeCode) { + rightSurfaceModel = surfaceModel; + rightSurfaceTypeCode = surfaceTypeCode; + } + if (surfaceType == SurfaceTypeEnum::INFLATED) { + rightSurfaceInflated = surfaceModel; + } + else if (surfaceType == SurfaceTypeEnum::VERY_INFLATED) { + rightSurfaceVeryInflated = surfaceModel; + } + break; + default: + break; + } } } else if (dynamic_cast(*iter) != NULL) { @@ -775,7 +971,12 @@ BrainBrowserWindowToolBar::addDefaultTabsAfterLoadingSpecFile() wholeBrainModel = dynamic_cast(*iter); } else if (dynamic_cast(*iter)) { - chartModel = dynamic_cast(*iter); + if (includeOldChartFlag) { + chartModel = dynamic_cast(*iter); + } + } + else if (dynamic_cast(*iter)) { + chartTwoModel = dynamic_cast(*iter); } else { CaretAssertMessage(0, AString("Unknow controller type: ") + (*iter)->getNameForGUI(true)); @@ -814,6 +1015,9 @@ BrainBrowserWindowToolBar::addDefaultTabsAfterLoadingSpecFile() if (chartModel != NULL) { numberOfTabsNeeded++; } + if (chartTwoModel != NULL) { + numberOfTabsNeeded++; + } if (leftSurfaceModel != NULL) { numberOfTabsNeeded++; } @@ -827,7 +1031,12 @@ BrainBrowserWindowToolBar::addDefaultTabsAfterLoadingSpecFile() const int32_t numberOfTabsToAdd = numberOfTabsNeeded - this->tabBar->count(); for (int32_t i = 0; i < numberOfTabsToAdd; i++) { AString errorMessage; - this->createNewTab(errorMessage); + BrowserTabContent* tabContent = this->createNewTab(errorMessage); + if (tabContent != NULL) { + insertTabContentPrivate(InsertTabMode::APPEND, + tabContent, + -1); + } } int32_t tabIndex = 0; @@ -837,6 +1046,8 @@ BrainBrowserWindowToolBar::addDefaultTabsAfterLoadingSpecFile() volumeModel); tabIndex = loadIntoTab(tabIndex, wholeBrainModel); + tabIndex = loadIntoTab(tabIndex, + chartTwoModel); tabIndex = loadIntoTab(tabIndex, chartModel); tabIndex = loadIntoTab(tabIndex, @@ -894,6 +1105,8 @@ BrainBrowserWindowToolBar::addDefaultTabsAfterLoadingSpecFile() break; case ModelTypeEnum::MODEL_TYPE_CHART: break; + case ModelTypeEnum::MODEL_TYPE_CHART_TWO: + break; } } } @@ -942,9 +1155,13 @@ BrainBrowserWindowToolBar::loadIntoTab(const int32_t tabIndexIn, if (controller != NULL) { if (tabIndex >= this->tabBar->count()) { AString errorMessage; - if (this->createNewTab(errorMessage) == NULL) { + BrowserTabContent* tabContent = this->createNewTab(errorMessage); + if (tabContent == NULL) { return -1; } + insertTabContentPrivate(InsertTabMode::APPEND, + tabContent, + -1); tabIndex = this->tabBar->count() - 1; } @@ -1142,6 +1359,17 @@ BrainBrowserWindowToolBar::renameTab() } +/** + * Update the names of all tabs. + */ +void +BrainBrowserWindowToolBar::updateAllTabNames() +{ + for (int32_t i = 0; i < this->tabBar->count(); i++) { + updateTabName(i); + } +} + /** * Update the name of the tab at the given index. The * name is obtained from the tabs browser content. @@ -1173,10 +1401,37 @@ void BrainBrowserWindowToolBar::closeSelectedTab() { tabCloseSelected(this->tabBar->currentIndex()); -// const int tabIndex = this->tabBar->currentIndex(); -// if (this->tabBar->count() > 1) { -// this->tabClosed(tabIndex); -// } +} + +/** + * Called when context menu requested for the tab bar. + * + * @param pos + * Postion of mouse in parent widget. + */ +void +BrainBrowserWindowToolBar::showTabMenu(const QPoint& pos) +{ + const int tabIndexUnderMouseIndex = this->tabBar->tabAt(pos); + + if (tabIndexUnderMouseIndex >= 0) { + BrainBrowserWindowToolBarTabPopUpMenu menu(this, + tabIndexUnderMouseIndex); + menu.exec(mapToGlobal(pos)); + } + else { + QMenu menu(this->tabBar); + menu.move(mapToGlobal(pos)); + QAction* addAction = menu.addAction("Add New Tab"); + + const QAction* selectedAction = menu.exec(); + if (selectedAction == addAction) { + addNewTab(); + } + else if (selectedAction != NULL) { + CaretAssertMessage(0, "Has a new action been added to the menu"); + } + } } /** @@ -1197,18 +1452,33 @@ BrainBrowserWindowToolBar::selectedTabChanged(int indx) if (browserWindow->isTileTabsSelected()) { const BrowserTabContent* btc = getTabContentFromSelectedTab(); if (btc != NULL) { - m_tabIndexForTileTabsHighlighting = btc->getTabNumber(); - - /* - * Short timer that will reset the tab highlighting - */ - const int timeInMilliseconds = 750; - QTimer::singleShot(timeInMilliseconds, - this, SLOT(resetTabIndexForTileTabsHighlighting())); + if (m_tileTabsHighlightingTimerEnabledFlag) { + m_tabIndexForTileTabsHighlighting = btc->getTabNumber(); + + /* + * Short timer that will reset the tab highlighting + */ + if (m_tileTabsHighlightingTimer == NULL) { + m_tileTabsHighlightingTimer = new QTimer(this); + QObject::connect(m_tileTabsHighlightingTimer, &QTimer::timeout, + this, &BrainBrowserWindowToolBar::resetTabIndexForTileTabsHighlighting); + } + + /* + * Note: There is no need to 'stop' the timer if it is running + * as the start() method will stop and restart the timer + */ + const int timeInMilliseconds = 750; + m_tileTabsHighlightingTimer->setSingleShot(true); + m_tileTabsHighlightingTimer->start(timeInMilliseconds); + } } } } - + + /* + * Graphics must be updated so border is visible around the selected tab + */ this->updateGraphicsWindow(); } @@ -1268,7 +1538,7 @@ BrainBrowserWindowToolBar::tabClosed(int tabIndex) void BrainBrowserWindowToolBar::tabMoved(int /*from*/, int /*to*/) { - this->updateGraphicsWindow(); + /* No need to redraw as the selectedTabChanged() is also called when a tab is moved */ } /** @@ -1284,7 +1554,8 @@ BrainBrowserWindowToolBar::removeTab(int tabIndex) if (p != NULL) { BrowserTabContent* btc = (BrowserTabContent*)p; - EventBrowserTabDelete deleteTabEvent(btc); + EventBrowserTabDelete deleteTabEvent(btc, + btc->getTabNumber()); EventManager::get()->sendEvent(deleteTabEvent.getPointer()); } @@ -1305,6 +1576,18 @@ BrainBrowserWindowToolBar::updateToolBar() if (this->isDestructionInProgress) { return; } + + /* + * If this is true, it indicates that one of the 'update' methods has mistakenly emitted a signal + * or an update. + */ + if (m_performingUpdateFlag) { + const AString msg("During an update of the toolbar, a signal was issued and needs to be blocked.\n" + "Set a break on this line and go through the call stack to find where the\n" + "signal was emitted and block it."); + CaretLogSevere(msg); + } + m_performingUpdateFlag = true; /* * If there are no models, close all but the first tab. @@ -1317,8 +1600,6 @@ BrainBrowserWindowToolBar::updateToolBar() } } - this->incrementUpdateCounter(__CARET_FUNCTION_NAME__); - BrowserTabContent* browserTabContent = this->getTabContentFromSelectedTab(); const ModelTypeEnum::Enum viewModel = this->updateViewWidget(browserTabContent); @@ -1327,14 +1608,19 @@ BrainBrowserWindowToolBar::updateToolBar() bool showWholeBrainSurfaceOptionsWidget = false; bool showSingleSurfaceOptionsWidget = false; bool showSurfaceMontageOptionsWidget = false; - bool showClippingOptionsWidget = true; + bool showClippingOptionsWidget = false; bool showVolumeIndicesWidget = false; bool showVolumePlaneWidget = false; bool showVolumeMontageWidget = false; - bool showChartAxesWidget = false; - bool showChartTypeWidget = false; - bool showChartAttributesWidget = false; + bool showChartOneAxesWidget = false; + bool showChartOneTypeWidget = false; + bool showChartTwoTypeWidget = false; + bool showChartOneAttributesWidget = false; + bool showChartTwoOrientationWidget = false; + bool showChartTwoAttributesWidget = false; + bool showChartTwoAxesWidget = false; + bool showChartTwoTitleWidget = false; bool showModeWidget = true; bool showWindowWidget = true; @@ -1345,50 +1631,78 @@ BrainBrowserWindowToolBar::updateToolBar() case ModelTypeEnum::MODEL_TYPE_SURFACE: showOrientationWidget = true; showSingleSurfaceOptionsWidget = true; + showClippingOptionsWidget = true; break; case ModelTypeEnum::MODEL_TYPE_SURFACE_MONTAGE: showOrientationWidget = true; showSurfaceMontageOptionsWidget = true; + showClippingOptionsWidget = true; break; case ModelTypeEnum::MODEL_TYPE_VOLUME_SLICES: showVolumeIndicesWidget = true; showVolumePlaneWidget = true; showVolumeMontageWidget = true; + showClippingOptionsWidget = true; break; case ModelTypeEnum::MODEL_TYPE_WHOLE_BRAIN: showOrientationWidget = true; showWholeBrainSurfaceOptionsWidget = true; showVolumeIndicesWidget = true; + showClippingOptionsWidget = true; break; case ModelTypeEnum::MODEL_TYPE_CHART: { - showChartTypeWidget = true; - showClippingOptionsWidget = false; - - ModelChart* modelChart = browserTabContent->getDisplayedChartModel(); + ModelChart* modelChart = browserTabContent->getDisplayedChartOneModel(); if (modelChart != NULL) { - switch (modelChart->getSelectedChartDataType(browserTabContent->getTabNumber())) { - case ChartDataTypeEnum::CHART_DATA_TYPE_INVALID: + showChartOneTypeWidget = true; + switch (modelChart->getSelectedChartOneDataType(browserTabContent->getTabNumber())) { + case ChartOneDataTypeEnum::CHART_DATA_TYPE_INVALID: break; - case ChartDataTypeEnum::CHART_DATA_TYPE_MATRIX_LAYER: - showChartAttributesWidget = true; + case ChartOneDataTypeEnum::CHART_DATA_TYPE_MATRIX_LAYER: + showChartOneAttributesWidget = true; break; - case ChartDataTypeEnum::CHART_DATA_TYPE_MATRIX_SERIES: - showChartAttributesWidget = true; + case ChartOneDataTypeEnum::CHART_DATA_TYPE_MATRIX_SERIES: + showChartOneAttributesWidget = true; + break; + case ChartOneDataTypeEnum::CHART_DATA_TYPE_LINE_TIME_SERIES: + showChartOneAxesWidget = true; + showChartOneAttributesWidget = true; + break; + case ChartOneDataTypeEnum::CHART_DATA_TYPE_LINE_FREQUENCY_SERIES: + showChartOneAxesWidget = true; + showChartOneAttributesWidget = true; + break; + case ChartOneDataTypeEnum::CHART_DATA_TYPE_LINE_DATA_SERIES: + showChartOneAxesWidget = true; + showChartOneAttributesWidget = true; + break; + } + } + } + break; + case ModelTypeEnum::MODEL_TYPE_CHART_TWO: + { + ModelChartTwo* modelChartTwo = browserTabContent->getDisplayedChartTwoModel(); + if (modelChartTwo != NULL) { + switch (modelChartTwo->getSelectedChartTwoDataType(browserTabContent->getTabNumber())) { + case ChartTwoDataTypeEnum::CHART_DATA_TYPE_INVALID: break; - case ChartDataTypeEnum::CHART_DATA_TYPE_LINE_TIME_SERIES: - showChartAxesWidget = true; - showChartAttributesWidget = true; + case ChartTwoDataTypeEnum::CHART_DATA_TYPE_HISTOGRAM: + showChartTwoAxesWidget = true; + showChartTwoTitleWidget = true; break; - case ChartDataTypeEnum::CHART_DATA_TYPE_LINE_FREQUENCY_SERIES: - showChartAxesWidget = true; - showChartAttributesWidget = true; + case ChartTwoDataTypeEnum::CHART_DATA_TYPE_LINE_SERIES: + showChartTwoAxesWidget = true; + showChartTwoTitleWidget = true; break; - case ChartDataTypeEnum::CHART_DATA_TYPE_LINE_DATA_SERIES: - showChartAxesWidget = true; - showChartAttributesWidget = true; + case ChartTwoDataTypeEnum::CHART_DATA_TYPE_MATRIX: + showChartTwoOrientationWidget = true; + showChartTwoAttributesWidget = true; + showChartTwoTitleWidget = true; break; } + + showChartTwoTypeWidget = true; } } break; @@ -1405,8 +1719,13 @@ BrainBrowserWindowToolBar::updateToolBar() this->singleSurfaceSelectionWidget->setVisible(false); this->surfaceMontageSelectionWidget->setVisible(false); this->chartTypeWidget->setVisible(false); + this->chartTypeTwoWidget->setVisible(false); this->chartAxesWidget->setVisible(false); this->chartAttributesWidget->setVisible(false); + this->chartTwoOrientationWidget->setVisible(false); + this->chartTwoAttributesWidget->setVisible(false); + this->chartTwoAxesWidget->setVisible(false); + this->chartTwoTitleWidget->setVisible(false); this->volumeIndicesWidget->setVisible(false); this->volumePlaneWidget->setVisible(false); this->volumeMontageWidget->setVisible(false); @@ -1418,9 +1737,14 @@ BrainBrowserWindowToolBar::updateToolBar() this->wholeBrainSurfaceOptionsWidget->setVisible(showWholeBrainSurfaceOptionsWidget); this->singleSurfaceSelectionWidget->setVisible(showSingleSurfaceOptionsWidget); this->surfaceMontageSelectionWidget->setVisible(showSurfaceMontageOptionsWidget); - this->chartTypeWidget->setVisible(showChartTypeWidget); - this->chartAxesWidget->setVisible(showChartAxesWidget); - this->chartAttributesWidget->setVisible(showChartAttributesWidget); + this->chartTypeWidget->setVisible(showChartOneTypeWidget); + this->chartTypeTwoWidget->setVisible(showChartTwoTypeWidget); + this->chartAxesWidget->setVisible(showChartOneAxesWidget); + this->chartAttributesWidget->setVisible(showChartOneAttributesWidget); + this->chartTwoOrientationWidget->setVisible(showChartTwoOrientationWidget); + this->chartTwoAttributesWidget->setVisible(showChartTwoAttributesWidget); + this->chartTwoAxesWidget->setVisible(showChartTwoAxesWidget); + this->chartTwoTitleWidget->setVisible(showChartTwoTitleWidget); this->volumeIndicesWidget->setVisible(showVolumeIndicesWidget); this->volumePlaneWidget->setVisible(showVolumePlaneWidget); this->volumeMontageWidget->setVisible(showVolumeMontageWidget); @@ -1430,13 +1754,7 @@ BrainBrowserWindowToolBar::updateToolBar() updateToolBarComponents(browserTabContent); - this->decrementUpdateCounter(__CARET_FUNCTION_NAME__); - - if (this->updateCounter != 0) { - CaretLogSevere("Update counter is non-zero at end of updateToolBar()"); - } - - this->updateTabName(-1); + this->updateAllTabNames(); BrainBrowserWindow* browserWindow = GuiManager::get()->getBrowserWindowByWindowIndex(this->browserWindowIndex); if (browserWindow != NULL) { @@ -1461,6 +1779,8 @@ BrainBrowserWindowToolBar::updateToolBar() } } } + + m_performingUpdateFlag = false; } /** @@ -1479,8 +1799,13 @@ BrainBrowserWindowToolBar::updateToolBarComponents(BrowserTabContent* browserTab this->updateSingleSurfaceOptionsWidget(browserTabContent); this->updateSurfaceMontageOptionsWidget(browserTabContent); this->updateChartTypeWidget(browserTabContent); + this->updateChartTypeTwoWidget(browserTabContent); this->updateChartAxesWidget(browserTabContent); this->updateChartAttributesWidget(browserTabContent); + this->updateChartTwoOrientationWidget(browserTabContent); + this->updateChartTwoAttributesWidget(browserTabContent); + this->updateChartTwoAxesWidget(browserTabContent); + this->updateChartTwoTitleWidget(browserTabContent); this->updateVolumeMontageWidget(browserTabContent); this->updateVolumePlaneWidget(browserTabContent); this->updateModeWidget(browserTabContent); @@ -1497,26 +1822,30 @@ BrainBrowserWindowToolBar::updateToolBarComponents(BrowserTabContent* browserTab QWidget* BrainBrowserWindowToolBar::createViewWidget() { - this->viewModeChartRadioButton = new QRadioButton("Chart"); + this->viewModeChartOneRadioButton = new QRadioButton("Chart Old"); + this->viewModeChartTwoRadioButton = new QRadioButton("Chart"); this->viewModeSurfaceRadioButton = new QRadioButton("Surface"); this->viewModeSurfaceMontageRadioButton = new QRadioButton("Montage"); this->viewModeVolumeRadioButton = new QRadioButton("Volume"); this->viewModeWholeBrainRadioButton = new QRadioButton("All"); -// this->viewModeChartRadioButton->setVisible(false); - QWidget* widget = new QWidget(); QVBoxLayout* layout = new QVBoxLayout(widget); - WuQtUtilities::setLayoutSpacingAndMargins(layout, 4, 5); - layout->addWidget(this->viewModeChartRadioButton); +#ifdef CARET_OS_MACOSX + WuQtUtilities::setLayoutSpacingAndMargins(layout, 4, 2); +#else + WuQtUtilities::setLayoutSpacingAndMargins(layout, 0, 2); +#endif layout->addWidget(this->viewModeSurfaceMontageRadioButton); layout->addWidget(this->viewModeVolumeRadioButton); layout->addWidget(this->viewModeWholeBrainRadioButton); + layout->addWidget(this->viewModeChartTwoRadioButton); layout->addWidget(this->viewModeSurfaceRadioButton); - layout->addStretch(); + layout->addWidget(this->viewModeChartOneRadioButton); QButtonGroup* viewModeRadioButtonGroup = new QButtonGroup(this); - viewModeRadioButtonGroup->addButton(this->viewModeChartRadioButton); + viewModeRadioButtonGroup->addButton(this->viewModeChartOneRadioButton); + viewModeRadioButtonGroup->addButton(this->viewModeChartTwoRadioButton); viewModeRadioButtonGroup->addButton(this->viewModeSurfaceRadioButton); viewModeRadioButtonGroup->addButton(this->viewModeSurfaceMontageRadioButton); viewModeRadioButtonGroup->addButton(this->viewModeVolumeRadioButton); @@ -1525,7 +1854,8 @@ BrainBrowserWindowToolBar::createViewWidget() this, SLOT(viewModeRadioButtonClicked(QAbstractButton*))); this->viewWidgetGroup = new WuQWidgetObjectGroup(this); - this->viewWidgetGroup->add(this->viewModeChartRadioButton); + this->viewWidgetGroup->add(this->viewModeChartOneRadioButton); + this->viewWidgetGroup->add(this->viewModeChartTwoRadioButton); this->viewWidgetGroup->add(this->viewModeSurfaceRadioButton); this->viewWidgetGroup->add(this->viewModeSurfaceMontageRadioButton); this->viewWidgetGroup->add(this->viewModeVolumeRadioButton); @@ -1555,8 +1885,6 @@ BrainBrowserWindowToolBar::updateViewWidget(BrowserTabContent* browserTabContent modelType = browserTabContent->getSelectedModelType(); } - this->incrementUpdateCounter(__CARET_FUNCTION_NAME__); - this->viewWidgetGroup->blockAllSignals(true); /* @@ -1567,7 +1895,16 @@ BrainBrowserWindowToolBar::updateViewWidget(BrowserTabContent* browserTabContent this->viewModeSurfaceMontageRadioButton->setEnabled(browserTabContent->isSurfaceMontageModelValid()); this->viewModeVolumeRadioButton->setEnabled(browserTabContent->isVolumeSliceModelValid()); this->viewModeWholeBrainRadioButton->setEnabled(browserTabContent->isWholeBrainModelValid()); - this->viewModeChartRadioButton->setEnabled(browserTabContent->isChartModelValid()); + this->viewModeChartOneRadioButton->setEnabled(browserTabContent->isChartOneModelValid()); + this->viewModeChartTwoRadioButton->setEnabled(browserTabContent->isChartTwoModelValid()); + } + else { + this->viewModeSurfaceRadioButton->setEnabled(false); + this->viewModeSurfaceMontageRadioButton->setEnabled(false); + this->viewModeVolumeRadioButton->setEnabled(false); + this->viewModeWholeBrainRadioButton->setEnabled(false); + this->viewModeChartOneRadioButton->setEnabled(false); + this->viewModeChartTwoRadioButton->setEnabled(false); } switch (modelType) { @@ -1586,14 +1923,15 @@ BrainBrowserWindowToolBar::updateViewWidget(BrowserTabContent* browserTabContent this->viewModeWholeBrainRadioButton->setChecked(true); break; case ModelTypeEnum::MODEL_TYPE_CHART: - this->viewModeChartRadioButton->setChecked(true); + this->viewModeChartOneRadioButton->setChecked(true); + break; + case ModelTypeEnum::MODEL_TYPE_CHART_TWO: + this->viewModeChartTwoRadioButton->setChecked(true); break; } this->viewWidgetGroup->blockAllSignals(false); - this->decrementUpdateCounter(__CARET_FUNCTION_NAME__); - return modelType; } @@ -1715,30 +2053,39 @@ BrainBrowserWindowToolBar::createOrientationWidget() this->orientationLeftOrLateralToolButton = new QToolButton(); this->orientationLeftOrLateralToolButton->setDefaultAction(this->orientationLeftOrLateralToolButtonAction); + WuQtUtilities::setToolButtonStyleForQt5Mac(this->orientationLeftOrLateralToolButton); this->orientationRightOrMedialToolButton = new QToolButton(); this->orientationRightOrMedialToolButton->setDefaultAction(this->orientationRightOrMedialToolButtonAction); + WuQtUtilities::setToolButtonStyleForQt5Mac(this->orientationRightOrMedialToolButton); this->orientationAnteriorToolButton = new QToolButton(); this->orientationAnteriorToolButton->setDefaultAction(this->orientationAnteriorToolButtonAction); + WuQtUtilities::setToolButtonStyleForQt5Mac(this->orientationAnteriorToolButton); this->orientationPosteriorToolButton = new QToolButton(); this->orientationPosteriorToolButton->setDefaultAction(this->orientationPosteriorToolButtonAction); + WuQtUtilities::setToolButtonStyleForQt5Mac(this->orientationPosteriorToolButton); this->orientationDorsalToolButton = new QToolButton(); this->orientationDorsalToolButton->setDefaultAction(this->orientationDorsalToolButtonAction); + WuQtUtilities::setToolButtonStyleForQt5Mac(this->orientationDorsalToolButton); this->orientationVentralToolButton = new QToolButton(); this->orientationVentralToolButton->setDefaultAction(this->orientationVentralToolButtonAction); + WuQtUtilities::setToolButtonStyleForQt5Mac(this->orientationVentralToolButton); this->orientationLateralMedialToolButton = new QToolButton(); this->orientationLateralMedialToolButton->setDefaultAction(this->orientationLateralMedialToolButtonAction); + WuQtUtilities::setToolButtonStyleForQt5Mac(this->orientationLateralMedialToolButton); this->orientationDorsalVentralToolButton = new QToolButton(); this->orientationDorsalVentralToolButton->setDefaultAction(this->orientationDorsalVentralToolButtonAction); + WuQtUtilities::setToolButtonStyleForQt5Mac(this->orientationDorsalVentralToolButton); this->orientationAnteriorPosteriorToolButton = new QToolButton(); this->orientationAnteriorPosteriorToolButton->setDefaultAction(this->orientationAnteriorPosteriorToolButtonAction); + WuQtUtilities::setToolButtonStyleForQt5Mac(this->orientationAnteriorPosteriorToolButton); WuQtUtilities::matchWidgetWidths(this->orientationLateralMedialToolButton, this->orientationDorsalVentralToolButton, @@ -1746,11 +2093,13 @@ BrainBrowserWindowToolBar::createOrientationWidget() QToolButton* orientationResetToolButton = new QToolButton(); orientationResetToolButton->setDefaultAction(this->orientationResetToolButtonAction); + WuQtUtilities::setToolButtonStyleForQt5Mac(orientationResetToolButton); this->orientationCustomViewSelectToolButton = new QToolButton(); this->orientationCustomViewSelectToolButton->setDefaultAction(this->customViewAction); this->orientationCustomViewSelectToolButton->setSizePolicy(QSizePolicy::Minimum, QSizePolicy::Fixed); + WuQtUtilities::setToolButtonStyleForQt5Mac(this->orientationCustomViewSelectToolButton); QGridLayout* buttonGridLayout = new QGridLayout(); buttonGridLayout->setColumnStretch(3, 100); @@ -1804,8 +2153,6 @@ BrainBrowserWindowToolBar::updateOrientationWidget(BrowserTabContent* browserTab return; } - this->incrementUpdateCounter(__CARET_FUNCTION_NAME__); - const int32_t tabIndex = browserTabContent->getTabNumber(); this->orientationWidgetGroup->blockAllSignals(true); @@ -1949,8 +2296,6 @@ BrainBrowserWindowToolBar::updateOrientationWidget(BrowserTabContent* browserTab this->orientationPosteriorToolButton->setVisible(showSingleViewOrientationButtons); } this->orientationWidgetGroup->blockAllSignals(false); - - this->decrementUpdateCounter(__CARET_FUNCTION_NAME__); } /** @@ -1983,6 +2328,7 @@ BrainBrowserWindowToolBar::createWholeBrainSurfaceOptionsWidget() this, SLOT(wholeBrainSurfaceLeftToolButtonTriggered(bool))); QToolButton* wholeBrainLeftSurfaceToolButton = new QToolButton(); + WuQtUtilities::setToolButtonStyleForQt5Mac(wholeBrainLeftSurfaceToolButton); wholeBrainLeftSurfaceToolButton->setDefaultAction(leftSurfaceAction); /* @@ -2000,6 +2346,7 @@ BrainBrowserWindowToolBar::createWholeBrainSurfaceOptionsWidget() this, SLOT(wholeBrainSurfaceRightToolButtonTriggered(bool))); QToolButton* wholeBrainRightSurfaceToolButton = new QToolButton(); + WuQtUtilities::setToolButtonStyleForQt5Mac(wholeBrainRightSurfaceToolButton); wholeBrainRightSurfaceToolButton->setDefaultAction(rightSurfaceAction); /* @@ -2017,6 +2364,7 @@ BrainBrowserWindowToolBar::createWholeBrainSurfaceOptionsWidget() this, SLOT(wholeBrainSurfaceCerebellumToolButtonTriggered(bool))); QToolButton* wholeBrainCerebellumSurfaceToolButton = new QToolButton(); + WuQtUtilities::setToolButtonStyleForQt5Mac(wholeBrainCerebellumSurfaceToolButton); wholeBrainCerebellumSurfaceToolButton->setDefaultAction(cerebellumSurfaceAction); /* @@ -2120,7 +2468,6 @@ BrainBrowserWindowToolBar::updateWholeBrainSurfaceOptionsWidget(BrowserTabConten if (this->wholeBrainSurfaceOptionsWidget->isHidden()) { return; } - this->incrementUpdateCounter(__CARET_FUNCTION_NAME__); ModelWholeBrain* wholeBrainModel = browserTabContent->getDisplayedWholeBrainModel(); if (wholeBrainModel != NULL) { @@ -2159,9 +2506,6 @@ BrainBrowserWindowToolBar::updateWholeBrainSurfaceOptionsWidget(BrowserTabConten this->wholeBrainSurfaceOptionsWidgetGroup->blockAllSignals(false); } - - - this->decrementUpdateCounter(__CARET_FUNCTION_NAME__); } /** @@ -2214,6 +2558,7 @@ BrainBrowserWindowToolBar::createModeWidget() this->modeInputModeAnnotationsAction->setCheckable(true); QToolButton* inputModeAnnotationsToolButton = new QToolButton(); inputModeAnnotationsToolButton->setDefaultAction(this->modeInputModeAnnotationsAction); + WuQtUtilities::setToolButtonStyleForQt5Mac(inputModeAnnotationsToolButton); /* * Borders @@ -2224,6 +2569,7 @@ BrainBrowserWindowToolBar::createModeWidget() this->modeInputModeBordersAction->setCheckable(true); QToolButton* inputModeBordersToolButton = new QToolButton(); inputModeBordersToolButton->setDefaultAction(this->modeInputModeBordersAction); + WuQtUtilities::setToolButtonStyleForQt5Mac(inputModeBordersToolButton); /* * Foci @@ -2234,16 +2580,23 @@ BrainBrowserWindowToolBar::createModeWidget() this->modeInputModeFociAction->setCheckable(true); QToolButton* inputModeFociToolButton = new QToolButton(); inputModeFociToolButton->setDefaultAction(this->modeInputModeFociAction); + WuQtUtilities::setToolButtonStyleForQt5Mac(inputModeFociToolButton); /* * Image */ - this->modeInputModeImageAction = WuQtUtilities::createAction("Image", - "Edit Image Control Points", - this); - this->modeInputModeImageAction->setCheckable(true); - QToolButton* inputModeImageToolButton = new QToolButton(); - inputModeImageToolButton->setDefaultAction(this->modeInputModeImageAction); + const bool showImageButtonFlag = false; + this->modeInputModeImageAction = NULL; + QToolButton* inputModeImageToolButton = NULL; + if (showImageButtonFlag) { + modeInputModeImageAction = WuQtUtilities::createAction("Image", + "Edit Image Control Points", + this); + this->modeInputModeImageAction->setCheckable(true); + inputModeImageToolButton = new QToolButton(); + inputModeImageToolButton->setDefaultAction(this->modeInputModeImageAction); + WuQtUtilities::setToolButtonStyleForQt5Mac(inputModeImageToolButton); + } /* * Volume Edit @@ -2254,6 +2607,7 @@ BrainBrowserWindowToolBar::createModeWidget() this->modeInputVolumeEditAction->setCheckable(true); QToolButton* inputModeVolumeEditButton = new QToolButton(); inputModeVolumeEditButton->setDefaultAction(this->modeInputVolumeEditAction); + WuQtUtilities::setToolButtonStyleForQt5Mac(inputModeVolumeEditButton); /* * View Mode @@ -2278,21 +2632,18 @@ BrainBrowserWindowToolBar::createModeWidget() this->modeInputModeViewAction->setCheckable(true); QToolButton* inputModeViewToolButton = new QToolButton(); inputModeViewToolButton->setDefaultAction(this->modeInputModeViewAction); + WuQtUtilities::setToolButtonStyleForQt5Mac(inputModeViewToolButton); WuQtUtilities::matchWidgetWidths(inputModeAnnotationsToolButton, inputModeBordersToolButton, - //inputModeFociToolButton, - //inputModeImageToolButton, inputModeViewToolButton, inputModeVolumeEditButton); -// inputModeFociToolButton->setContentsMargins(0, 0, 0, 0); -// inputModeImageToolButton->setContentsMargins(0, 0, 0, 0); inputModeFociToolButton->setSizePolicy(QSizePolicy::Preferred, inputModeFociToolButton->sizePolicy().verticalPolicy()); - inputModeImageToolButton->setSizePolicy(QSizePolicy::Preferred, - inputModeImageToolButton->sizePolicy().verticalPolicy()); -// inputModeFociToolButton->setFixedWidth(inputModeFociToolButton->sizeHint().width()); -// inputModeImageToolButton->setFixedWidth(inputModeImageToolButton->sizeHint().width()); + if (inputModeImageToolButton != NULL) { + inputModeImageToolButton->setSizePolicy(QSizePolicy::Preferred, + inputModeImageToolButton->sizePolicy().verticalPolicy()); + } /* * Layout for input modes @@ -2305,8 +2656,13 @@ BrainBrowserWindowToolBar::createModeWidget() modeRow++; inputModeLayout->addWidget(inputModeBordersToolButton, modeRow, 0, 1, 2, Qt::AlignHCenter); modeRow++; - inputModeLayout->addWidget(inputModeFociToolButton, modeRow, 0); - inputModeLayout->addWidget(inputModeImageToolButton, modeRow, 1); + if (inputModeImageToolButton != NULL) { + inputModeLayout->addWidget(inputModeFociToolButton, modeRow, 0); + inputModeLayout->addWidget(inputModeImageToolButton, modeRow, 1); + } + else { + inputModeLayout->addWidget(inputModeFociToolButton, modeRow, 0, 1, 2, Qt::AlignHCenter); + } modeRow++; inputModeLayout->addWidget(inputModeViewToolButton, modeRow, 0, 1, 2, Qt::AlignHCenter); modeRow++; @@ -2317,7 +2673,9 @@ BrainBrowserWindowToolBar::createModeWidget() this->modeInputModeActionGroup->addAction(this->modeInputModeAnnotationsAction); this->modeInputModeActionGroup->addAction(this->modeInputModeBordersAction); this->modeInputModeActionGroup->addAction(this->modeInputModeFociAction); - this->modeInputModeActionGroup->addAction(this->modeInputModeImageAction); + if (this->modeInputModeImageAction != NULL) { + this->modeInputModeActionGroup->addAction(this->modeInputModeImageAction); + } this->modeInputModeActionGroup->addAction(this->modeInputModeViewAction); this->modeInputModeActionGroup->addAction(this->modeInputVolumeEditAction); QObject::connect(this->modeInputModeActionGroup, SIGNAL(triggered(QAction*)), @@ -2354,9 +2712,24 @@ BrainBrowserWindowToolBar::modeInputModeActionTriggered(QAction* action) return; } + EventGetOrSetUserInputModeProcessor getInputModeEvent(this->browserWindowIndex); + EventManager::get()->sendEvent(getInputModeEvent.getPointer()); + const UserInputModeAbstract::UserInputMode currentMode = getInputModeEvent.getUserInputMode(); + UserInputModeAbstract::UserInputMode inputMode = UserInputModeAbstract::INVALID; if (action == this->modeInputModeAnnotationsAction) { + if (currentMode != UserInputModeAbstract::ANNOTATIONS) { + BrainBrowserWindow* bbw = GuiManager::get()->getBrowserWindowByWindowIndex(browserWindowIndex); + CaretAssert(bbw); + if ( ! bbw->changeInputModeToAnnotationsWarningDialog()) { + /* + * Since mode is rejected, need to update toolbar + */ + updateModeWidget(getTabContentFromSelectedTab()); + return; + } + } inputMode = UserInputModeAbstract::ANNOTATIONS; } else if (action == this->modeInputModeBordersAction) { @@ -2380,7 +2753,8 @@ BrainBrowserWindowToolBar::modeInputModeActionTriggered(QAction* action) else if (action == this->modeInputModeFociAction) { inputMode = UserInputModeAbstract::FOCI; } - else if (action == this->modeInputModeImageAction) { + else if ((action == this->modeInputModeImageAction) + && (this->modeInputModeImageAction != NULL)) { inputMode = UserInputModeAbstract::IMAGE; } else if (action == this->modeInputVolumeEditAction) { @@ -2414,8 +2788,6 @@ BrainBrowserWindowToolBar::updateModeWidget(BrowserTabContent* /*browserTabConte return; } - this->incrementUpdateCounter(__CARET_FUNCTION_NAME__); - this->modeWidgetGroup->blockAllSignals(true); EventGetOrSetUserInputModeProcessor getInputModeEvent(this->browserWindowIndex); @@ -2435,7 +2807,9 @@ BrainBrowserWindowToolBar::updateModeWidget(BrowserTabContent* /*browserTabConte this->modeInputModeFociAction->setChecked(true); break; case UserInputModeAbstract::IMAGE: - this->modeInputModeImageAction->setChecked(true); + if (this->modeInputModeImageAction != NULL) { + this->modeInputModeImageAction->setChecked(true); + } break; case UserInputModeAbstract::VOLUME_EDIT: this->modeInputVolumeEditAction->setChecked(true); @@ -2448,8 +2822,6 @@ BrainBrowserWindowToolBar::updateModeWidget(BrowserTabContent* /*browserTabConte this->modeWidgetGroup->blockAllSignals(false); this->updateDisplayedModeUserInputWidget(); - - this->decrementUpdateCounter(__CARET_FUNCTION_NAME__); } void @@ -2520,20 +2892,16 @@ BrainBrowserWindowToolBar::updateDisplayedModeUserInputWidget() /** * Create the tab options widget. * - * @param windowAspectRatioLockedAction - * Action for locking the window's aspect ratio. - * @param tabAspectRatioLockedAction - * Action for locking the tab's aspect ratio. + * @param lockWindowAndAllTabAspectAction + * Action for locking the window's aspect ratio and the aspect ratio of all tabs * - * @return The tab options widget. + * @return Button to lock window and tab's aspect ratios. */ QWidget* -BrainBrowserWindowToolBar::createTabOptionsWidget(QAction* windowAspectRatioLockedAction, - QAction* tabAspectRatioLockedAction) +BrainBrowserWindowToolBar::createTabOptionsWidget(QToolButton* toolBarLockWindowAndAllTabAspectRatioButton) { m_tabOptionsComponent = new BrainBrowserWindowToolBarTab(this->browserWindowIndex, - windowAspectRatioLockedAction, - tabAspectRatioLockedAction, + toolBarLockWindowAndAllTabAspectRatioButton, this); QWidget* w = this->createToolWidget("Tab", @@ -2571,7 +2939,7 @@ QWidget* BrainBrowserWindowToolBar::createChartTypeWidget() { m_chartTypeToolBarComponent = new BrainBrowserWindowToolBarChartType(this); - QWidget* w = this->createToolWidget("Chart Type", + QWidget* w = this->createToolWidget("Chart Type (OLD)", m_chartTypeToolBarComponent, WIDGET_PLACEMENT_LEFT, WIDGET_PLACEMENT_TOP, @@ -2596,6 +2964,76 @@ BrainBrowserWindowToolBar::updateChartTypeWidget(BrowserTabContent* browserTabCo m_chartTypeToolBarComponent->updateContent(browserTabContent); } +/** + * Create the chart two title widget. + * + * @return + * Widget containing the chart title. + */ +QWidget* +BrainBrowserWindowToolBar::createChartTwoTitleWidget() +{ + m_chartTwoTitleToolBarComponent = new BrainBrowserWindowToolBarChartTwoTitle(this); + QWidget* w = this->createToolWidget("Chart Title", + m_chartTwoTitleToolBarComponent, + WIDGET_PLACEMENT_LEFT, + WIDGET_PLACEMENT_TOP, + 100); + w->setVisible(false); + return w; +} +/** + * Update the chart title widget. + * + * @param browserTabContent + * The active tab content. + */ +void +BrainBrowserWindowToolBar::updateChartTwoTitleWidget(BrowserTabContent* browserTabContent) +{ + if (this->chartTwoTitleWidget->isHidden()) { + return; + } + + m_chartTwoTitleToolBarComponent->updateContent(browserTabContent); +} + + +/** + * Create the chart type two widget. + * + * @return + * Widget containing the chart type. + */ +QWidget* +BrainBrowserWindowToolBar::createChartTypeTwoWidget() +{ + m_chartTwoTypeToolBarComponent = new BrainBrowserWindowToolBarChartTwoType(this); + QWidget* w = this->createToolWidget("Chart Type", + m_chartTwoTypeToolBarComponent, + WIDGET_PLACEMENT_LEFT, + WIDGET_PLACEMENT_TOP, + 100); + w->setVisible(false); + return w; +} + +/** + * Update the chart type widget. + * + * @param browserTabContent + * The active model display (may be NULL). + */ +void +BrainBrowserWindowToolBar::updateChartTypeTwoWidget(BrowserTabContent* browserTabContent) +{ + if (this->chartTypeTwoWidget->isHidden()) { + return; + } + + m_chartTwoTypeToolBarComponent->updateContent(browserTabContent); +} + /** * Create the chart axes widget. * @@ -2665,6 +3103,108 @@ BrainBrowserWindowToolBar::updateChartAttributesWidget(BrowserTabContent* browse m_chartAttributesToolBarComponent->updateContent(browserTabContent); } +/** + * Create the chart two orientation widget. + * + * @return + * Widget containing the two chart orientation. + */ +QWidget* +BrainBrowserWindowToolBar::createChartTwoOrientationWidget() +{ + m_chartTwoOrientationToolBarComponent = new BrainBrowserWindowToolBarChartTwoOrientation(this); + QWidget* w = this->createToolWidget("Chart
    Orientation", + m_chartTwoOrientationToolBarComponent, + WIDGET_PLACEMENT_LEFT, + WIDGET_PLACEMENT_TOP, + 100); + w->setVisible(false); + return w; +} + +/** + * Update the chart orientation widget. + * + * @param browserTabContent + * The active model display (may be NULL). + */ +void +BrainBrowserWindowToolBar::updateChartTwoOrientationWidget(BrowserTabContent* browserTabContent) +{ + if (this->chartTwoOrientationWidget->isHidden()) { + return; + } + m_chartTwoOrientationToolBarComponent->updateContent(browserTabContent); +} + +/** + * Create the chart two attributes widget. + * + * @return + * Widget containing the chart two attributes. + */ +QWidget* +BrainBrowserWindowToolBar::createChartTwoAttributesWidget() +{ + this->m_chartTwoAttributesToolBarComponent = new BrainBrowserWindowToolBarChartTwoAttributes(this); + QWidget* w = this->createToolWidget("Chart
    Attributes", + this->m_chartTwoAttributesToolBarComponent, + WIDGET_PLACEMENT_LEFT, + WIDGET_PLACEMENT_TOP, + 100); + w->setVisible(false); + return w; +} + +/** + * Update the chart attributes widget. + * + * @param browserTabContent + * The active model display (may be NULL). + */ +void +BrainBrowserWindowToolBar::updateChartTwoAttributesWidget(BrowserTabContent* browserTabContent) +{ + if (this->chartTwoAttributesWidget->isHidden()) { + return; + } + m_chartTwoAttributesToolBarComponent->updateContent(browserTabContent); +} + +/** + * Create the chart two axes widget. + * + * @return + * Widget containing the chart two axes. + */ +QWidget* +BrainBrowserWindowToolBar::createChartTwoAxesWidget() +{ + this->m_chartTwoAxesToolBarComponent = new BrainBrowserWindowToolBarChartTwoAxes(this); + QWidget* w = this->createToolWidget("Chart Axes", + this->m_chartTwoAxesToolBarComponent, + WIDGET_PLACEMENT_LEFT, + WIDGET_PLACEMENT_TOP, + 100); + w->setVisible(false); + return w; +} + +/** + * Update the chart axes widget. + * + * @param browserTabContent + * The active model display (may be NULL). + */ +void +BrainBrowserWindowToolBar::updateChartTwoAxesWidget(BrowserTabContent* browserTabContent) +{ + if (this->chartTwoAxesWidget->isHidden()) { + return; + } + m_chartTwoAxesToolBarComponent->updateContent(browserTabContent); +} + /** * Create the single surface options widget. * @@ -2717,15 +3257,11 @@ BrainBrowserWindowToolBar::updateSingleSurfaceOptionsWidget(BrowserTabContent* b return; } - this->incrementUpdateCounter(__CARET_FUNCTION_NAME__); - this->singleSurfaceSelectionWidgetGroup->blockAllSignals(true); this->surfaceSurfaceSelectionControl->updateControl(browserTabContent->getSurfaceModelSelector()); this->singleSurfaceSelectionWidgetGroup->blockAllSignals(false); - - this->decrementUpdateCounter(__CARET_FUNCTION_NAME__); } /** @@ -2826,11 +3362,7 @@ BrainBrowserWindowToolBar::updateVolumeMontageWidget(BrowserTabContent* browserT return; } - this->incrementUpdateCounter(__CARET_FUNCTION_NAME__); - m_volumeMontageComponent->updateContent(browserTabContent); - - this->decrementUpdateCounter(__CARET_FUNCTION_NAME__); } /** @@ -2872,7 +3404,8 @@ BrainBrowserWindowToolBar::updateVolumePlaneWidget(BrowserTabContent* browserTab * a descriptive label added. * * @param name - * Name for the descriptive labe. + * Name for the descriptive label. For a multi-line label, + * separate the lines with an HTML "
    " tag. * @param childWidget * Child widget that is in the tool widget. * @param verticalBarPlacement @@ -2889,7 +3422,8 @@ BrainBrowserWindowToolBar::createToolWidget(const QString& name, const WidgetPlacement contentPlacement, const int /*horizontalStretching*/) { - QLabel* nameLabel = new QLabel("" + name + ""); + QLabel* nameLabel = new QLabel("

    " + name + "
    "); + nameLabel->setFixedHeight(nameLabel->sizeHint().height()); QWidget* w = new QWidget(); QGridLayout* layout = new QGridLayout(w); @@ -2974,15 +3508,17 @@ BrainBrowserWindowToolBar::updateToolBox() void BrainBrowserWindowToolBar::viewModeRadioButtonClicked(QAbstractButton*) { - CaretLogEntering(); BrowserTabContent* btc = this->getTabContentFromSelectedTab(); if (btc == NULL) { return; } - if (this->viewModeChartRadioButton->isChecked()) { + if (this->viewModeChartOneRadioButton->isChecked()) { btc->setSelectedModelType(ModelTypeEnum::MODEL_TYPE_CHART); } + else if (this->viewModeChartTwoRadioButton->isChecked()) { + btc->setSelectedModelType(ModelTypeEnum::MODEL_TYPE_CHART_TWO); + } else if (this->viewModeSurfaceRadioButton->isChecked()) { btc->setSelectedModelType(ModelTypeEnum::MODEL_TYPE_SURFACE); } @@ -2999,7 +3535,6 @@ BrainBrowserWindowToolBar::viewModeRadioButtonClicked(QAbstractButton*) btc->setSelectedModelType(ModelTypeEnum::MODEL_TYPE_INVALID); } - this->checkUpdateCounter(); this->updateToolBar(); this->updateTabName(-1); this->updateToolBox(); @@ -3015,10 +3550,7 @@ BrainBrowserWindowToolBar::orientationLeftOrLateralToolButtonTriggered(bool /*ch { BrowserTabContent* btc = this->getTabContentFromSelectedTab(); btc->leftView(); - this->updateGraphicsWindow(); - this->updateOtherYokedWindows(); - - this->checkUpdateCounter(); + this->updateGraphicsWindowAndYokedWindows(); } /** @@ -3029,10 +3561,7 @@ BrainBrowserWindowToolBar::orientationRightOrMedialToolButtonTriggered(bool /*ch { BrowserTabContent* btc = this->getTabContentFromSelectedTab(); btc->rightView(); - this->updateGraphicsWindow(); - this->updateOtherYokedWindows(); - - this->checkUpdateCounter(); + this->updateGraphicsWindowAndYokedWindows(); } /** @@ -3043,10 +3572,7 @@ BrainBrowserWindowToolBar::orientationAnteriorToolButtonTriggered(bool /*checked { BrowserTabContent* btc = this->getTabContentFromSelectedTab(); btc->anteriorView(); - this->updateGraphicsWindow(); - this->updateOtherYokedWindows(); - - this->checkUpdateCounter(); + this->updateGraphicsWindowAndYokedWindows(); } /** @@ -3057,10 +3583,7 @@ BrainBrowserWindowToolBar::orientationPosteriorToolButtonTriggered(bool /*checke { BrowserTabContent* btc = this->getTabContentFromSelectedTab(); btc->posteriorView(); - this->updateGraphicsWindow(); - this->updateOtherYokedWindows(); - - this->checkUpdateCounter(); + this->updateGraphicsWindowAndYokedWindows(); } /** @@ -3071,10 +3594,7 @@ BrainBrowserWindowToolBar::orientationDorsalToolButtonTriggered(bool /*checked*/ { BrowserTabContent* btc = this->getTabContentFromSelectedTab(); btc->dorsalView(); - this->updateGraphicsWindow(); - this->updateOtherYokedWindows(); - - this->checkUpdateCounter(); + this->updateGraphicsWindowAndYokedWindows(); } /** @@ -3085,10 +3605,7 @@ BrainBrowserWindowToolBar::orientationVentralToolButtonTriggered(bool /*checked* { BrowserTabContent* btc = this->getTabContentFromSelectedTab(); btc->ventralView(); - this->updateGraphicsWindow(); - this->updateOtherYokedWindows(); - - this->checkUpdateCounter(); + this->updateGraphicsWindowAndYokedWindows(); } /** @@ -3103,11 +3620,8 @@ BrainBrowserWindowToolBar::orientationResetToolButtonTriggered(bool /*checked*/) Model* mdc = btc->getModelForDisplay(); if (mdc != NULL) { this->updateVolumeIndicesWidget(btc); - this->updateGraphicsWindow(); - this->updateOtherYokedWindows(); + this->updateGraphicsWindowAndYokedWindows(); } - - this->checkUpdateCounter(); } /** @@ -3118,10 +3632,7 @@ BrainBrowserWindowToolBar::orientationLateralMedialToolButtonTriggered(bool /*ch { BrowserTabContent* btc = this->getTabContentFromSelectedTab(); btc->leftView(); - this->updateGraphicsWindow(); - this->updateOtherYokedWindows(); - - this->checkUpdateCounter(); + this->updateGraphicsWindowAndYokedWindows(); } /** @@ -3132,10 +3643,7 @@ BrainBrowserWindowToolBar::orientationDorsalVentralToolButtonTriggered(bool /*ch { BrowserTabContent* btc = this->getTabContentFromSelectedTab(); btc->dorsalView(); - this->updateGraphicsWindow(); - this->updateOtherYokedWindows(); - - this->checkUpdateCounter(); + this->updateGraphicsWindowAndYokedWindows(); } /** @@ -3146,10 +3654,7 @@ BrainBrowserWindowToolBar::orientationAnteriorPosteriorToolButtonTriggered(bool { BrowserTabContent* btc = this->getTabContentFromSelectedTab(); btc->anteriorView(); - this->updateGraphicsWindow(); - this->updateOtherYokedWindows(); - - this->checkUpdateCounter(); + this->updateGraphicsWindowAndYokedWindows(); } /** @@ -3190,8 +3695,7 @@ BrainBrowserWindowToolBar::customViewActionTriggered() if (prefs->getCustomView(customViewName, modelTransform)) { BrowserTabContent* btc = this->getTabContentFromSelectedTab(); btc->setTransformationsFromModelTransform(modelTransform); - this->updateGraphicsWindow(); - this->updateOtherYokedWindows(); + this->updateGraphicsWindowAndYokedWindows(); } } } @@ -3203,9 +3707,6 @@ BrainBrowserWindowToolBar::customViewActionTriggered() void BrainBrowserWindowToolBar::wholeBrainSurfaceTypeComboBoxIndexChanged(int /*indx*/) { - CaretLogEntering(); - this->checkUpdateCounter(); - BrowserTabContent* btc = this->getTabContentFromSelectedTab(); const int32_t tabIndex = btc->getTabNumber(); @@ -3222,8 +3723,7 @@ BrainBrowserWindowToolBar::wholeBrainSurfaceTypeComboBoxIndexChanged(int /*indx* if (isValid) { wholeBrainModel->setSelectedSurfaceType(tabIndex, surfaceType); this->updateVolumeIndicesWidget(btc); /* slices may get deselected */ - this->updateGraphicsWindow(); - this->updateOtherYokedWindows(); + this->updateGraphicsWindowAndYokedWindows(); } } } @@ -3234,9 +3734,6 @@ BrainBrowserWindowToolBar::wholeBrainSurfaceTypeComboBoxIndexChanged(int /*indx* void BrainBrowserWindowToolBar::wholeBrainSurfaceLeftCheckBoxStateChanged(int /*state*/) { - CaretLogEntering(); - this->checkUpdateCounter(); - BrowserTabContent* btc = this->getTabContentFromSelectedTab(); ModelWholeBrain* wholeBrainModel = btc->getDisplayedWholeBrainModel(); @@ -3245,8 +3742,7 @@ BrainBrowserWindowToolBar::wholeBrainSurfaceLeftCheckBoxStateChanged(int /*state } btc->setWholeBrainLeftEnabled(this->wholeBrainSurfaceLeftCheckBox->isChecked()); - this->updateGraphicsWindow(); - this->updateOtherYokedWindows(); + this->updateGraphicsWindowAndYokedWindows(); } /** @@ -3255,9 +3751,6 @@ BrainBrowserWindowToolBar::wholeBrainSurfaceLeftCheckBoxStateChanged(int /*state void BrainBrowserWindowToolBar::wholeBrainSurfaceLeftToolButtonTriggered(bool /*checked*/) { - CaretLogEntering(); - this->checkUpdateCounter(); - BrowserTabContent* btc = this->getTabContentFromSelectedTab(); ModelWholeBrain* wholeBrainModel = btc->getDisplayedWholeBrainModel(); if (wholeBrainModel == NULL) { @@ -3296,8 +3789,7 @@ BrainBrowserWindowToolBar::wholeBrainSurfaceLeftToolButtonTriggered(bool /*check wholeBrainModel->setSelectedSurface(StructureEnum::CORTEX_LEFT, tabIndex, surfaces[i]); - this->updateGraphicsWindow(); - this->updateOtherYokedWindows(); + this->updateGraphicsWindowAndYokedWindows(); break; } } @@ -3312,11 +3804,6 @@ BrainBrowserWindowToolBar::wholeBrainSurfaceLeftToolButtonTriggered(bool /*check void BrainBrowserWindowToolBar::wholeBrainSurfaceRightToolButtonTriggered(bool /*checked*/) { - CaretLogEntering(); - this->checkUpdateCounter(); - CaretLogEntering(); - this->checkUpdateCounter(); - BrowserTabContent* btc = this->getTabContentFromSelectedTab(); ModelWholeBrain* wholeBrainModel = btc->getDisplayedWholeBrainModel(); if (wholeBrainModel == NULL) { @@ -3355,8 +3842,7 @@ BrainBrowserWindowToolBar::wholeBrainSurfaceRightToolButtonTriggered(bool /*chec wholeBrainModel->setSelectedSurface(StructureEnum::CORTEX_RIGHT, tabIndex, surfaces[i]); - this->updateGraphicsWindow(); - this->updateOtherYokedWindows(); + this->updateGraphicsWindowAndYokedWindows(); break; } } @@ -3371,11 +3857,6 @@ BrainBrowserWindowToolBar::wholeBrainSurfaceRightToolButtonTriggered(bool /*chec void BrainBrowserWindowToolBar::wholeBrainSurfaceCerebellumToolButtonTriggered(bool /*checked*/) { - CaretLogEntering(); - this->checkUpdateCounter(); - CaretLogEntering(); - this->checkUpdateCounter(); - BrowserTabContent* btc = this->getTabContentFromSelectedTab(); ModelWholeBrain* wholeBrainModel = btc->getDisplayedWholeBrainModel(); if (wholeBrainModel == NULL) { @@ -3414,8 +3895,7 @@ BrainBrowserWindowToolBar::wholeBrainSurfaceCerebellumToolButtonTriggered(bool / wholeBrainModel->setSelectedSurface(StructureEnum::CEREBELLUM, tabIndex, surfaces[i]); - this->updateGraphicsWindow(); - this->updateOtherYokedWindows(); + this->updateGraphicsWindowAndYokedWindows(); break; } } @@ -3430,9 +3910,6 @@ BrainBrowserWindowToolBar::wholeBrainSurfaceCerebellumToolButtonTriggered(bool / void BrainBrowserWindowToolBar::wholeBrainSurfaceRightCheckBoxStateChanged(int /*state*/) { - CaretLogEntering(); - this->checkUpdateCounter(); - BrowserTabContent* btc = this->getTabContentFromSelectedTab(); ModelWholeBrain* wholeBrainModel = btc->getDisplayedWholeBrainModel(); @@ -3441,8 +3918,7 @@ BrainBrowserWindowToolBar::wholeBrainSurfaceRightCheckBoxStateChanged(int /*stat } btc->setWholeBrainRightEnabled(this->wholeBrainSurfaceRightCheckBox->isChecked()); - this->updateGraphicsWindow(); - this->updateOtherYokedWindows(); + this->updateGraphicsWindowAndYokedWindows(); } /** @@ -3451,9 +3927,6 @@ BrainBrowserWindowToolBar::wholeBrainSurfaceRightCheckBoxStateChanged(int /*stat void BrainBrowserWindowToolBar::wholeBrainSurfaceCerebellumCheckBoxStateChanged(int /*state*/) { - CaretLogEntering(); - this->checkUpdateCounter(); - BrowserTabContent* btc = this->getTabContentFromSelectedTab(); ModelWholeBrain* wholeBrainModel = btc->getDisplayedWholeBrainModel(); @@ -3462,8 +3935,7 @@ BrainBrowserWindowToolBar::wholeBrainSurfaceCerebellumCheckBoxStateChanged(int / } btc->setWholeBrainCerebellumEnabled(this->wholeBrainSurfaceCerebellumCheckBox->isChecked()); - this->updateGraphicsWindow(); - this->updateOtherYokedWindows(); + this->updateGraphicsWindowAndYokedWindows(); } /** @@ -3472,9 +3944,6 @@ BrainBrowserWindowToolBar::wholeBrainSurfaceCerebellumCheckBoxStateChanged(int / void BrainBrowserWindowToolBar::wholeBrainSurfaceSeparationLeftRightSpinBoxValueChanged(double /*d*/) { - CaretLogEntering(); - this->checkUpdateCounter(); - BrowserTabContent* btc = this->getTabContentFromSelectedTab(); ModelWholeBrain* wholeBrainModel = btc->getDisplayedWholeBrainModel(); @@ -3483,8 +3952,7 @@ BrainBrowserWindowToolBar::wholeBrainSurfaceSeparationLeftRightSpinBoxValueChang } btc->setWholeBrainLeftRightSeparation(this->wholeBrainSurfaceSeparationLeftRightSpinBox->value()); - this->updateGraphicsWindow(); - this->updateOtherYokedWindows(); + this->updateGraphicsWindowAndYokedWindows(); } /** @@ -3493,9 +3961,6 @@ BrainBrowserWindowToolBar::wholeBrainSurfaceSeparationLeftRightSpinBoxValueChang void BrainBrowserWindowToolBar::wholeBrainSurfaceSeparationCerebellumSpinBoxSelected(double /*d*/) { - CaretLogEntering(); - this->checkUpdateCounter(); - BrowserTabContent* btc = this->getTabContentFromSelectedTab(); ModelWholeBrain* wholeBrainModel = btc->getDisplayedWholeBrainModel(); @@ -3504,8 +3969,7 @@ BrainBrowserWindowToolBar::wholeBrainSurfaceSeparationCerebellumSpinBoxSelected( } btc->setWholeBrainCerebellumSeparation(this->wholeBrainSurfaceSeparationCerebellumSpinBox->value()); - this->updateGraphicsWindow(); - this->updateOtherYokedWindows(); + this->updateGraphicsWindowAndYokedWindows(); } /** @@ -3531,29 +3995,6 @@ BrainBrowserWindowToolBar::surfaceSelectionControlChanged( } this->updateTabName(-1); - - this->checkUpdateCounter(); -} - -void -BrainBrowserWindowToolBar::checkUpdateCounter() -{ - if (this->updateCounter != 0) { - CaretLogWarning(AString("Update counter is non-zero, this indicates that signal needs to be blocked during update, value=") - + AString::number(updateCounter)); - } -} - -void -BrainBrowserWindowToolBar::incrementUpdateCounter(const char* /*methodName*/) -{ - this->updateCounter++; -} - -void -BrainBrowserWindowToolBar::decrementUpdateCounter(const char* /*methodName*/) -{ - this->updateCounter--; } /** @@ -3565,21 +4006,24 @@ BrainBrowserWindowToolBar::decrementUpdateCounter(const char* /*methodName*/) void BrainBrowserWindowToolBar::receiveEvent(Event* event) { - if (event->getEventType() == EventTypeEnum::EVENT_BROWSER_WINDOW_CONTENT_GET) { - EventBrowserWindowContentGet* getModelEvent = - dynamic_cast(event); + if (event->getEventType() == EventTypeEnum::EVENT_BROWSER_WINDOW_DRAWING_CONTENT_GET) { + EventBrowserWindowDrawingContent* getModelEvent = + dynamic_cast(event); CaretAssert(getModelEvent); if (getModelEvent->getBrowserWindowIndex() == this->browserWindowIndex) { BrainBrowserWindow* browserWindow = GuiManager::get()->getBrowserWindowByWindowIndex(this->browserWindowIndex); + if (browserWindow != NULL) { + const int32_t numTabs = this->tabBar->count(); + for (int32_t i = 0; i < numTabs; i++) { + BrowserTabContent* btc = this->getTabContentFromTab(i); + getModelEvent->addBrowserTab(btc); + } + + getModelEvent->setTileTabsSelected(browserWindow->isTileTabsSelected()); + if (browserWindow->isTileTabsSelected()) { - const int32_t numTabs = this->tabBar->count(); - for (int32_t i = 0; i < numTabs; i++) { - BrowserTabContent* btc = this->getTabContentFromTab(i); - getModelEvent->addTabContentToDraw(btc); - } - /* * Tab that is highlighted so user knows which tab * any changes in toolbar/toolboxes apply to. @@ -3588,10 +4032,6 @@ BrainBrowserWindowToolBar::receiveEvent(Event* event) getModelEvent->setTileTabsConfiguration(browserWindow->getSelectedTileTabsConfiguration()); } - else { - BrowserTabContent* btc = this->getTabContentFromSelectedTab(); - getModelEvent->addTabContentToDraw(btc); - } getModelEvent->setSelectedBrowserTabContent(this->getTabContentFromSelectedTab()); } @@ -3623,10 +4063,15 @@ BrainBrowserWindowToolBar::receiveEvent(Event* event) case EventBrowserWindowCreateTabs::MODE_LOADED_DATA_FILE: if (tabBar->count() == 0) { AString errorMessage; - createNewTab(errorMessage); + BrowserTabContent* tabContent = createNewTab(errorMessage); if (errorMessage.isEmpty() == false) { CaretLogSevere(errorMessage); } + if (tabContent != NULL) { + insertTabContentPrivate(InsertTabMode::APPEND, + tabContent, + -1); + } } break; case EventBrowserWindowCreateTabs::MODE_LOADED_SPEC_FILE: @@ -3636,15 +4081,44 @@ BrainBrowserWindowToolBar::receiveEvent(Event* event) } tabEvent->setEventProcessed(); } + else if (event->getEventType() == EventTypeEnum::EVENT_BROWSER_WINDOW_TILE_TAB_OPERATION) { + EventBrowserWindowTileTabOperation* tileTabsEvent = + dynamic_cast(event); + CaretAssert(tileTabsEvent); + + if (tileTabsEvent->getWindowIndex() == this->browserWindowIndex) { + const int32_t browserTabIndex = tileTabsEvent->getBrowserTabIndex(); + int32_t tabBarIndex = getTabBarIndexWithBrowserTabIndex(browserTabIndex); + if (tabBarIndex >= 0) { + const EventBrowserWindowTileTabOperation::Operation operation = tileTabsEvent->getOperation(); + switch (operation) { + case EventBrowserWindowTileTabOperation::OPERATION_NEW_TAB_AFTER: + insertNewTabAtTabBarIndex(tabBarIndex + 1); + break; + case EventBrowserWindowTileTabOperation::OPERATION_NEW_TAB_BEFORE: + insertNewTabAtTabBarIndex(tabBarIndex); + break; + case EventBrowserWindowTileTabOperation::OPERATION_SELECT_TAB: + m_tileTabsHighlightingTimerEnabledFlag = false; + this->tabBar->setCurrentIndex(tabBarIndex); + m_tileTabsHighlightingTimerEnabledFlag = true; + break; + } + } + + tileTabsEvent->setEventProcessed(); + } + } else if (event->getEventType() == EventTypeEnum::EVENT_UPDATE_YOKED_WINDOWS) { EventUpdateYokedWindows* yokeUpdateEvent = - dynamic_cast(event); + dynamic_cast(event); CaretAssert(yokeUpdateEvent); BrowserTabContent* browserTabContent = getTabContentFromSelectedTab(); if (browserTabContent != NULL) { if (this->browserWindowIndex != yokeUpdateEvent->getBrowserWindowIndexThatIssuedEvent()) { - if (browserTabContent->getYokingGroup() == yokeUpdateEvent->getYokingGroup()) { + if (yokeUpdateEvent->isBrainOrChartModelYoking(browserTabContent->getBrainModelYokingGroup(), + browserTabContent->getChartModelYokingGroup())) { this->updateToolBar(); this->updateGraphicsWindow(); } @@ -3677,25 +4151,49 @@ BrainBrowserWindowToolBar::receiveEvent(Event* event) else { } +} + +/** + * Get the tab bar index containing the browser tab index. + * + * @param browserTabIndex + * Index of the browser tab. + * @return + * Index in tab bar containing the browser tab index or -1 if not found. + */ +int32_t +BrainBrowserWindowToolBar::getTabBarIndexWithBrowserTabIndex(const int32_t browserTabIndex) +{ + const int numTabs = this->tabBar->count(); + if (numTabs > 0) { + for (int32_t i = 0; i < numTabs; i++) { + const BrowserTabContent* btc = getTabContentFromTab(i); + if (btc->getTabNumber() == browserTabIndex){ + return i; + } + } + } -// CaretLogFinest("Toolbar width/height: " -// + AString::number(width()) -// + "/" -// + AString::number(height())); + return -1; } /** - * If this windows is yoked, issue an event to update other - * windows that are using the same yoking. + * If this window is yoked, update all windows since they may + * be yoked to this window. If NOT yoked, just update this window. */ void -BrainBrowserWindowToolBar::updateOtherYokedWindows() +BrainBrowserWindowToolBar::updateGraphicsWindowAndYokedWindows() { BrowserTabContent* browserTabContent = getTabContentFromSelectedTab(); if (browserTabContent != NULL) { - if (browserTabContent->isYoked()) { + if (browserTabContent->isBrainModelYoked()) { + EventManager::get()->sendEvent(EventGraphicsUpdateAllWindows().getPointer()); EventManager::get()->sendEvent(EventUpdateYokedWindows(this->browserWindowIndex, - browserTabContent->getYokingGroup()).getPointer()); + browserTabContent->getBrainModelYokingGroup(), + browserTabContent->getChartModelYokingGroup()).getPointer()); + } + else { + updateGraphicsWindow(); } } } @@ -3886,8 +4384,10 @@ BrainBrowserWindowToolBar::restoreFromScene(const SceneAttributes* sceneAttribut if (tabContent->getTabNumber() == selectedTabIndex) { defaultTabBarIndex = iTab; } - this->insertTabAtIndex(tabContent, - iTab); + CaretAssert(iTab >= 0); + insertTabContentPrivate(InsertTabMode::AT_TAB_BAR_INDEX, + tabContent, + iTab); } else { sceneAttributes->addToErrorMessage("Toolbar in window " @@ -3914,3 +4414,12 @@ BrainBrowserWindowToolBar::restoreFromScene(const SceneAttributes* sceneAttribut showHideToolBar(showToolBar); } +/** + * @return Number of tabs in the tab bar. + */ +int32_t +BrainBrowserWindowToolBar::getNumberOfTabs() const +{ + return this->tabBar->count(); +} + diff --git a/src/GuiQt/BrainBrowserWindowToolBar.h b/src/GuiQt/BrainBrowserWindowToolBar.h index 1dedb247222c2d15686ba31912d683e480502627..d7bfe5410c8ef449c6344ef91f294127af2c9dcf 100644 --- a/src/GuiQt/BrainBrowserWindowToolBar.h +++ b/src/GuiQt/BrainBrowserWindowToolBar.h @@ -52,6 +52,11 @@ namespace caret { class BrainBrowserWindowToolBarChartAxes; class BrainBrowserWindowToolBarChartAttributes; + class BrainBrowserWindowToolBarChartTwoAttributes; + class BrainBrowserWindowToolBarChartTwoAxes; + class BrainBrowserWindowToolBarChartTwoOrientation; + class BrainBrowserWindowToolBarChartTwoTitle; + class BrainBrowserWindowToolBarChartTwoType; class BrainBrowserWindowToolBarChartType; class BrainBrowserWindowToolBarClipping; class BrainBrowserWindowToolBarSlicePlane; @@ -80,15 +85,14 @@ namespace caret { BrowserTabContent* initialBrowserTabContent, QAction* overlayToolBoxAction, QAction* layersToolBoxAction, - QAction* windowAspectRatioLockedAction, - QAction* tabAspectRatioLockedAction, + QToolButton* toolBarLockWindowAndAllTabAspectRatioButton, BrainBrowserWindow* parentBrainBrowserWindow); ~BrainBrowserWindowToolBar(); void addNewTab(); - void addNewTabCloneContent(BrowserTabContent* browserTabContentToBeCloned); + void addNewDuplicatedTab(BrowserTabContent* browserTabContentToBeCloned); void addNewTabWithContent(BrowserTabContent* browserTabContent); @@ -96,6 +100,7 @@ namespace caret { void receiveEvent(Event* event); + int32_t getNumberOfTabs() const; virtual SceneClass* saveToScene(const SceneAttributes* sceneAttributes, const AString& instanceName); @@ -120,27 +125,34 @@ namespace caret { BrowserTabContent* getTabContentFromSelectedTab(); BrowserTabContent* getTabContentFromTab(const int tabIndex); + int32_t getTabBarIndexWithBrowserTabIndex(const int32_t browserTabIndex); + Model* getDisplayedModel(); int32_t loadIntoTab(const int32_t tabIndex, Model* controller); void updateGraphicsWindow(); + void updateGraphicsWindowAndYokedWindows(); void updateUserInterface(); void updateToolBox(); + void updateAllTabNames(); void updateTabName(const int32_t tabIndex); - void updateOtherYokedWindows(); QWidget* createViewWidget(); QWidget* createOrientationWidget(); QWidget* createWholeBrainSurfaceOptionsWidget(); QWidget* createVolumeIndicesWidget(); QWidget* createModeWidget(); - QWidget* createTabOptionsWidget(QAction* windowAspectRatioLockedAction, - QAction* tabAspectRatioLockedAction); + QWidget* createTabOptionsWidget(QToolButton* toolBarLockWindowAndAllTabAspectRatioButton); QWidget* createChartAxesWidget(); QWidget* createChartAttributesWidget(); + QWidget* createChartTwoOrientationWidget(); + QWidget* createChartTwoAttributesWidget(); + QWidget* createChartTwoAxesWidget(); + QWidget* createChartTwoTitleWidget(); QWidget* createChartTypeWidget(); + QWidget* createChartTypeTwoWidget(); QWidget* createSingleSurfaceOptionsWidget(); QWidget* createSurfaceMontageOptionsWidget(); QWidget* createClippingOptionsWidget(); @@ -157,7 +169,12 @@ namespace caret { void updateSurfaceMontageOptionsWidget(BrowserTabContent* browserTabContent); void updateChartAxesWidget(BrowserTabContent* browserTabContent); void updateChartAttributesWidget(BrowserTabContent* browserTabContent); + void updateChartTwoAttributesWidget(BrowserTabContent* browserTabContent); + void updateChartTwoAxesWidget(BrowserTabContent* browserTabContent); + void updateChartTwoOrientationWidget(BrowserTabContent* browserTabContent); + void updateChartTwoTitleWidget(BrowserTabContent* browserTabContent); void updateChartTypeWidget(BrowserTabContent* browserTabContent); + void updateChartTypeTwoWidget(BrowserTabContent* browserTabContent); void updateVolumeMontageWidget(BrowserTabContent* browserTabContent); void updateVolumePlaneWidget(BrowserTabContent* browserTabContent); void updateClippingOptionsWidget(BrowserTabContent* browserTabContent); @@ -180,8 +197,13 @@ namespace caret { QWidget* volumeMontageWidget; QWidget* volumePlaneWidget; QWidget* chartTypeWidget; + QWidget* chartTypeTwoWidget; QWidget* chartAxesWidget; QWidget* chartAttributesWidget; + QWidget* chartTwoOrientationWidget; + QWidget* chartTwoAttributesWidget; + QWidget* chartTwoAxesWidget; + QWidget* chartTwoTitleWidget; WuQWidgetObjectGroup* viewWidgetGroup; WuQWidgetObjectGroup* orientationWidgetGroup; @@ -205,19 +227,6 @@ namespace caret { /** Is set to the user input widget provided by the user input processor */ QWidget* userInputControlsWidgetActiveInputWidget; - /** - * When updating, no signals should be emitted. This variable - * is incremented at the beginning of an update method and - * decremented at the end of the update method. If it is - * non-zero in a slot method, then a signal was emitted during - * the update and the widget that emitted the signal should - * have its signal blocked. - */ - void incrementUpdateCounter(const char* methodName); - void decrementUpdateCounter(const char* methodName); - void checkUpdateCounter(); - int updateCounter; - void removeAndReturnAllTabs(std::vector& allTabContent); void getAllTabContent(std::vector& allTabContent) const; @@ -245,24 +254,36 @@ namespace caret { void selectedTabChanged(int indx); void tabMoved(int, int); void tabCloseSelected(int); + void showTabMenu(const QPoint& pos); private: - void insertTabAtIndex(BrowserTabContent* browserTabContent, - const int32_t insertAtIndex); + enum class InsertTabMode { + APPEND, + AT_TAB_BAR_INDEX, + AT_TAB_CONTENTS_INDEX + }; + + bool allowAddingNewTab(); - void addOrInsertNewTab(BrowserTabContent* browserTabContent, - const int32_t insertAtIndex); + void insertTabContentPrivate(const InsertTabMode insertTabMode, + BrowserTabContent* browserTabContent, + const int32_t tabBarIndex); void removeTab(int index); void tabClosed(int index); + void insertNewTabAtTabBarIndex(int32_t tabBarIndex); + void insertAndCloneTabContentAtTabBarIndex(const BrowserTabContent* tabContentToBeCloned, + const int32_t tabBarIndex); + BrowserTabContent* createNewTab(AString& errorMessage); QRadioButton* viewModeSurfaceRadioButton; QRadioButton* viewModeSurfaceMontageRadioButton; QRadioButton* viewModeVolumeRadioButton; QRadioButton* viewModeWholeBrainRadioButton; - QRadioButton* viewModeChartRadioButton; + QRadioButton* viewModeChartOneRadioButton; + QRadioButton* viewModeChartTwoRadioButton; QAction* customViewAction; @@ -349,8 +370,14 @@ namespace caret { private: BrainBrowserWindowToolBarChartAxes* m_chartAxisToolBarComponent; + BrainBrowserWindowToolBarChartTwoType* m_chartTwoTypeToolBarComponent; BrainBrowserWindowToolBarChartType* m_chartTypeToolBarComponent; BrainBrowserWindowToolBarChartAttributes* m_chartAttributesToolBarComponent; + BrainBrowserWindowToolBarChartTwoAxes* m_chartTwoAxesToolBarComponent; + BrainBrowserWindowToolBarChartTwoOrientation* m_chartTwoOrientationToolBarComponent; + BrainBrowserWindowToolBarChartTwoAttributes* m_chartTwoAttributesToolBarComponent; + BrainBrowserWindowToolBarChartTwoTitle* m_chartTwoTitleToolBarComponent; + BrainBrowserWindowToolBarSurfaceMontage* m_surfaceMontageToolBarComponent; BrainBrowserWindowToolBarClipping* m_clippingToolBarComponent; @@ -384,6 +411,11 @@ namespace caret { private: friend class BrainBrowserWindow; friend class BrainBrowserWindowToolBarChartAxes; + friend class BrainBrowserWindowToolBarChartTwoAxes; + friend class BrainBrowserWindowToolBarChartTwoOrientation; + friend class BrainBrowserWindowToolBarChartTwoAttributes; + + friend class BrainBrowserWindowToolBarChartTwoType; friend class BrainBrowserWindowToolBarChartType; friend class BrainBrowserWindowToolBarClipping; friend class BrainBrowserWindowToolBarComponent; @@ -400,9 +432,20 @@ namespace caret { * box around it. */ int32_t m_tabIndexForTileTabsHighlighting; + QTimer* m_tileTabsHighlightingTimer = NULL; + bool m_tileTabsHighlightingTimerEnabledFlag = true; bool isContructorFinished; bool isDestructionInProgress; + + /** + * Tracks when update is performed to catch incorrectly emitted signals. + */ + bool m_performingUpdateFlag = false; + + const int32_t TAB_INDEX_APPEND_TO_TOOLBAR = 10000; + + friend class BrainBrowserWindowToolBarTabPopUpMenu; }; } diff --git a/src/GuiQt/BrainBrowserWindowToolBarChartAttributes.cxx b/src/GuiQt/BrainBrowserWindowToolBarChartAttributes.cxx index 5cd60d51af0e0a454a2c448acf9738d94a93b3b3..425708ea2b27066c47d088bcdf583ff3ef4a059d 100644 --- a/src/GuiQt/BrainBrowserWindowToolBarChartAttributes.cxx +++ b/src/GuiQt/BrainBrowserWindowToolBarChartAttributes.cxx @@ -37,7 +37,6 @@ #include "CaretDataFileSelectionModel.h" #include "EnumComboBoxTemplate.h" #include "CaretMappableDataFile.h" -#include "CaretMappableDataFileAndMapSelectionModel.h" #include "ChartMatrixDisplayProperties.h" #include "ChartModelDataSeries.h" #include "ChartModelFrequencySeries.h" @@ -129,26 +128,26 @@ BrainBrowserWindowToolBarChartAttributes::getCartesianChart() BrowserTabContent* browserTabContent = getTabContentFromSelectedTab(); if (browserTabContent != NULL) { - ModelChart* modelChart = browserTabContent->getDisplayedChartModel(); + ModelChart* modelChart = browserTabContent->getDisplayedChartOneModel(); if (modelChart != NULL) { const int32_t tabIndex = browserTabContent->getTabNumber(); - const ChartDataTypeEnum::Enum chartType = modelChart->getSelectedChartDataType(tabIndex); + const ChartOneDataTypeEnum::Enum chartType = modelChart->getSelectedChartOneDataType(tabIndex); switch (chartType) { - case ChartDataTypeEnum::CHART_DATA_TYPE_INVALID: + case ChartOneDataTypeEnum::CHART_DATA_TYPE_INVALID: break; - case ChartDataTypeEnum::CHART_DATA_TYPE_MATRIX_LAYER: + case ChartOneDataTypeEnum::CHART_DATA_TYPE_MATRIX_LAYER: break; - case ChartDataTypeEnum::CHART_DATA_TYPE_MATRIX_SERIES: + case ChartOneDataTypeEnum::CHART_DATA_TYPE_MATRIX_SERIES: break; - case ChartDataTypeEnum::CHART_DATA_TYPE_LINE_DATA_SERIES: + case ChartOneDataTypeEnum::CHART_DATA_TYPE_LINE_DATA_SERIES: cartesianChart = modelChart->getSelectedDataSeriesChartModel(tabIndex); //dynamic_cast(chart); break; - case ChartDataTypeEnum::CHART_DATA_TYPE_LINE_FREQUENCY_SERIES: + case ChartOneDataTypeEnum::CHART_DATA_TYPE_LINE_FREQUENCY_SERIES: cartesianChart = modelChart->getSelectedFrequencySeriesChartModel(tabIndex); break; - case ChartDataTypeEnum::CHART_DATA_TYPE_LINE_TIME_SERIES: + case ChartOneDataTypeEnum::CHART_DATA_TYPE_LINE_TIME_SERIES: cartesianChart = modelChart->getSelectedTimeSeriesChartModel(tabIndex); //dynamic_cast(chart); break; } @@ -169,16 +168,16 @@ BrainBrowserWindowToolBarChartAttributes::getChartableMatrixDisplayProperties() BrowserTabContent* browserTabContent = getTabContentFromSelectedTab(); if (browserTabContent != NULL) { - ModelChart* modelChart = browserTabContent->getDisplayedChartModel(); + ModelChart* modelChart = browserTabContent->getDisplayedChartOneModel(); if (modelChart != NULL) { const int32_t tabIndex = browserTabContent->getTabNumber(); - const ChartDataTypeEnum::Enum chartType = modelChart->getSelectedChartDataType(tabIndex); + const ChartOneDataTypeEnum::Enum chartType = modelChart->getSelectedChartOneDataType(tabIndex); switch (chartType) { - case ChartDataTypeEnum::CHART_DATA_TYPE_INVALID: + case ChartOneDataTypeEnum::CHART_DATA_TYPE_INVALID: break; - case ChartDataTypeEnum::CHART_DATA_TYPE_MATRIX_LAYER: + case ChartOneDataTypeEnum::CHART_DATA_TYPE_MATRIX_LAYER: { ChartableMatrixInterface* matrixInterface = modelChart->getChartableMatrixParcelFileSelectionModel(tabIndex)->getSelectedFileOfType(); if (matrixInterface != NULL) { @@ -186,7 +185,7 @@ BrainBrowserWindowToolBarChartAttributes::getChartableMatrixDisplayProperties() } } break; - case ChartDataTypeEnum::CHART_DATA_TYPE_MATRIX_SERIES: + case ChartOneDataTypeEnum::CHART_DATA_TYPE_MATRIX_SERIES: { CaretDataFileSelectionModel* fileModel = modelChart->getChartableMatrixSeriesFileSelectionModel(tabIndex); CaretDataFile* caretFile = fileModel->getSelectedFile(); @@ -198,11 +197,11 @@ BrainBrowserWindowToolBarChartAttributes::getChartableMatrixDisplayProperties() } } break; - case ChartDataTypeEnum::CHART_DATA_TYPE_LINE_DATA_SERIES: + case ChartOneDataTypeEnum::CHART_DATA_TYPE_LINE_DATA_SERIES: break; - case ChartDataTypeEnum::CHART_DATA_TYPE_LINE_FREQUENCY_SERIES: + case ChartOneDataTypeEnum::CHART_DATA_TYPE_LINE_FREQUENCY_SERIES: break; - case ChartDataTypeEnum::CHART_DATA_TYPE_LINE_TIME_SERIES: + case ChartOneDataTypeEnum::CHART_DATA_TYPE_LINE_TIME_SERIES: break; } } @@ -351,6 +350,7 @@ EventListenerInterface() SLOT(resetButtonClicked())); QToolButton* resetToolButton = new QToolButton(); resetToolButton->setDefaultAction(resetButtonAction); + WuQtUtilities::setToolButtonStyleForQt5Mac(resetToolButton); WuQtUtilities::matchWidgetWidths(m_cellHeightSpinBox, m_cellWidthSpinBox); diff --git a/src/GuiQt/BrainBrowserWindowToolBarChartAxes.cxx b/src/GuiQt/BrainBrowserWindowToolBarChartAxes.cxx index a516c1a5e009b905b416a35b6c8804095db03a7e..f8ed112f2442a5d681d99e1e70417d08535b5dac 100644 --- a/src/GuiQt/BrainBrowserWindowToolBarChartAxes.cxx +++ b/src/GuiQt/BrainBrowserWindowToolBarChartAxes.cxx @@ -273,26 +273,26 @@ BrainBrowserWindowToolBarChartAxes::getCartesianChart() BrowserTabContent* browserTabContent = getTabContentFromSelectedTab(); if (browserTabContent != NULL) { - ModelChart* modelChart = browserTabContent->getDisplayedChartModel(); + ModelChart* modelChart = browserTabContent->getDisplayedChartOneModel(); if (modelChart != NULL) { const int32_t tabIndex = browserTabContent->getTabNumber(); - const ChartDataTypeEnum::Enum chartType = modelChart->getSelectedChartDataType(tabIndex); + const ChartOneDataTypeEnum::Enum chartType = modelChart->getSelectedChartOneDataType(tabIndex); switch (chartType) { - case ChartDataTypeEnum::CHART_DATA_TYPE_INVALID: + case ChartOneDataTypeEnum::CHART_DATA_TYPE_INVALID: break; - case ChartDataTypeEnum::CHART_DATA_TYPE_MATRIX_LAYER: + case ChartOneDataTypeEnum::CHART_DATA_TYPE_MATRIX_LAYER: break; - case ChartDataTypeEnum::CHART_DATA_TYPE_MATRIX_SERIES: + case ChartOneDataTypeEnum::CHART_DATA_TYPE_MATRIX_SERIES: break; - case ChartDataTypeEnum::CHART_DATA_TYPE_LINE_DATA_SERIES: + case ChartOneDataTypeEnum::CHART_DATA_TYPE_LINE_DATA_SERIES: cartesianChart = modelChart->getSelectedDataSeriesChartModel(tabIndex); //dynamic_cast(chart); break; - case ChartDataTypeEnum::CHART_DATA_TYPE_LINE_FREQUENCY_SERIES: + case ChartOneDataTypeEnum::CHART_DATA_TYPE_LINE_FREQUENCY_SERIES: cartesianChart = modelChart->getSelectedFrequencySeriesChartModel(tabIndex); break; - case ChartDataTypeEnum::CHART_DATA_TYPE_LINE_TIME_SERIES: + case ChartOneDataTypeEnum::CHART_DATA_TYPE_LINE_TIME_SERIES: cartesianChart = modelChart->getSelectedTimeSeriesChartModel(tabIndex); //dynamic_cast(chart); break; } diff --git a/src/GuiQt/BrainBrowserWindowToolBarChartTwoAttributes.cxx b/src/GuiQt/BrainBrowserWindowToolBarChartTwoAttributes.cxx new file mode 100644 index 0000000000000000000000000000000000000000..17054f71d269a6c51b15090832d2f65ac2fea626 --- /dev/null +++ b/src/GuiQt/BrainBrowserWindowToolBarChartTwoAttributes.cxx @@ -0,0 +1,371 @@ + +/*LICENSE_START*/ +/* + * Copyright (C) 2014 Washington University School of Medicine + * + * 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 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ +/*LICENSE_END*/ + +#include +#include +#include +#include +#include +#include +#include + +#define __BRAIN_BROWSER_WINDOW_TOOL_BAR_CHART_TWO_ATTRIBUTES_DECLARE__ +#include "BrainBrowserWindowToolBarChartTwoAttributes.h" +#undef __BRAIN_BROWSER_WINDOW_TOOL_BAR_CHART_TWO_ATTRIBUTES_DECLARE__ + +#include "BrowserTabContent.h" +#include "CaretAssert.h" +#include "CaretDataFile.h" +#include "CaretDataFileSelectionModel.h" +#include "EnumComboBoxTemplate.h" +#include "CaretMappableDataFile.h" +#include "ChartTwoMatrixDisplayProperties.h" +#include "EventChartTwoAttributesChanged.h" +#include "EventGraphicsUpdateAllWindows.h" +#include "EventManager.h" +#include "EventUserInterfaceUpdate.h" +#include "ModelChartTwo.h" +#include "WuQFactory.h" +#include "WuQWidgetObjectGroup.h" +#include "WuQtUtilities.h" + +using namespace caret; + + + +/** + * \class caret::BrainBrowserWindowToolBarChartTwoAttributes + * \brief Controls for chart attributes. + * \ingroup GuiQt + */ + +/** + * Constructor. + * + * @param parentToolBar + * The parent toolbar. + */ +BrainBrowserWindowToolBarChartTwoAttributes::BrainBrowserWindowToolBarChartTwoAttributes(BrainBrowserWindowToolBar* parentToolBar) +: BrainBrowserWindowToolBarComponent(parentToolBar) +{ + m_cartesianChartAttributesWidget = new CartesianChartTwoAttributesWidget(this); + + m_matrixChartTwoAttributesWidget = new MatrixChartTwoAttributesWidget(this); + + m_stackedWidget = new QStackedWidget(); + m_stackedWidget->addWidget(m_cartesianChartAttributesWidget); + m_stackedWidget->addWidget(m_matrixChartTwoAttributesWidget); + + QVBoxLayout* layout = new QVBoxLayout(this); + WuQtUtilities::setLayoutSpacingAndMargins(layout, 0, 0); + layout->addWidget(m_stackedWidget); + layout->addStretch(); +} + +/** + * Destructor. + */ +BrainBrowserWindowToolBarChartTwoAttributes::~BrainBrowserWindowToolBarChartTwoAttributes() +{ +} + +/** + * Update content of this tool bar component. + * + * @param browserTabContent + * Content of the browser tab. + */ +void +BrainBrowserWindowToolBarChartTwoAttributes::updateContent(BrowserTabContent* browserTabContent) +{ + if (browserTabContent != NULL) { + ModelChartTwo* modelChartTwo = browserTabContent->getDisplayedChartTwoModel(); + const int32_t tabIndex = browserTabContent->getTabNumber(); + if (modelChartTwo != NULL) { + switch (modelChartTwo->getSelectedChartTwoDataType(tabIndex)) { + case ChartTwoDataTypeEnum::CHART_DATA_TYPE_INVALID: + break; + case ChartTwoDataTypeEnum::CHART_DATA_TYPE_HISTOGRAM: + break; + case ChartTwoDataTypeEnum::CHART_DATA_TYPE_LINE_SERIES: + m_stackedWidget->setCurrentWidget(m_cartesianChartAttributesWidget); + m_cartesianChartAttributesWidget->updateContent(); + break; + case ChartTwoDataTypeEnum::CHART_DATA_TYPE_MATRIX: + m_stackedWidget->setCurrentWidget(m_matrixChartTwoAttributesWidget); + m_matrixChartTwoAttributesWidget->updateContent(); + break; + } + } + } +} + +/** + * @return Matrix chart interface in this widget. Returned value will + * be NULL if the chart (or no chart) is not a Matrix Chart. + */ +ChartTwoMatrixDisplayProperties* +BrainBrowserWindowToolBarChartTwoAttributes::getChartableTwoMatrixDisplayProperties() +{ + ChartTwoMatrixDisplayProperties* matrixDisplayProperties = NULL; + + BrowserTabContent* browserTabContent = getTabContentFromSelectedTab(); + if (browserTabContent != NULL) { + matrixDisplayProperties = browserTabContent->getChartTwoMatrixDisplayProperties(); + } + + return matrixDisplayProperties; +} + + +/** + * Update the graphics. + */ +void +BrainBrowserWindowToolBarChartTwoAttributes::updateGraphics() +{ + EventManager::get()->sendEvent(EventGraphicsUpdateAllWindows().getPointer()); +} + + + + +/* ===========================================================================*/ + +/** + * \class caret::CartesianChartTwoAttributesWidget + * \brief Controls for cartesian chart attributes. + * \ingroup GuiQt + */ + +/** + * Constructor. + * + * @param brainBrowserWindowToolBarChartAttributes + * The parent attributes widget. + */ +CartesianChartTwoAttributesWidget::CartesianChartTwoAttributesWidget(BrainBrowserWindowToolBarChartTwoAttributes* brainBrowserWindowToolBarChartAttributes) +: QWidget(brainBrowserWindowToolBarChartAttributes) +{ + m_brainBrowserWindowToolBarChartAttributes = brainBrowserWindowToolBarChartAttributes; + + QLabel* cartesianLineWidthLabel = new QLabel("Line width "); + m_cartesianLineWidthDoubleSpinBox = WuQFactory::newDoubleSpinBoxWithMinMaxStepDecimalsSignalDouble(0.0, + 10000.0, + 0.1, + 1, + this, + SLOT(cartesianLineWidthValueChanged(double))); + m_cartesianLineWidthDoubleSpinBox->setFixedWidth(65); + + QGridLayout* gridLayout = new QGridLayout(this); + WuQtUtilities::setLayoutSpacingAndMargins(gridLayout, 0, 0); + gridLayout->setColumnStretch(0, 0); + gridLayout->setColumnStretch(0, 100); + gridLayout->addWidget(cartesianLineWidthLabel, 0, 0); + gridLayout->addWidget(m_cartesianLineWidthDoubleSpinBox, 0, 1); + + this->setFixedSize(this->sizeHint()); +} + +/** + * Destructor. + */ +CartesianChartTwoAttributesWidget::~CartesianChartTwoAttributesWidget() +{ + +} + +/** + * Update the content of this widget. + */ +void +CartesianChartTwoAttributesWidget::updateContent() +{ +} + +/** + * Called when the cartesian line width is changed. + * + * @param value + * New value for line width. + */ +void +CartesianChartTwoAttributesWidget::cartesianLineWidthValueChanged(double /*value*/) +{ +} + + + + +/* ===========================================================================*/ + + + +/** + * \class caret::MatrixChartTwoAttributesWidget + * \brief Controls for matrix chart attributes. + * \ingroup GuiQt + */ + +/** + * Constructor. + * + * @param brainBrowserWindowToolBarChartAttributes + * The parent attributes widget. + */ +MatrixChartTwoAttributesWidget::MatrixChartTwoAttributesWidget(BrainBrowserWindowToolBarChartTwoAttributes* brainBrowserWindowToolBarChartAttributes) +: QWidget(brainBrowserWindowToolBarChartAttributes), +EventListenerInterface() +{ + m_brainBrowserWindowToolBarChartAttributes = brainBrowserWindowToolBarChartAttributes; + + QLabel* cellWidthLabel = new QLabel("Cell Width"); + const float minPercent = 1.0; + const float maxPercent = 100000.0; + m_cellWidthPercentageSpinBox = WuQFactory::newDoubleSpinBoxWithMinMaxStepDecimalsSignalDouble(minPercent, + maxPercent, + 1.0, + 1, + this, + SLOT(valueChanged())); + m_cellWidthPercentageSpinBox->setToolTip("Percentage of tab width filled with matrix"); + m_cellWidthPercentageSpinBox->setKeyboardTracking(false); + m_cellWidthPercentageSpinBox->setSuffix("%"); + + QLabel* cellHeightLabel = new QLabel("Cell Height"); + m_cellHeightPercentageSpinBox = WuQFactory::newDoubleSpinBoxWithMinMaxStepDecimalsSignalDouble(minPercent, + maxPercent, + 1.0, + 1, + this, + SLOT(valueChanged())); + m_cellHeightPercentageSpinBox->setToolTip("Percentage of tab height filled with matrix"); + m_cellHeightPercentageSpinBox->setKeyboardTracking(false); + m_cellHeightPercentageSpinBox->setSuffix("%"); + + WuQtUtilities::matchWidgetWidths(m_cellHeightPercentageSpinBox, + m_cellWidthPercentageSpinBox); + + m_highlightSelectionCheckBox = new QCheckBox("Highlight Selection"); + m_highlightSelectionCheckBox->setToolTip("Highlight selected row/column in the matrix"); + QObject::connect(m_highlightSelectionCheckBox, SIGNAL(clicked(bool)), + this, SLOT(valueChanged())); + + m_displayGridLinesCheckBox = new QCheckBox("Show Grid Outline"); + QObject::connect(m_displayGridLinesCheckBox, SIGNAL(clicked(bool)), + this, SLOT(valueChanged())); + m_displayGridLinesCheckBox->setToolTip("Outline cells in the matrix"); + + m_manualWidgetsGroup = new WuQWidgetObjectGroup(this); + m_manualWidgetsGroup->add(m_cellWidthPercentageSpinBox); + m_manualWidgetsGroup->add(m_cellHeightPercentageSpinBox); + m_manualWidgetsGroup->add(m_displayGridLinesCheckBox); + + const int32_t COLUMN_LABEL = 0; + const int32_t COLUMN_WIDGET = 1; + + QGridLayout* gridLayout = new QGridLayout(this); + WuQtUtilities::setLayoutSpacingAndMargins(gridLayout, 2, 2); + int32_t rowIndex = gridLayout->rowCount(); + gridLayout->addWidget(cellWidthLabel, rowIndex, COLUMN_LABEL); + gridLayout->addWidget(m_cellWidthPercentageSpinBox, rowIndex, COLUMN_WIDGET); + rowIndex++; + gridLayout->addWidget(cellHeightLabel, rowIndex, COLUMN_LABEL); + gridLayout->addWidget(m_cellHeightPercentageSpinBox, rowIndex, COLUMN_WIDGET); + rowIndex++; + gridLayout->addWidget(m_highlightSelectionCheckBox, rowIndex, COLUMN_LABEL, 1, 2, Qt::AlignLeft); + rowIndex++; + gridLayout->addWidget(m_displayGridLinesCheckBox, rowIndex, COLUMN_LABEL, 1, 2, Qt::AlignLeft); + rowIndex++; + + this->setFixedSize(this->sizeHint()); +} + +/** + * Destructor. + */ +MatrixChartTwoAttributesWidget::~MatrixChartTwoAttributesWidget() +{ + EventManager::get()->removeAllEventsFromListener(this); +} + +/** + * Receive an event. + * + * @param event + * The event. + */ +void +MatrixChartTwoAttributesWidget::receiveEvent(Event* /*event*/) +{ +} + + +/** + * Update the content of this widget. + */ +void +MatrixChartTwoAttributesWidget::updateContent() +{ + ChartTwoMatrixDisplayProperties* matrixDisplayProperties = m_brainBrowserWindowToolBarChartAttributes->getChartableTwoMatrixDisplayProperties(); + if (matrixDisplayProperties != NULL) { + m_cellWidthPercentageSpinBox->blockSignals(true); + m_cellWidthPercentageSpinBox->setValue(matrixDisplayProperties->getCellPercentageZoomWidth()); + m_cellWidthPercentageSpinBox->blockSignals(false); + + m_cellHeightPercentageSpinBox->blockSignals(true); + m_cellHeightPercentageSpinBox->setValue(matrixDisplayProperties->getCellPercentageZoomHeight()); + m_cellHeightPercentageSpinBox->blockSignals(false); + + m_highlightSelectionCheckBox->blockSignals(true); + m_highlightSelectionCheckBox->setChecked(matrixDisplayProperties->isSelectedRowColumnHighlighted()); + m_highlightSelectionCheckBox->blockSignals(false); + + m_displayGridLinesCheckBox->blockSignals(true); + m_displayGridLinesCheckBox->setChecked(matrixDisplayProperties->isGridLinesDisplayed()); + m_displayGridLinesCheckBox->blockSignals(false); + } +} + +/** + * Gets called when user changes value of a user-interface component. + */ +void +MatrixChartTwoAttributesWidget::valueChanged() +{ + ChartTwoMatrixDisplayProperties* matrixDisplayProperties = m_brainBrowserWindowToolBarChartAttributes->getChartableTwoMatrixDisplayProperties(); + if (matrixDisplayProperties != NULL) { + matrixDisplayProperties->setCellPercentageZoomWidth(m_cellWidthPercentageSpinBox->value()); + matrixDisplayProperties->setCellPercentageZoomHeight(m_cellHeightPercentageSpinBox->value()); + matrixDisplayProperties->setSelectedRowColumnHighlighted(m_highlightSelectionCheckBox->isChecked()); + matrixDisplayProperties->setGridLinesDisplayed(m_displayGridLinesCheckBox->isChecked()); + + BrowserTabContent* tabContent = m_brainBrowserWindowToolBarChartAttributes->getTabContentFromSelectedTab(); + CaretAssert(tabContent); + + tabContent->updateChartModelYokedBrowserTabs(); + m_brainBrowserWindowToolBarChartAttributes->updateGraphics(); + EventManager::get()->sendEvent(EventUserInterfaceUpdate().addToolBar().getPointer()); + } +} + + diff --git a/src/GuiQt/BrainBrowserWindowToolBarChartTwoAttributes.h b/src/GuiQt/BrainBrowserWindowToolBarChartTwoAttributes.h new file mode 100644 index 0000000000000000000000000000000000000000..3855496a6074d730de90b75121c54f6a0b2e80ba --- /dev/null +++ b/src/GuiQt/BrainBrowserWindowToolBarChartTwoAttributes.h @@ -0,0 +1,137 @@ +#ifndef __BRAIN_BROWSER_WINDOW_TOOL_BAR_CHART_TWO_ATTRIBUTES_H__ +#define __BRAIN_BROWSER_WINDOW_TOOL_BAR_CHART_TWO_ATTRIBUTES_H__ + +/*LICENSE_START*/ +/* + * Copyright (C) 2014 Washington University School of Medicine + * + * 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 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ +/*LICENSE_END*/ + + +#include "BrainBrowserWindowToolBarComponent.h" +#include "EventListenerInterface.h" + +class QCheckBox; +class QDoubleSpinBox; +class QStackedWidget; + +namespace caret { + + class CartesianChartTwoAttributesWidget; + class ChartTwoMatrixDisplayProperties; + class EnumComboBoxTemplate; + class MatrixChartTwoAttributesWidget; + class WuQWidgetObjectGroup; + + class BrainBrowserWindowToolBarChartTwoAttributes : public BrainBrowserWindowToolBarComponent { + Q_OBJECT + + public: + BrainBrowserWindowToolBarChartTwoAttributes(BrainBrowserWindowToolBar* parentToolBar); + + virtual ~BrainBrowserWindowToolBarChartTwoAttributes(); + + virtual void updateContent(BrowserTabContent* browserTabContent); + + // ADD_NEW_METHODS_HERE + + private: + BrainBrowserWindowToolBarChartTwoAttributes(const BrainBrowserWindowToolBarChartTwoAttributes&); + + BrainBrowserWindowToolBarChartTwoAttributes& operator=(const BrainBrowserWindowToolBarChartTwoAttributes&); + + //ChartModelCartesian* getCartesianChart(); + + ChartTwoMatrixDisplayProperties* getChartableTwoMatrixDisplayProperties(); + + void updateGraphics(); + + CartesianChartTwoAttributesWidget* m_cartesianChartAttributesWidget; + + MatrixChartTwoAttributesWidget* m_matrixChartTwoAttributesWidget; + + QStackedWidget* m_stackedWidget; + + // ADD_NEW_MEMBERS_HERE + + friend class CartesianChartTwoAttributesWidget; + friend class MatrixChartTwoAttributesWidget; + }; + + /* + * While this should be a private class in the class above + * Qt's meta-object compiler (moc) does not allow it to be. + */ + class CartesianChartTwoAttributesWidget : public QWidget { + Q_OBJECT + + public: + CartesianChartTwoAttributesWidget(BrainBrowserWindowToolBarChartTwoAttributes* brainBrowserWindowToolBarChartAttributes); + + ~CartesianChartTwoAttributesWidget(); + + virtual void updateContent(); + + private slots: + void cartesianLineWidthValueChanged(double value); + + private: + BrainBrowserWindowToolBarChartTwoAttributes* m_brainBrowserWindowToolBarChartAttributes; + + QDoubleSpinBox* m_cartesianLineWidthDoubleSpinBox; + + }; + + /* + * While this should be a private class in the class above + * Qt's meta-object compiler (moc) does not allow it to be. + */ + class MatrixChartTwoAttributesWidget : public QWidget, public EventListenerInterface { + Q_OBJECT + + public: + MatrixChartTwoAttributesWidget(BrainBrowserWindowToolBarChartTwoAttributes* brainBrowserWindowToolBarChartAttributes); + + ~MatrixChartTwoAttributesWidget(); + + virtual void receiveEvent(Event* event); + + virtual void updateContent(); + + private slots: + void valueChanged(); + + private: + BrainBrowserWindowToolBarChartTwoAttributes* m_brainBrowserWindowToolBarChartAttributes; + + QDoubleSpinBox* m_cellWidthPercentageSpinBox; + + QDoubleSpinBox* m_cellHeightPercentageSpinBox; + + QCheckBox* m_highlightSelectionCheckBox; + + QCheckBox* m_displayGridLinesCheckBox; + + WuQWidgetObjectGroup* m_manualWidgetsGroup; + + }; +#ifdef __BRAIN_BROWSER_WINDOW_TOOL_BAR_CHART_TWO_ATTRIBUTES_DECLARE__ + // +#endif // __BRAIN_BROWSER_WINDOW_TOOL_BAR_CHART_TWO_ATTRIBUTES_DECLARE__ + +} // namespace +#endif //__BRAIN_BROWSER_WINDOW_TOOL_BAR_CHART_TWO_ATTRIBUTES_H__ diff --git a/src/GuiQt/BrainBrowserWindowToolBarChartTwoAxes.cxx b/src/GuiQt/BrainBrowserWindowToolBarChartTwoAxes.cxx new file mode 100644 index 0000000000000000000000000000000000000000..24e5e9d2a12b713632a478575e754223a815fba3 --- /dev/null +++ b/src/GuiQt/BrainBrowserWindowToolBarChartTwoAxes.cxx @@ -0,0 +1,798 @@ + +/*LICENSE_START*/ +/* + * Copyright (C) 2014 Washington University School of Medicine + * + * 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 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ +/*LICENSE_END*/ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define __BRAIN_BROWSER_WINDOW_TOOL_BAR_CHART_TWO_AXES_DECLARE__ +#include "BrainBrowserWindowToolBarChartTwoAxes.h" +#undef __BRAIN_BROWSER_WINDOW_TOOL_BAR_CHART_TWO_AXES_DECLARE__ + +#include "AnnotationPercentSizeText.h" +#include "BrowserTabContent.h" +#include "CaretAssert.h" +#include "CaretDataFile.h" +#include "CaretDataFileSelectionModel.h" +#include "CaretMappableDataFile.h" +#include "ChartTwoCartesianAxis.h" +#include "ChartTwoOverlay.h" +#include "ChartTwoOverlaySet.h" +#include "ChartableTwoFileBaseChart.h" +#include "ChartableTwoFileDelegate.h" +#include "ChartableTwoFileHistogramChart.h" +#include "ChartableTwoFileLineSeriesChart.h" +#include "EnumComboBoxTemplate.h" +#include "EventBrowserWindowGraphicsRedrawn.h" +#include "EventChartTwoAttributesChanged.h" +#include "EventGraphicsUpdateAllWindows.h" +#include "EventManager.h" +#include "ModelChartTwo.h" +#include "WuQDataEntryDialog.h" +#include "WuQFactory.h" +#include "WuQDoubleSpinBox.h" +#include "WuQWidgetObjectGroup.h" +#include "WuQtUtilities.h" + +using namespace caret; + + + +/** + * \class caret::BrainBrowserWindowToolBarChartTwoAxes + * \brief Controls for chart attributes. + * \ingroup GuiQt + */ + +/** + * Constructor. + * + * @param parentToolBar + * The parent toolbar. + */ +BrainBrowserWindowToolBarChartTwoAxes::BrainBrowserWindowToolBarChartTwoAxes(BrainBrowserWindowToolBar* parentToolBar) +: BrainBrowserWindowToolBarComponent(parentToolBar) +{ + m_chartOverlaySet = NULL; + m_chartAxis = NULL; + + /* + * 'Show' checkboxes + */ + m_axisDisplayedByUserCheckBox = new QCheckBox("Axis"); + m_axisDisplayedByUserCheckBox->setToolTip("Show/hide the axis"); + QObject::connect(m_axisDisplayedByUserCheckBox, &QCheckBox::clicked, + this, &BrainBrowserWindowToolBarChartTwoAxes::valueChanged); + + m_showTickMarksCheckBox = new QCheckBox("Ticks"); + QObject::connect(m_showTickMarksCheckBox, &QCheckBox::clicked, + this, &BrainBrowserWindowToolBarChartTwoAxes::valueChangedBool); + m_showTickMarksCheckBox->setToolTip("Show ticks along the axis"); + + m_showLabelCheckBox = new QCheckBox("Label"); + QObject::connect(m_showLabelCheckBox, &QCheckBox::clicked, + this, &BrainBrowserWindowToolBarChartTwoAxes::valueChangedBool); + m_showLabelCheckBox->setToolTip("Show label on axis"); + + m_showNumericsCheckBox = new QCheckBox("Nums"); + QObject::connect(m_showNumericsCheckBox, &QCheckBox::clicked, + this, &BrainBrowserWindowToolBarChartTwoAxes::valueChangedBool); + m_showNumericsCheckBox->setToolTip("Show numeric scale values on axis"); + + m_rotateNumericsCheckBox = new QCheckBox("Rotate"); + QObject::connect(m_rotateNumericsCheckBox, &QCheckBox::clicked, + this, &BrainBrowserWindowToolBarChartTwoAxes::valueChangedBool); + m_rotateNumericsCheckBox->setToolTip("Rotate numeric scale values on axis"); + + /* + * Axes selection + */ + m_axisComboBox = new EnumComboBoxTemplate(this); + m_axisComboBox->setup(); + QObject::connect(m_axisComboBox, &EnumComboBoxTemplate::itemActivated, + this, &BrainBrowserWindowToolBarChartTwoAxes::axisChanged); + m_axisComboBox->getWidget()->setToolTip("Choose axis for editing"); + + /* + * Controls for layer selection and label editing + */ + m_axisLabelToolButton = new QToolButton(); + m_axisLabelToolButton->setText("Edit Label..."); + QObject::connect(m_axisLabelToolButton, &QToolButton::clicked, + this, &BrainBrowserWindowToolBarChartTwoAxes::axisLabelToolButtonClicked); + WuQtUtilities::setToolButtonStyleForQt5Mac(m_axisLabelToolButton); + m_axisLabelToolButton->setToolTip("Edit the axis name for the file in the selected overlay"); + + QLabel* axisLabelFromOverlayLabel = new QLabel("Label From File In"); + m_axisLabelFromOverlayComboBox = new QComboBox(); + m_axisLabelFromOverlayComboBox->setToolTip("Label for axis is from file in selected layer"); + QObject::connect(m_axisLabelFromOverlayComboBox, static_cast(&QComboBox::activated), + this, &BrainBrowserWindowToolBarChartTwoAxes::valueChangedInt); + + /* + * Range controls + */ + const AString rangeTooltip("Auto - Adjusts axis range to fit data with some\n" + " padding so that scale value are usually\n" + " whole numbers\n" + "Data - Axis range is limited to minimum and \n" + " maximum values of the data\n" + "User - Axis range is controlled by user"); + m_autoUserRangeComboBox = new EnumComboBoxTemplate(this); + m_autoUserRangeComboBox->setup(); + QObject::connect(m_autoUserRangeComboBox, &EnumComboBoxTemplate::itemActivated, + this, &BrainBrowserWindowToolBarChartTwoAxes::valueChanged); + m_autoUserRangeComboBox->getWidget()->setToolTip(rangeTooltip); + + m_userMinimumValueSpinBox = new WuQDoubleSpinBox(this); + m_userMinimumValueSpinBox->setDecimalsModeAuto(); + m_userMinimumValueSpinBox->setSingleStepPercentage(1.0); + QObject::connect(m_userMinimumValueSpinBox, static_cast(&WuQDoubleSpinBox::valueChanged), + this, &BrainBrowserWindowToolBarChartTwoAxes::axisMinimumValueChanged); + m_userMinimumValueSpinBox->setToolTip("Set user scaling axis minimum value"); + + m_userMaximumValueSpinBox = new WuQDoubleSpinBox(this); + m_userMaximumValueSpinBox->setDecimalsModeAuto(); + m_userMaximumValueSpinBox->setSingleStepPercentage(1.0); + QObject::connect(m_userMaximumValueSpinBox, static_cast(&WuQDoubleSpinBox::valueChanged), + this, &BrainBrowserWindowToolBarChartTwoAxes::axisMaximumValueChanged); + m_userMaximumValueSpinBox->setToolTip("Set user scaling axis maximum value"); + + /* + * Format controls + */ + m_userNumericFormatComboBox = new EnumComboBoxTemplate(this); + m_userNumericFormatComboBox->setup(); + QObject::connect(m_userNumericFormatComboBox, &EnumComboBoxTemplate::itemActivated, + this, &BrainBrowserWindowToolBarChartTwoAxes::valueChanged); + m_userNumericFormatComboBox->getWidget()->setToolTip("Choose format of axis scale numeric values"); + + m_userDigitsRightOfDecimalSpinBox = WuQFactory::newSpinBoxWithMinMaxStep(0, 10, 1); + QObject::connect(m_userDigitsRightOfDecimalSpinBox, static_cast(&QSpinBox::valueChanged), + this, &BrainBrowserWindowToolBarChartTwoAxes::valueChangedInt); + m_userDigitsRightOfDecimalSpinBox->setToolTip("Set digits right of decimal for\ndecimal or scientific format"); + + m_numericSubdivisionsModeComboBox = new EnumComboBoxTemplate(this); + m_numericSubdivisionsModeComboBox->setup(); + QObject::connect(m_numericSubdivisionsModeComboBox, &EnumComboBoxTemplate::itemActivated, + this, &BrainBrowserWindowToolBarChartTwoAxes::valueChanged); + m_numericSubdivisionsModeComboBox->getWidget()->setToolTip("Numeric subdivisions mode"); + + m_userSubdivisionsSpinBox = WuQFactory::newSpinBoxWithMinMaxStep(0, 100, 1); + QObject::connect(m_userSubdivisionsSpinBox, static_cast(&QSpinBox::valueChanged), + this, &BrainBrowserWindowToolBarChartTwoAxes::valueChangedInt); + m_userSubdivisionsSpinBox->setToolTip("Set subdivisions on the axis when Auto is not checked"); + + /* + * Size spin boxes + */ + m_labelSizeSpinBox = new WuQDoubleSpinBox(this); + m_labelSizeSpinBox->setRangePercentage(0.0, 100.0); + QObject::connect(m_labelSizeSpinBox, static_cast(&WuQDoubleSpinBox::valueChanged), + this, &BrainBrowserWindowToolBarChartTwoAxes::valueChangedDouble); + m_labelSizeSpinBox->setToolTip("Set height of label as percentage of tab height for selected axis"); + + m_numericsSizeSpinBox = new WuQDoubleSpinBox(this); + m_numericsSizeSpinBox->setRangePercentage(0.0, 100.0); + QObject::connect(m_numericsSizeSpinBox, static_cast(&WuQDoubleSpinBox::valueChanged), + this, &BrainBrowserWindowToolBarChartTwoAxes::valueChangedDouble); + m_numericsSizeSpinBox->setToolTip("Set height of numeric values as percentage of tab height for selected axis"); + + m_linesTicksSizeSpinBox = new WuQDoubleSpinBox(this); + m_linesTicksSizeSpinBox->setRangePercentage(0.0, 100.0); + QObject::connect(m_linesTicksSizeSpinBox, static_cast(&WuQDoubleSpinBox::valueChanged), + this, &BrainBrowserWindowToolBarChartTwoAxes::axisLineThicknessChanged); + m_linesTicksSizeSpinBox->setToolTip("Set thickness of axis lines as percentage of tab height for ALL axes"); + + m_paddingSizeSpinBox = new WuQDoubleSpinBox(this); + m_paddingSizeSpinBox->setRangePercentage(0.0, 100.0); + QObject::connect(m_paddingSizeSpinBox, static_cast(&WuQDoubleSpinBox::valueChanged), + this, &BrainBrowserWindowToolBarChartTwoAxes::valueChangedDouble); + m_paddingSizeSpinBox->setToolTip("Set padding (space between edge and labels) as percentage of tab height for selected axis"); + + /* + * Group widgets for blocking signals + */ + m_widgetGroup = new WuQWidgetObjectGroup(this); + m_widgetGroup->add(m_axisComboBox); + m_widgetGroup->add(m_axisDisplayedByUserCheckBox); + m_widgetGroup->add(m_axisLabelToolButton); + m_widgetGroup->add(m_autoUserRangeComboBox->getWidget()); + m_widgetGroup->add(m_userMinimumValueSpinBox); + m_widgetGroup->add(m_userMaximumValueSpinBox); + m_widgetGroup->add(m_showTickMarksCheckBox); + m_widgetGroup->add(m_showLabelCheckBox); + m_widgetGroup->add(m_showNumericsCheckBox); + m_widgetGroup->add(m_rotateNumericsCheckBox); + m_widgetGroup->add(m_axisLabelFromOverlayComboBox); + m_widgetGroup->add(m_userNumericFormatComboBox->getWidget()); + m_widgetGroup->add(m_userDigitsRightOfDecimalSpinBox); + m_widgetGroup->add(m_numericSubdivisionsModeComboBox->getWidget()); + m_widgetGroup->add(m_userSubdivisionsSpinBox); + m_widgetGroup->add(m_labelSizeSpinBox); + m_widgetGroup->add(m_numericsSizeSpinBox); + m_widgetGroup->add(m_linesTicksSizeSpinBox); + m_widgetGroup->add(m_paddingSizeSpinBox); + + /* + * Size layout + */ + QWidget* sizesWidget = new QWidget(); + QGridLayout* sizesLayout = new QGridLayout(sizesWidget); + WuQtUtilities::setLayoutSpacingAndMargins(sizesLayout, 2, 0); + int32_t sizesRow = 0; + sizesLayout->addWidget(new QLabel("Label"), sizesRow, 0); + sizesLayout->addWidget(m_labelSizeSpinBox->getWidget(), sizesRow, 1); + sizesRow++; + sizesLayout->addWidget(new QLabel("Scale"), sizesRow, 0); + sizesLayout->addWidget(m_numericsSizeSpinBox->getWidget(), sizesRow, 1); + sizesRow++; + sizesLayout->addWidget(new QLabel("Pad"), sizesRow, 0); + sizesLayout->addWidget(m_paddingSizeSpinBox->getWidget(), sizesRow, 1); + sizesRow++; + sizesLayout->addWidget(new QLabel("Lines"), sizesRow, 0); + sizesLayout->addWidget(m_linesTicksSizeSpinBox->getWidget(), sizesRow, 1); + sizesRow++; + + /* + * Show widgets layout + */ + const bool displayLabelShowAtTopFlag = false; + QWidget* showWidget = new QWidget(); + QGridLayout* showLayout = new QGridLayout(showWidget); + WuQtUtilities::setLayoutSpacingAndMargins(showLayout, 1, 0); + int32_t axisRow = 0; + if (displayLabelShowAtTopFlag) { + showLayout->addWidget(new QLabel("Show"), axisRow, 0, Qt::AlignHCenter); + axisRow++; + } + showLayout->addWidget(m_axisDisplayedByUserCheckBox, axisRow, 0); + axisRow++; + showLayout->addWidget(m_showLabelCheckBox, axisRow, 0); + axisRow++; + showLayout->addWidget(m_showNumericsCheckBox, axisRow, 0); + axisRow++; + showLayout->addWidget(m_rotateNumericsCheckBox, axisRow, 0); + axisRow++; + showLayout->addWidget(m_showTickMarksCheckBox, axisRow, 0); + axisRow++; + showWidget->setSizePolicy(showWidget->sizePolicy().horizontalPolicy(), + QSizePolicy::Fixed); + + /* + * Range widgets layout + */ + QWidget* rangeWidget = new QWidget(); + QGridLayout* rangeLayout = new QGridLayout(rangeWidget); + WuQtUtilities::setLayoutSpacingAndMargins(rangeLayout, 3, 0); + int rangeRow = 0; + rangeLayout->addWidget(new QLabel("Range"), rangeRow, 0); + m_autoUserRangeComboBox->getComboBox()->setSizeAdjustPolicy(QComboBox::AdjustToContentsOnFirstShow); + rangeLayout->addWidget(m_autoUserRangeComboBox->getWidget(), rangeRow, 1); + rangeRow++; + m_userMinimumValueSpinBox->setFixedWidth(90); + m_userMaximumValueSpinBox->setFixedWidth(90); + rangeLayout->addWidget(new QLabel("Max"), rangeRow, 0); + rangeLayout->addWidget(m_userMaximumValueSpinBox->getWidget(), rangeRow, 1); + rangeRow++; + rangeLayout->addWidget(new QLabel("Min"), rangeRow, 0); + rangeLayout->addWidget(m_userMinimumValueSpinBox->getWidget(), rangeRow, 1); + + /* + * Numerics widgets layout + */ + QWidget* numericsWidget = new QWidget(); + QGridLayout* numericsLayout = new QGridLayout(numericsWidget); + WuQtUtilities::setLayoutSpacingAndMargins(numericsLayout, 3, 0); + int numericsRow = 0; + numericsLayout->addWidget(new QLabel("Format"), numericsRow, 0); + numericsLayout->addWidget(m_userNumericFormatComboBox->getWidget(), numericsRow, 1); + numericsRow++; + numericsLayout->addWidget(new QLabel("Decimals"), numericsRow, 0); + numericsLayout->addWidget(m_userDigitsRightOfDecimalSpinBox, numericsRow, 1); + numericsRow++; + numericsLayout->addWidget(new QLabel("Subdivisions"), numericsRow, 0); + numericsLayout->addWidget(m_numericSubdivisionsModeComboBox->getWidget(), numericsRow, 1); + numericsRow++; + numericsLayout->addWidget(m_userSubdivisionsSpinBox, numericsRow, 1); + + /* + * Top layout + */ + QHBoxLayout* topLayout = new QHBoxLayout(); + WuQtUtilities::setLayoutSpacingAndMargins(topLayout, 3, 0); + topLayout->addWidget(new QLabel("Edit Axis ")); + topLayout->addWidget(m_axisComboBox->getWidget()); + topLayout->addSpacing(10); + topLayout->addStretch(); + topLayout->addWidget(axisLabelFromOverlayLabel); + topLayout->addWidget(m_axisLabelFromOverlayComboBox); + topLayout->addSpacing(10); + topLayout->addWidget(m_axisLabelToolButton); + + /* + * Grid layout containing layouts + */ + QGridLayout* gridLayout = new QGridLayout(); + gridLayout->setHorizontalSpacing(4); + gridLayout->setVerticalSpacing(1); + gridLayout->setContentsMargins(0, 0, 0, 0); +// WuQtUtilities::setLayoutSpacingAndMargins(gridLayout, 2, 0); + gridLayout->addLayout(topLayout, 0, 0, 1, 7); + gridLayout->addWidget(showWidget, 1, 0, Qt::AlignTop); + gridLayout->addWidget(WuQtUtilities::createVerticalLineWidget(), 1, 1); + gridLayout->addWidget(sizesWidget, 1, 2, Qt::AlignTop); + gridLayout->addWidget(WuQtUtilities::createVerticalLineWidget(), 1, 3); + gridLayout->addWidget(rangeWidget, 1, 4, Qt::AlignTop); + gridLayout->addWidget(WuQtUtilities::createVerticalLineWidget(), 1, 5); + gridLayout->addWidget(numericsWidget, 1, 6, Qt::AlignTop); + + QVBoxLayout* layout = new QVBoxLayout(this); + WuQtUtilities::setLayoutSpacingAndMargins(layout, 0, 0); //2); + layout->addLayout(gridLayout); + layout->addStretch(); + + EventManager::get()->addEventListener(this, + EventTypeEnum::EVENT_BROWSER_WINDOW_GRAPHICS_HAVE_BEEN_REDRAWN); +} + +/** + * Destructor. + */ +BrainBrowserWindowToolBarChartTwoAxes::~BrainBrowserWindowToolBarChartTwoAxes() +{ + EventManager::get()->removeEventFromListener(this, + EventTypeEnum::EVENT_BROWSER_WINDOW_GRAPHICS_HAVE_BEEN_REDRAWN); +} + +/** + * Receive an event. + * + * @param event + * The event. + */ +void +BrainBrowserWindowToolBarChartTwoAxes::receiveEvent(Event* event) +{ + if (event->getEventType() == EventTypeEnum::EVENT_BROWSER_WINDOW_GRAPHICS_HAVE_BEEN_REDRAWN) { + CaretAssert(dynamic_cast(event)); + updateContent(getTabContentFromSelectedTab()); + } + else { + BrainBrowserWindowToolBarComponent::receiveEvent(event); + } +} + +/** + * Update content of this tool bar component. + * + * @param browserTabContent + * Content of the browser tab. + */ +void +BrainBrowserWindowToolBarChartTwoAxes::updateContent(BrowserTabContent* browserTabContent) +{ + if (browserTabContent == NULL) { + setEnabled(false); + return; + } + + updateControls(browserTabContent); +} + +/** + * Get the selection data. + * + * @param browserTabContent + * The tab content. + * @param chartOverlaySetOut + * The chart overlay set (may be NULL) + * @param validAxesLocationsOut + * The valid axes locations. + * @param selectedAxisOut + * Output with selected axis (may be NULL) + */ +void +BrainBrowserWindowToolBarChartTwoAxes::getSelectionData(BrowserTabContent* browserTabContent, + ChartTwoOverlaySet* &chartOverlaySetOut, + std::vector& validAxesLocationsOut, + ChartTwoCartesianAxis* &selectedAxisOut) const +{ + chartOverlaySetOut = NULL; + validAxesLocationsOut.clear(); + selectedAxisOut = NULL; + + if (browserTabContent == NULL) { + return; + } + + const ChartAxisLocationEnum::Enum lastSelectedAxis = getSelectedAxisLocation(); + + if (browserTabContent != NULL) { + ModelChartTwo* modelChartTwo = browserTabContent->getDisplayedChartTwoModel(); + const int32_t tabIndex = browserTabContent->getTabNumber(); + if (modelChartTwo != NULL) { + switch (modelChartTwo->getSelectedChartTwoDataType(tabIndex)) { + case ChartTwoDataTypeEnum::CHART_DATA_TYPE_INVALID: + break; + case ChartTwoDataTypeEnum::CHART_DATA_TYPE_HISTOGRAM: + chartOverlaySetOut = modelChartTwo->getChartTwoOverlaySet(tabIndex); + break; + case ChartTwoDataTypeEnum::CHART_DATA_TYPE_LINE_SERIES: + chartOverlaySetOut = modelChartTwo->getChartTwoOverlaySet(tabIndex); + break; + case ChartTwoDataTypeEnum::CHART_DATA_TYPE_MATRIX: + break; + } + + if (chartOverlaySetOut != NULL) { + int32_t defaultAxisIndex = -1; + + std::vector axes; + chartOverlaySetOut->getDisplayedChartAxes(axes); + const int32_t numAxes = static_cast(axes.size()); + for (int32_t i = 0; i < numAxes; i++) { + const ChartAxisLocationEnum::Enum axisLocation = axes[i]->getAxisLocation(); + if (lastSelectedAxis == axisLocation) { + defaultAxisIndex = i; + } + validAxesLocationsOut.push_back(axisLocation); + } + CaretAssert(validAxesLocationsOut.size() == axes.size()); + + if (defaultAxisIndex < 0) { + /* + * If selected axis not found, switch to opposite axis + * User may have switched vertical axis from left to right + */ + ChartAxisLocationEnum::Enum oppositeAxis = ChartAxisLocationEnum::getOppositeAxis(lastSelectedAxis); + for (int32_t i = 0; i < numAxes; i++) { + if (oppositeAxis == axes[i]->getAxisLocation()) { + defaultAxisIndex = i; + break; + } + } + } + + if ( ! axes.empty()) { + if (defaultAxisIndex < 0) { + defaultAxisIndex = 0; + } + CaretAssertVectorIndex(axes, defaultAxisIndex); + selectedAxisOut = axes[defaultAxisIndex]; + } + } + } + } +} + +/** + * Called when axis is changed. + */ +void +BrainBrowserWindowToolBarChartTwoAxes::axisChanged() +{ + updateContent(getTabContentFromSelectedTab()); +} + +/** + * @return The selected axes location (will return left even if no valid selection). + */ +ChartAxisLocationEnum::Enum +BrainBrowserWindowToolBarChartTwoAxes::getSelectedAxisLocation() const +{ + ChartAxisLocationEnum::Enum axis = ChartAxisLocationEnum::CHART_AXIS_LOCATION_LEFT; + + if (m_axisComboBox->getComboBox()->count() > 0) { + axis = m_axisComboBox->getSelectedItem(); + } + + return axis; +} + + +void +BrainBrowserWindowToolBarChartTwoAxes::updateControls(BrowserTabContent* browserTabContent) +{ + m_chartOverlaySet = NULL; + std::vector validAxesLocations; + ChartTwoCartesianAxis* selectedAxis = NULL; + + getSelectionData(browserTabContent, + m_chartOverlaySet, + validAxesLocations, + selectedAxis); + + m_widgetGroup->blockAllSignals(true); + + m_chartAxis = selectedAxis; + if ((m_chartOverlaySet != NULL) + && (m_chartAxis != NULL)) { + m_axisComboBox->setupWithItems(validAxesLocations); + m_axisComboBox->setSelectedItem(m_chartAxis->getAxisLocation()); + + m_axisDisplayedByUserCheckBox->setChecked(m_chartAxis->isDisplayedByUser()); + m_autoUserRangeComboBox->setSelectedItem(m_chartAxis->getScaleRangeMode()); + float rangeMin(0.0f), rangeMax(0.0f); + m_chartAxis->getDataRange(rangeMin, rangeMax); + m_userMinimumValueSpinBox->setRangeExceedable(rangeMin, rangeMax); + m_userMinimumValueSpinBox->setValue(m_chartAxis->getUserScaleMinimumValue()); + m_userMaximumValueSpinBox->setRangeExceedable(rangeMin, rangeMax); + m_userMaximumValueSpinBox->setValue(m_chartAxis->getUserScaleMaximumValue()); + m_showTickMarksCheckBox->setChecked(m_chartAxis->isShowTickmarks()); + m_showLabelCheckBox->setChecked(m_chartAxis->isShowLabel()); + m_showNumericsCheckBox->setChecked(m_chartAxis->isNumericsTextDisplayed()); + m_rotateNumericsCheckBox->setChecked(m_chartAxis->isNumericsTextRotated()); + const NumericFormatModeEnum::Enum numericFormat = m_chartAxis->getUserNumericFormat(); + m_userNumericFormatComboBox->setSelectedItem(numericFormat); + m_userDigitsRightOfDecimalSpinBox->setValue(m_chartAxis->getUserDigitsRightOfDecimal()); + m_userDigitsRightOfDecimalSpinBox->setEnabled(numericFormat != NumericFormatModeEnum::AUTO); + m_numericSubdivisionsModeComboBox->setSelectedItem(m_chartAxis->getNumericSubdivsionsMode()); + m_userSubdivisionsSpinBox->setValue(m_chartAxis->getUserNumberOfSubdivisions()); + m_userSubdivisionsSpinBox->setEnabled( m_chartAxis->getNumericSubdivsionsMode() == ChartTwoNumericSubdivisionsModeEnum::USER); + + m_labelSizeSpinBox->setValue(m_chartAxis->getLabelTextSize()); + m_numericsSizeSpinBox->setValue(m_chartAxis->getNumericsTextSize()); + m_linesTicksSizeSpinBox->setValue(m_chartOverlaySet->getAxisLineThickness()); + m_paddingSizeSpinBox->setValue(m_chartAxis->getPaddingSize()); + + const int32_t overlayCount = m_chartOverlaySet->getNumberOfDisplayedOverlays(); + int32_t selectedOverlayIndex = m_chartAxis->getLabelOverlayIndex(overlayCount); + const int32_t comboBoxCount = m_axisLabelFromOverlayComboBox->count(); + if (overlayCount < comboBoxCount) { + m_axisLabelFromOverlayComboBox->setMaxCount(overlayCount); + } + else if (overlayCount > comboBoxCount) { + for (int32_t j = comboBoxCount; j < overlayCount; j++) { + m_axisLabelFromOverlayComboBox->addItem("Layer " + AString::number(j + 1)); + } + } + + if ((selectedOverlayIndex >= 0) + && (selectedOverlayIndex < m_axisLabelFromOverlayComboBox->count())) { + m_axisLabelFromOverlayComboBox->setCurrentIndex(selectedOverlayIndex); + } + setEnabled(true); + } + else { + setEnabled(false); + } + + m_widgetGroup->blockAllSignals(false); +} + +/** + * Called when the axis line thickness changes. + */ +void +BrainBrowserWindowToolBarChartTwoAxes::axisLineThicknessChanged(double) +{ + if (m_chartOverlaySet != NULL) { + m_chartOverlaySet->setAxisLineThickness(m_linesTicksSizeSpinBox->value()); + + const BrowserTabContent* tabContent = getTabContentFromSelectedTab(); + CaretAssert(tabContent); + + const YokingGroupEnum::Enum yokingGroup = tabContent->getChartModelYokingGroup(); + if (yokingGroup != YokingGroupEnum::YOKING_GROUP_OFF) { + const ModelChartTwo* modelChartTwo = tabContent->getDisplayedChartTwoModel(); + CaretAssert(modelChartTwo); + const int32_t tabIndex = tabContent->getTabNumber(); + EventChartTwoAttributesChanged attributesEvent; + attributesEvent.setLineThicknessChanged(yokingGroup, + modelChartTwo->getSelectedChartTwoDataType(tabIndex), + m_linesTicksSizeSpinBox->value()); + EventManager::get()->sendEvent(attributesEvent.getPointer()); + } + } + + updateGraphics(); + + updateContent(getTabContentFromSelectedTab()); +} + + +/** + * Called when a widget is changed by the user. + */ +void +BrainBrowserWindowToolBarChartTwoAxes::valueChanged() +{ + CaretAssert(m_chartAxis); + if (m_chartAxis != NULL) { + m_chartAxis->setLabelOverlayIndex(m_axisLabelFromOverlayComboBox->currentIndex()); + m_chartAxis->setDisplayedByUser(m_axisDisplayedByUserCheckBox->isChecked()); + m_chartAxis->setScaleRangeMode(m_autoUserRangeComboBox->getSelectedItem()); + m_chartAxis->setUserScaleMinimumValue(m_userMinimumValueSpinBox->value()); + m_chartAxis->setUserScaleMaximumValue(m_userMaximumValueSpinBox->value()); + m_chartAxis->setShowTickmarks(m_showTickMarksCheckBox->isChecked()); + m_chartAxis->setShowLabel(m_showLabelCheckBox->isChecked()); + m_chartAxis->setNumericsTextDisplayed(m_showNumericsCheckBox->isChecked()); + m_chartAxis->setNumericsTextRotated(m_rotateNumericsCheckBox->isChecked()); + m_chartAxis->setUserNumericFormat(m_userNumericFormatComboBox->getSelectedItem()); + m_chartAxis->setUserDigitsRightOfDecimal(m_userDigitsRightOfDecimalSpinBox->value()); + m_chartAxis->setNumericSubdivsionsMode(m_numericSubdivisionsModeComboBox->getSelectedItem()); + m_chartAxis->setUserNumberOfSubdivisions(m_userSubdivisionsSpinBox->value()); + + m_chartAxis->setLabelTextSize(m_labelSizeSpinBox->value()); + m_chartAxis->setNumericsTextSize(m_numericsSizeSpinBox->value()); + m_chartAxis->setPaddingSize(m_paddingSizeSpinBox->value()); + + const BrowserTabContent* tabContent = getTabContentFromSelectedTab(); + CaretAssert(tabContent); + + const YokingGroupEnum::Enum yokingGroup = tabContent->getChartModelYokingGroup(); + if (yokingGroup != YokingGroupEnum::YOKING_GROUP_OFF) { + const ModelChartTwo* modelChartTwo = tabContent->getDisplayedChartTwoModel(); + CaretAssert(modelChartTwo); + const int32_t tabIndex = tabContent->getTabNumber(); + EventChartTwoAttributesChanged attributesEvent; + attributesEvent.setCartesianAxisChanged(yokingGroup, + modelChartTwo->getSelectedChartTwoDataType(tabIndex), + m_chartAxis); + EventManager::get()->sendEvent(attributesEvent.getPointer()); + } + } + + updateGraphics(); + + updateContent(getTabContentFromSelectedTab()); +} + +/** + * Update the graphics. + */ +void +BrainBrowserWindowToolBarChartTwoAxes::updateGraphics() +{ + EventManager::get()->sendEvent(EventGraphicsUpdateAllWindows().getPointer()); +} + +/** + * Called when the minimum value is changed. + * + * @param minimumValue + * New minimum value. + */ +void +BrainBrowserWindowToolBarChartTwoAxes::axisMinimumValueChanged(double minimumValue) +{ + if (m_chartAxis != NULL) { + /* + * If the minimum or maximum value is modified by user, + * ensure Auto/User Range selection is USER + */ + m_autoUserRangeComboBox->getWidget()->blockSignals(true); + m_autoUserRangeComboBox->setSelectedItem(ChartTwoAxisScaleRangeModeEnum::USER); + m_autoUserRangeComboBox->getWidget()->blockSignals(false); + + /* + * Ensure maximum value is always greater than or equal to minimum + */ + if (minimumValue > m_userMaximumValueSpinBox->value()) { + m_userMaximumValueSpinBox->getWidget()->blockSignals(true); + m_userMaximumValueSpinBox->setValue(minimumValue); + m_userMaximumValueSpinBox->getWidget()->blockSignals(false); + } + + valueChanged(); + } +} + +/** + * Called when the maximum value is changed. + * + * @param maximumValue + * New maximum value. + */ +void +BrainBrowserWindowToolBarChartTwoAxes::axisMaximumValueChanged(double maximumValue) +{ + if (m_chartAxis != NULL) { + /* + * If the minimum or maximum value is modified by user, + * ensure Auto/User Range selection is USER + */ + m_autoUserRangeComboBox->getWidget()->blockSignals(true); + m_autoUserRangeComboBox->setSelectedItem(ChartTwoAxisScaleRangeModeEnum::USER); + m_autoUserRangeComboBox->getWidget()->blockSignals(false); + + /* + * Ensure minimum value is always less than or equal to maximum + */ + if (maximumValue < m_userMinimumValueSpinBox->value()) { + m_userMinimumValueSpinBox->getWidget()->blockSignals(true); + m_userMinimumValueSpinBox->setValue(maximumValue); + m_userMinimumValueSpinBox->getWidget()->blockSignals(false); + } + + valueChanged(); + } +} + +/** + * Called when a widget is changed by a slot using a bool parameter. + * Parameters must match when using function pointers. + */ +void +BrainBrowserWindowToolBarChartTwoAxes::valueChangedBool(bool) +{ + valueChanged(); +} + +/** + * Called when a widget is changed by a slot using a double parameter. + * Parameters must match when using function pointers. + */ +void +BrainBrowserWindowToolBarChartTwoAxes::valueChangedDouble(double) +{ + valueChanged(); +} + +/** + * Called when a widget is changed by a slot using a int parameter. + * Parameters must match when using function pointers. + */ +void +BrainBrowserWindowToolBarChartTwoAxes::valueChangedInt(int) +{ + valueChanged(); +} + +/** + * Called when name toolbutton is clicked to change axis label. + */ +void +BrainBrowserWindowToolBarChartTwoAxes::axisLabelToolButtonClicked(bool) +{ + ChartTwoOverlaySet* chartOverlaySet = NULL; + std::vector validAxesLocations; + ChartTwoCartesianAxis* selectedAxis = NULL; + + getSelectionData(getTabContentFromSelectedTab(), + chartOverlaySet, + validAxesLocations, + selectedAxis); + + if ((chartOverlaySet != NULL) + && (selectedAxis != NULL)) { + WuQDataEntryDialog newNameDialog("Axis Label", + m_axisLabelToolButton); + QLineEdit* lineEdit = newNameDialog.addLineEditWidget("Label"); + lineEdit->setText(chartOverlaySet->getAxisLabel(selectedAxis)); + if (newNameDialog.exec() == WuQDataEntryDialog::Accepted) { + const AString name = lineEdit->text().trimmed(); + chartOverlaySet->setAxisLabel(selectedAxis, name); + valueChanged(); + } + } +} + + + + diff --git a/src/GuiQt/BrainBrowserWindowToolBarChartTwoAxes.h b/src/GuiQt/BrainBrowserWindowToolBarChartTwoAxes.h new file mode 100644 index 0000000000000000000000000000000000000000..df8db9b20ebc24f9d1adedb25eb8185c3c1570e5 --- /dev/null +++ b/src/GuiQt/BrainBrowserWindowToolBarChartTwoAxes.h @@ -0,0 +1,147 @@ +#ifndef __BRAIN_BROWSER_WINDOW_TOOL_BAR_CHART_TWO_AXES_H__ +#define __BRAIN_BROWSER_WINDOW_TOOL_BAR_CHART_TWO_AXES_H__ + +/*LICENSE_START*/ +/* + * Copyright (C) 2014 Washington University School of Medicine + * + * 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 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ +/*LICENSE_END*/ + + +#include "BrainBrowserWindowToolBarComponent.h" +#include "ChartAxisLocationEnum.h" + +class QCheckBox; +class QComboBox; +class QDoubleSpinBox; +class QLabel; +class QSpinBox; +class QToolButton; + +namespace caret { + + class AnnotationPercentSizeText; + class ChartTwoCartesianAxis; + class ChartTwoCartesianAxisWidget; + class ChartTwoOverlaySet; + class EnumComboBoxTemplate; + class WuQDoubleSpinBox; + class WuQWidgetObjectGroup; + + class BrainBrowserWindowToolBarChartTwoAxes : public BrainBrowserWindowToolBarComponent { + Q_OBJECT + + public: + BrainBrowserWindowToolBarChartTwoAxes(BrainBrowserWindowToolBar* parentToolBar); + + virtual ~BrainBrowserWindowToolBarChartTwoAxes(); + + virtual void updateContent(BrowserTabContent* browserTabContent); + + virtual void receiveEvent(Event* event); + + // ADD_NEW_METHODS_HERE + + private slots: + void axisChanged(); + + void valueChanged(); + + void valueChangedBool(bool); + + void valueChangedDouble(double); + + void valueChangedInt(int); + + void axisMinimumValueChanged(double); + + void axisMaximumValueChanged(double); + + void axisLabelToolButtonClicked(bool); + + void axisLineThicknessChanged(double); + + private: + BrainBrowserWindowToolBarChartTwoAxes(const BrainBrowserWindowToolBarChartTwoAxes&); + + BrainBrowserWindowToolBarChartTwoAxes& operator=(const BrainBrowserWindowToolBarChartTwoAxes&); + + ChartAxisLocationEnum::Enum getSelectedAxisLocation() const; + + void getSelectionData(BrowserTabContent* browserTabContent, + ChartTwoOverlaySet* &chartOverlaySetOut, + std::vector& validAxesLocationsOut, + ChartTwoCartesianAxis* &selectedAxisOut) const; + + void updateControls(BrowserTabContent* browserTabContent); + + void updateGraphics(); + + // ADD_NEW_MEMBERS_HERE + + QCheckBox* m_axisDisplayedByUserCheckBox; + + EnumComboBoxTemplate* m_axisComboBox; + + QToolButton* m_axisLabelToolButton; + + QComboBox* m_axisLabelFromOverlayComboBox; + + EnumComboBoxTemplate* m_autoUserRangeComboBox; + + WuQDoubleSpinBox* m_userMinimumValueSpinBox; + + WuQDoubleSpinBox* m_userMaximumValueSpinBox; + + QCheckBox* m_showTickMarksCheckBox; + + QCheckBox* m_showLabelCheckBox; + + QCheckBox* m_showNumericsCheckBox; + + QCheckBox* m_rotateNumericsCheckBox; + + EnumComboBoxTemplate* m_userNumericFormatComboBox; + + QSpinBox* m_userDigitsRightOfDecimalSpinBox; + + EnumComboBoxTemplate* m_numericSubdivisionsModeComboBox; + + QSpinBox* m_userSubdivisionsSpinBox; + + WuQDoubleSpinBox* m_labelSizeSpinBox; + + WuQDoubleSpinBox* m_numericsSizeSpinBox; + + WuQDoubleSpinBox* m_linesTicksSizeSpinBox; + + WuQDoubleSpinBox* m_paddingSizeSpinBox; + + ChartTwoOverlaySet* m_chartOverlaySet; + + ChartTwoCartesianAxis* m_chartAxis; + + WuQWidgetObjectGroup* m_widgetGroup; + + }; + +#ifdef __BRAIN_BROWSER_WINDOW_TOOL_BAR_CHART_TWO_AXES_DECLARE__ + // +#endif // __BRAIN_BROWSER_WINDOW_TOOL_BAR_CHART_TWO_AXES_DECLARE__ + +} // namespace +#endif //__BRAIN_BROWSER_WINDOW_TOOL_BAR_CHART_TWO_AXES_H__ diff --git a/src/GuiQt/BrainBrowserWindowToolBarChartTwoOrientation.cxx b/src/GuiQt/BrainBrowserWindowToolBarChartTwoOrientation.cxx new file mode 100644 index 0000000000000000000000000000000000000000..ea4313238c88edccb2b250b8b077f985b66dbcc5 --- /dev/null +++ b/src/GuiQt/BrainBrowserWindowToolBarChartTwoOrientation.cxx @@ -0,0 +1,127 @@ + +/*LICENSE_START*/ +/* + * Copyright (C) 2017 Washington University School of Medicine + * + * 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 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ +/*LICENSE_END*/ + +#define __BRAIN_BROWSER_WINDOW_TOOL_BAR_CHART_TWO_ORIENTATION_DECLARE__ +#include "BrainBrowserWindowToolBarChartTwoOrientation.h" +#undef __BRAIN_BROWSER_WINDOW_TOOL_BAR_CHART_TWO_ORIENTATION_DECLARE__ + +#include +#include + +#include "BrainBrowserWindowToolBar.h" +#include "BrowserTabContent.h" +#include "CaretAssert.h" +#include "CaretPreferences.h" +#include "EventManager.h" +#include "ModelChartTwo.h" +#include "SessionManager.h" +#include "WuQtUtilities.h" + +using namespace caret; + + + +/** + * \class caret::BrainBrowserWindowToolBarChartTwoOrientation + * \brief Toolbar component for chart orientation. + * \ingroup GuiQt + */ + +/** + * Constructor. + */ +BrainBrowserWindowToolBarChartTwoOrientation::BrainBrowserWindowToolBarChartTwoOrientation(BrainBrowserWindowToolBar* parentToolBar) +: BrainBrowserWindowToolBarComponent(parentToolBar) +{ + m_orientationResetToolButtonAction = WuQtUtilities::createAction("Reset", + "Reset the view to remove any panning or zooming", + this, + this, + SLOT(orientationResetToolButtonTriggered(bool))); + + const QString customToolTip = ("Pressing the \"Custom\" button displays a dialog for creating and editing orientations.\n" + "Note that custom orientations are stored in your Workbench's preferences and thus\n" + "will be availble in any concurrent or future instances of Workbench."); + m_customViewAction = WuQtUtilities::createAction("Custom", + customToolTip, + this, + this, + SLOT(customViewActionTriggered())); + + + QToolButton* orientationResetToolButton = new QToolButton(); + orientationResetToolButton->setDefaultAction(m_orientationResetToolButtonAction); + WuQtUtilities::setToolButtonStyleForQt5Mac(orientationResetToolButton); + + m_orientationCustomViewSelectToolButton = new QToolButton(); + m_orientationCustomViewSelectToolButton->setDefaultAction(m_customViewAction); + m_orientationCustomViewSelectToolButton->setSizePolicy(QSizePolicy::Minimum, + QSizePolicy::Fixed); + WuQtUtilities::setToolButtonStyleForQt5Mac(m_orientationCustomViewSelectToolButton); + + QVBoxLayout* layout = new QVBoxLayout(this); + WuQtUtilities::setLayoutSpacingAndMargins(layout, 4, 5); + layout->addWidget(orientationResetToolButton, 0, Qt::AlignHCenter); + layout->addWidget(m_orientationCustomViewSelectToolButton, 0, Qt::AlignHCenter); + + setSizePolicy(QSizePolicy::Fixed, + QSizePolicy::Fixed); +} + +/** + * Destructor. + */ +BrainBrowserWindowToolBarChartTwoOrientation::~BrainBrowserWindowToolBarChartTwoOrientation() +{ + EventManager::get()->removeAllEventsFromListener(this); +} + +/** + * Update content of this tool bar component. + * + * @param browserTabContent + * Content of the browser tab. + */ +void +BrainBrowserWindowToolBarChartTwoOrientation::updateContent(BrowserTabContent* /*browserTabContent*/) +{ +} + +/** + * Called when orientation reset button is pressed. + */ +void +BrainBrowserWindowToolBarChartTwoOrientation::orientationResetToolButtonTriggered(bool /*checked*/) +{ + BrowserTabContent* btc = this->getTabContentFromSelectedTab(); + btc->resetView(); + this->updateGraphicsWindowAndYokedWindows(); +} + +/** + * Called when custom view is triggered and displays Custom View Menu. + */ +void +BrainBrowserWindowToolBarChartTwoOrientation::customViewActionTriggered() +{ + m_parentToolBar->customViewActionTriggered(); +} + diff --git a/src/GuiQt/BrainBrowserWindowToolBarChartTwoOrientation.h b/src/GuiQt/BrainBrowserWindowToolBarChartTwoOrientation.h new file mode 100644 index 0000000000000000000000000000000000000000..6890f270082690b7e1d2e91bcdd6c6704e25e1f0 --- /dev/null +++ b/src/GuiQt/BrainBrowserWindowToolBarChartTwoOrientation.h @@ -0,0 +1,68 @@ +#ifndef __BRAIN_BROWSER_WINDOW_TOOL_BAR_CHART_TWO_ORIENTATION_H__ +#define __BRAIN_BROWSER_WINDOW_TOOL_BAR_CHART_TWO_ORIENTATION_H__ + +/*LICENSE_START*/ +/* + * Copyright (C) 2017 Washington University School of Medicine + * + * 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 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ +/*LICENSE_END*/ + + +#include "BrainBrowserWindowToolBarComponent.h" + +class QToolButton; + +namespace caret { + + class BrainBrowserWindowToolBarChartTwoOrientation : public BrainBrowserWindowToolBarComponent { + Q_OBJECT + + public: + BrainBrowserWindowToolBarChartTwoOrientation(BrainBrowserWindowToolBar* parentToolBar); + + virtual ~BrainBrowserWindowToolBarChartTwoOrientation(); + + virtual void updateContent(BrowserTabContent* browserTabContent); + + // ADD_NEW_METHODS_HERE + + private slots: + void customViewActionTriggered(); + + void orientationResetToolButtonTriggered(bool /*checked*/); + + private: + BrainBrowserWindowToolBarChartTwoOrientation(const BrainBrowserWindowToolBarChartTwoOrientation&); + + BrainBrowserWindowToolBarChartTwoOrientation& operator=(const BrainBrowserWindowToolBarChartTwoOrientation&); + + QAction* m_orientationResetToolButtonAction; + + QAction* m_customViewAction; + + QToolButton* m_orientationCustomViewSelectToolButton; + + // ADD_NEW_MEMBERS_HERE + + }; + +#ifdef __BRAIN_BROWSER_WINDOW_TOOL_BAR_CHART_TWO_ORIENTATION_DECLARE__ + // +#endif // __BRAIN_BROWSER_WINDOW_TOOL_BAR_CHART_TWO_ORIENTATION_DECLARE__ + +} // namespace +#endif //__BRAIN_BROWSER_WINDOW_TOOL_BAR_CHART_TWO_ORIENTATION_H__ diff --git a/src/GuiQt/BrainBrowserWindowToolBarChartTwoTitle.cxx b/src/GuiQt/BrainBrowserWindowToolBarChartTwoTitle.cxx new file mode 100644 index 0000000000000000000000000000000000000000..2e1e2b9cb0573bbaf708f6437f832117a6751bc4 --- /dev/null +++ b/src/GuiQt/BrainBrowserWindowToolBarChartTwoTitle.cxx @@ -0,0 +1,223 @@ + +/*LICENSE_START*/ +/* + * Copyright (C) 2017 Washington University School of Medicine + * + * 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 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ +/*LICENSE_END*/ + +#define __BRAIN_BROWSER_WINDOW_TOOL_BAR_CHART_TWO_TITLE_DECLARE__ +#include "BrainBrowserWindowToolBarChartTwoTitle.h" +#undef __BRAIN_BROWSER_WINDOW_TOOL_BAR_CHART_TWO_TITLE_DECLARE__ + +#include +#include +#include +#include +#include +#include + +#include "AnnotationPercentSizeText.h" +#include "BrainBrowserWindowToolBar.h" +#include "BrowserTabContent.h" +#include "CaretAssert.h" +#include "ChartTwoOverlaySet.h" +#include "ChartTwoTitle.h" +#include "EventChartTwoAttributesChanged.h" +#include "EventManager.h" +#include "ModelChartTwo.h" +#include "WuQDataEntryDialog.h" +#include "WuQDoubleSpinBox.h" +#include "WuQtUtilities.h" + +using namespace caret; + + + +/** + * \class caret::BrainBrowserWindowToolBarChartTwoTitle + * \brief Toolbar component for chart orientation. + * \ingroup GuiQt + */ + +/** + * Constructor. + */ +BrainBrowserWindowToolBarChartTwoTitle::BrainBrowserWindowToolBarChartTwoTitle(BrainBrowserWindowToolBar* parentToolBar) +: BrainBrowserWindowToolBarComponent(parentToolBar) +{ + m_showTitleCheckBox = new QCheckBox("Show Title"); + m_showTitleCheckBox->setToolTip("Show the title at the top of the chart"); + QObject::connect(m_showTitleCheckBox, &QCheckBox::clicked, + this, &BrainBrowserWindowToolBarChartTwoTitle::showTitleCheckBoxClicked); + + QAction* editTitleAction = new QAction("Edit Title..."); + editTitleAction->setToolTip("Edit the chart title in a dialog"); + QObject::connect(editTitleAction, &QAction::triggered, + this, &BrainBrowserWindowToolBarChartTwoTitle::editTitleActionTriggered); + + QToolButton* editTitleToolButton = new QToolButton; + WuQtUtilities::setToolButtonStyleForQt5Mac(editTitleToolButton); + editTitleToolButton->setDefaultAction(editTitleAction); + + m_titleSizeSpinBox = new WuQDoubleSpinBox(this); + m_titleSizeSpinBox->setRangePercentage(0.0, 100.0); + QObject::connect(m_titleSizeSpinBox, static_cast(&WuQDoubleSpinBox::valueChanged), + this, &BrainBrowserWindowToolBarChartTwoTitle::sizeSpinBoxValueChanged); + m_titleSizeSpinBox->setToolTip("Set height of title as percentage of tab height"); + + m_paddingSizeSpinBox = new WuQDoubleSpinBox(this); + m_paddingSizeSpinBox->setRangePercentage(0.0, 100.0); + QObject::connect(m_paddingSizeSpinBox, static_cast(&WuQDoubleSpinBox::valueChanged), + this, &BrainBrowserWindowToolBarChartTwoTitle::sizeSpinBoxValueChanged); + + m_paddingSizeSpinBox->setToolTip("Set padding (space between edge and labels) as percentage of tab height"); + QGridLayout* layout = new QGridLayout(this); + WuQtUtilities::setLayoutSpacingAndMargins(layout, 3, 0); //4, 5); + int row = 0; + layout->addWidget(m_showTitleCheckBox, row, 0, 1, 2, Qt::AlignHCenter); + row++; + layout->addWidget(new QLabel("Size"), row, 0); + layout->addWidget(m_titleSizeSpinBox->getWidget(), row, 1); + row++; + layout->addWidget(new QLabel("Pad"), row, 0); + layout->addWidget(m_paddingSizeSpinBox->getWidget(), row, 1); + row++; + layout->addWidget(editTitleToolButton, row, 0, 1, 2, Qt::AlignHCenter); + row++; + + setSizePolicy(QSizePolicy::Fixed, + QSizePolicy::Fixed); +} + +/** + * Destructor. + */ +BrainBrowserWindowToolBarChartTwoTitle::~BrainBrowserWindowToolBarChartTwoTitle() +{ +} + +/** + * Update content of this tool bar component. + * + * @param browserTabContent + * Content of the browser tab. + */ +void +BrainBrowserWindowToolBarChartTwoTitle::updateContent(BrowserTabContent* browserTabContent) +{ + m_chartOverlaySet = NULL; + + if (browserTabContent != NULL) { + ModelChartTwo* modelChartTwo = browserTabContent->getDisplayedChartTwoModel(); + if (modelChartTwo != NULL) { + const int32_t tabIndex = browserTabContent->getTabNumber(); + m_chartOverlaySet = modelChartTwo->getChartTwoOverlaySet(tabIndex); + } + } + + if (m_chartOverlaySet != NULL) { + setEnabled(true); + const ChartTwoTitle* chartTitle = m_chartOverlaySet->getChartTitle(); + m_showTitleCheckBox->setChecked(chartTitle->isDisplayed()); + + m_titleSizeSpinBox->blockSignals(true); + m_titleSizeSpinBox->setValue(chartTitle->getTextSize()); + m_titleSizeSpinBox->blockSignals(false); + + m_paddingSizeSpinBox->blockSignals(true); + m_paddingSizeSpinBox->setValue(chartTitle->getPaddingSize()); + m_paddingSizeSpinBox->blockSignals(false); + } + else { + setEnabled(false); + } +} + +/** + * Called when show title checkbox is clicked. + * + * @param checked + * Status of checkbox. + */ +void +BrainBrowserWindowToolBarChartTwoTitle::showTitleCheckBoxClicked(bool checked) +{ + if (m_chartOverlaySet != NULL) { + ChartTwoTitle* chartTitle = m_chartOverlaySet->getChartTitle(); + chartTitle->setDisplayed(checked); + this->performUpdating(); + } +} + +/** + * Called when a size spin box value is changed + */ +void +BrainBrowserWindowToolBarChartTwoTitle::sizeSpinBoxValueChanged(double) +{ + if (m_chartOverlaySet != NULL) { + ChartTwoTitle* chartTitle = m_chartOverlaySet->getChartTitle(); + chartTitle->setTextSize(m_titleSizeSpinBox->value()); + chartTitle->setPaddingSize(m_paddingSizeSpinBox->value()); + this->performUpdating(); + } +} + +/** + * Called when edit title action is triggered. + */ +void +BrainBrowserWindowToolBarChartTwoTitle::editTitleActionTriggered() +{ + if (m_chartOverlaySet != NULL) { + WuQDataEntryDialog newNameDialog("Chart Title", + this); + QLineEdit* lineEdit = newNameDialog.addLineEditWidget("Title"); + lineEdit->setText(m_chartOverlaySet->getChartTitle()->getText()); + if (newNameDialog.exec() == WuQDataEntryDialog::Accepted) { + const AString name = lineEdit->text().trimmed(); + m_chartOverlaySet->getChartTitle()->setText(name); + if ( ! name.isEmpty()) { + m_chartOverlaySet->getChartTitle()->setDisplayed(true); + m_showTitleCheckBox->setChecked(true); + } + this->performUpdating(); + } + } +} + +void +BrainBrowserWindowToolBarChartTwoTitle::performUpdating() +{ + const BrowserTabContent* tabContent = getTabContentFromSelectedTab(); + CaretAssert(tabContent); + + const YokingGroupEnum::Enum yokingGroup = tabContent->getChartModelYokingGroup(); + if (yokingGroup != YokingGroupEnum::YOKING_GROUP_OFF) { + const ModelChartTwo* modelChartTwo = tabContent->getDisplayedChartTwoModel(); + CaretAssert(modelChartTwo); + const int32_t tabIndex = tabContent->getTabNumber(); + + EventChartTwoAttributesChanged attributesEvent; + attributesEvent.setTitleChanged(yokingGroup, + modelChartTwo->getSelectedChartTwoDataType(tabIndex), + m_chartOverlaySet->getChartTitle()); + EventManager::get()->sendEvent(attributesEvent.getPointer()); + } + + this->updateGraphicsWindowAndYokedWindows(); +} diff --git a/src/GuiQt/BrainBrowserWindowToolBarChartTwoTitle.h b/src/GuiQt/BrainBrowserWindowToolBarChartTwoTitle.h new file mode 100644 index 0000000000000000000000000000000000000000..cf803400fc22a11c01d4bfd1bada9e638a2129ec --- /dev/null +++ b/src/GuiQt/BrainBrowserWindowToolBarChartTwoTitle.h @@ -0,0 +1,78 @@ +#ifndef __BRAIN_BROWSER_WINDOW_TOOL_BAR_CHART_TWO_TITLE_H__ +#define __BRAIN_BROWSER_WINDOW_TOOL_BAR_CHART_TWO_TITLE_H__ + +/*LICENSE_START*/ +/* + * Copyright (C) 2017 Washington University School of Medicine + * + * 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 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ +/*LICENSE_END*/ + + +#include "BrainBrowserWindowToolBarComponent.h" + +class QCheckBox; + +namespace caret { + + class ChartTwoOverlaySet; + class WuQDoubleSpinBox; + + class BrainBrowserWindowToolBarChartTwoTitle : public BrainBrowserWindowToolBarComponent { + Q_OBJECT + + public: + BrainBrowserWindowToolBarChartTwoTitle(BrainBrowserWindowToolBar* parentToolBar); + + virtual ~BrainBrowserWindowToolBarChartTwoTitle(); + + virtual void updateContent(BrowserTabContent* browserTabContent); + + // ADD_NEW_METHODS_HERE + + private slots: + void editTitleActionTriggered(); + + void showTitleCheckBoxClicked(bool); + + void sizeSpinBoxValueChanged(double); + + private: + BrainBrowserWindowToolBarChartTwoTitle(const BrainBrowserWindowToolBarChartTwoTitle&); + + BrainBrowserWindowToolBarChartTwoTitle& operator=(const BrainBrowserWindowToolBarChartTwoTitle&); + + void performUpdating(); + + QCheckBox* m_showTitleCheckBox; + + WuQDoubleSpinBox* m_titleSizeSpinBox; + + WuQDoubleSpinBox* m_paddingSizeSpinBox; + + + ChartTwoOverlaySet* m_chartOverlaySet; + + // ADD_NEW_MEMBERS_HERE + + }; + +#ifdef __BRAIN_BROWSER_WINDOW_TOOL_BAR_CHART_TWO_TITLE_DECLARE__ + // +#endif // __BRAIN_BROWSER_WINDOW_TOOL_BAR_CHART_TWO_TITLE_DECLARE__ + +} // namespace +#endif //__BRAIN_BROWSER_WINDOW_TOOL_BAR_CHART_TWO_TITLE_H__ diff --git a/src/GuiQt/BrainBrowserWindowToolBarChartTwoType.cxx b/src/GuiQt/BrainBrowserWindowToolBarChartTwoType.cxx new file mode 100644 index 0000000000000000000000000000000000000000..a1cdab23ee8c43c7a589c398b7478bc8a6502b48 --- /dev/null +++ b/src/GuiQt/BrainBrowserWindowToolBarChartTwoType.cxx @@ -0,0 +1,156 @@ + +/*LICENSE_START*/ +/* + * Copyright (C) 2014 Washington University School of Medicine + * + * 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 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ +/*LICENSE_END*/ + +#define __BRAIN_BROWSER_WINDOW_TOOL_BAR_CHART_TWO_TYPE_DECLARE__ +#include "BrainBrowserWindowToolBarChartTwoType.h" +#undef __BRAIN_BROWSER_WINDOW_TOOL_BAR_CHART_TWO_TYPE_DECLARE__ + +#include +#include +#include + +#include "BrowserTabContent.h" +#include "BrainBrowserWindowToolBar.h" +#include "CaretAssert.h" +#include "EnumComboBoxTemplate.h" +#include "ModelChartTwo.h" +#include "WuQtUtilities.h" + +using namespace caret; + + + +/** + * \class caret::BrainBrowserWindowToolBarChartTwoType + * \brief Chart Component of Brain Browser Window ToolBar + * \ingroup GuiQt + */ + +/** + * Constructor. + * + * @param parentToolBar + * parent toolbar. + */ +BrainBrowserWindowToolBarChartTwoType::BrainBrowserWindowToolBarChartTwoType(BrainBrowserWindowToolBar* parentToolBar) +: BrainBrowserWindowToolBarComponent(parentToolBar), +m_parentToolBar(parentToolBar) +{ + m_chartTypeButtonGroup = new QButtonGroup(this); + + QVBoxLayout* radioButtonLayout = new QVBoxLayout(this); + + std::vector allChartTypes; + ChartTwoDataTypeEnum::getAllEnums(allChartTypes); + + for (auto ct : allChartTypes) { + if (ct == ChartTwoDataTypeEnum::CHART_DATA_TYPE_INVALID) { + continue; + } + + QRadioButton* rb = new QRadioButton(ChartTwoDataTypeEnum::toGuiName(ct)); + m_chartTypeButtonGroup->addButton(rb, + m_chartTypeRadioButtons.size()); + + radioButtonLayout->addWidget(rb); + + m_chartTypeRadioButtons.push_back(std::make_pair(ct, rb)); + } + + WuQtUtilities::setLayoutSpacingAndMargins(radioButtonLayout, 4, 5); + radioButtonLayout->addStretch(); + + QObject::connect(m_chartTypeButtonGroup, SIGNAL(buttonClicked(int)), + this, SLOT(chartTypeRadioButtonClicked(int))); +} + +/** + * Destructor. + */ +BrainBrowserWindowToolBarChartTwoType::~BrainBrowserWindowToolBarChartTwoType() +{ +} + +/** + * Called when a radio button is clicked. + * + * @param buttonIndex + * Index of button that is clicked. + */ +void +BrainBrowserWindowToolBarChartTwoType::chartTypeRadioButtonClicked(int buttonIndex) +{ + CaretAssertVectorIndex(m_chartTypeRadioButtons, buttonIndex); + + ChartTwoDataTypeEnum::Enum chartDataType = m_chartTypeRadioButtons[buttonIndex].first; + + m_parentToolBar->getTabContentFromSelectedTab(); + BrowserTabContent* btc = m_parentToolBar->getTabContentFromSelectedTab(); + ModelChartTwo* chartModelTwo = btc->getDisplayedChartTwoModel(); + const int32_t tabIndex = btc->getTabNumber(); + chartModelTwo->setSelectedChartTwoDataType(tabIndex, + chartDataType); + + //updateContent(btc); + + invalidateColoringAndUpdateGraphicsWindow(); + m_parentToolBar->updateUserInterface(); +} + +/** + * Update content of this tool bar component. + * + * @param browserTabContent + * Content of the browser tab. + */ +void +BrainBrowserWindowToolBarChartTwoType::updateContent(BrowserTabContent* browserTabContent) +{ + m_chartTypeButtonGroup->blockSignals(true); + + + const ModelChartTwo* chartModelTwo = browserTabContent->getDisplayedChartTwoModel(); + if (chartModelTwo != NULL) { + const int32_t tabIndex = browserTabContent->getTabNumber(); + const ChartTwoDataTypeEnum::Enum selectedChartType = chartModelTwo->getSelectedChartTwoDataType(tabIndex); + + std::vector validChartDataTypes; + chartModelTwo->getValidChartTwoDataTypes(validChartDataTypes); + + for (auto chartTypeButton : m_chartTypeRadioButtons) { + ChartTwoDataTypeEnum::Enum chartType = chartTypeButton.first; + QRadioButton* radioButton = chartTypeButton.second; + + const bool validTypeFlag = (std::find(validChartDataTypes.begin(), + validChartDataTypes.end(), + chartType) != validChartDataTypes.end()); + radioButton->setEnabled(validTypeFlag); + + if (chartType == selectedChartType) { + radioButton->setChecked(true); + } + } + + } + + m_chartTypeButtonGroup->blockSignals(false); +} + diff --git a/src/GuiQt/BrainBrowserWindowToolBarChartTwoType.h b/src/GuiQt/BrainBrowserWindowToolBarChartTwoType.h new file mode 100644 index 0000000000000000000000000000000000000000..b784137f66ec1417fada78d89aa2ed118c818bf9 --- /dev/null +++ b/src/GuiQt/BrainBrowserWindowToolBarChartTwoType.h @@ -0,0 +1,73 @@ +#ifndef __BRAIN_BROWSER_WINDOW_TOOL_BAR_CHART_TWO_TYPE_H__ +#define __BRAIN_BROWSER_WINDOW_TOOL_BAR_CHART_TWO_TYPE_H__ + +/*LICENSE_START*/ +/* + * Copyright (C) 2014 Washington University School of Medicine + * + * 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 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ +/*LICENSE_END*/ + +class QAbstractButton; +class QButtonGroup; +class QRadioButton; + +#include "BrainBrowserWindowToolBarComponent.h" +#include "ChartTwoDataTypeEnum.h" + +namespace caret { + + class EnumComboBoxTemplate; + + class BrainBrowserWindowToolBarChartTwoType : public BrainBrowserWindowToolBarComponent { + Q_OBJECT + + public: + BrainBrowserWindowToolBarChartTwoType(BrainBrowserWindowToolBar* parentToolBar); + + virtual ~BrainBrowserWindowToolBarChartTwoType(); + + virtual void updateContent(BrowserTabContent* browserTabContent); + + private slots: + void chartTypeRadioButtonClicked(int buttonIndex); + + private: + BrainBrowserWindowToolBarChartTwoType(const BrainBrowserWindowToolBarChartTwoType&); + + BrainBrowserWindowToolBarChartTwoType& operator=(const BrainBrowserWindowToolBarChartTwoType&); + + public: + + // ADD_NEW_METHODS_HERE + + private: + // ADD_NEW_MEMBERS_HERE + + BrainBrowserWindowToolBar* m_parentToolBar; + + std::vector > m_chartTypeRadioButtons; + + QButtonGroup* m_chartTypeButtonGroup; + }; + + +#ifdef __BRAIN_BROWSER_WINDOW_TOOL_BAR_CHART_TWO_TYPE_DECLARE__ + // +#endif // __BRAIN_BROWSER_WINDOW_TOOL_BAR_CHART_TWO_TYPE_DECLARE__ + +} // namespace +#endif //__BRAIN_BROWSER_WINDOW_TOOL_BAR_CHART_TWO_TYPE_H__ diff --git a/src/GuiQt/BrainBrowserWindowToolBarChartType.cxx b/src/GuiQt/BrainBrowserWindowToolBarChartType.cxx index 776f296bb63dc9154215a943d55aa23e278e37ca..b45ecc59be06b4ffae2f8dfa666e3ed598bedf96 100644 --- a/src/GuiQt/BrainBrowserWindowToolBarChartType.cxx +++ b/src/GuiQt/BrainBrowserWindowToolBarChartType.cxx @@ -59,18 +59,18 @@ m_parentToolBar(parentToolBar) QVBoxLayout* radioButtonLayout = new QVBoxLayout(this); - std::vector allChartTypes; - ChartDataTypeEnum::getAllEnums(allChartTypes); + std::vector allChartTypes; + ChartOneDataTypeEnum::getAllEnums(allChartTypes); - for (std::vector::iterator chartIter = allChartTypes.begin(); + for (std::vector::iterator chartIter = allChartTypes.begin(); chartIter != allChartTypes.end(); chartIter++) { - const ChartDataTypeEnum::Enum ct = *chartIter; - if (ct == ChartDataTypeEnum::CHART_DATA_TYPE_INVALID) { + const ChartOneDataTypeEnum::Enum ct = *chartIter; + if (ct == ChartOneDataTypeEnum::CHART_DATA_TYPE_INVALID) { continue; } - QRadioButton* rb = new QRadioButton(ChartDataTypeEnum::toGuiName(ct)); + QRadioButton* rb = new QRadioButton(ChartOneDataTypeEnum::toGuiName(ct)); m_chartTypeButtonGroup->addButton(rb, m_chartTypeRadioButtons.size()); @@ -82,28 +82,8 @@ m_parentToolBar(parentToolBar) WuQtUtilities::setLayoutSpacingAndMargins(radioButtonLayout, 4, 5); radioButtonLayout->addStretch(); -// m_chartMatrixLayerTypeRadioButton = new QRadioButton(ChartDataTypeEnum::toGuiName(ChartDataTypeEnum::CHART_DATA_TYPE_MATRIX_LAYER)); -// -// m_chartMatrixSeriesTypeRadioButton = new QRadioButton(ChartDataTypeEnum::toGuiName(ChartDataTypeEnum::CHART_DATA_TYPE_MATRIX_SERIES)); -// -// m_chartDataSeriesTypeRadioButton = new QRadioButton(ChartDataTypeEnum::toGuiName(ChartDataTypeEnum::CHART_DATA_TYPE_LINE_DATA_SERIES)); -// -// m_chartTimeSeriesTypeRadioButton = new QRadioButton(ChartDataTypeEnum::toGuiName(ChartDataTypeEnum::CHART_DATA_TYPE_LINE_TIME_SERIES)); - - QObject::connect(m_chartTypeButtonGroup, SIGNAL(buttonClicked(int)), this, SLOT(chartTypeRadioButtonClicked(int))); -// m_chartTypeButtonGroup->addButton(m_chartMatrixLayerTypeRadioButton); -// m_chartTypeButtonGroup->addButton(m_chartMatrixSeriesTypeRadioButton); -// m_chartTypeButtonGroup->addButton(m_chartDataSeriesTypeRadioButton); -// m_chartTypeButtonGroup->addButton(m_chartTimeSeriesTypeRadioButton); - -// WuQtUtilities::setLayoutSpacingAndMargins(layout, 4, 5); -// layout->addWidget(m_chartDataSeriesTypeRadioButton); -// layout->addWidget(m_chartMatrixLayerTypeRadioButton); -// layout->addWidget(m_chartMatrixSeriesTypeRadioButton); -// layout->addWidget(m_chartTimeSeriesTypeRadioButton); -// layout->addStretch(); } /** @@ -124,35 +104,15 @@ BrainBrowserWindowToolBarChartType::chartTypeRadioButtonClicked(int buttonIndex) { CaretAssertVectorIndex(m_chartTypeRadioButtons, buttonIndex); - ChartDataTypeEnum::Enum chartDataType = m_chartTypeRadioButtons[buttonIndex].first; - -// ChartDataTypeEnum::Enum chartDataType = ChartDataTypeEnum::CHART_DATA_TYPE_INVALID; -// -// if (m_chartDataSeriesTypeRadioButton->isChecked()) { -// chartDataType = ChartDataTypeEnum::CHART_DATA_TYPE_LINE_DATA_SERIES; -// } -// else if (m_chartMatrixLayerTypeRadioButton->isChecked()) { -// chartDataType = ChartDataTypeEnum::CHART_DATA_TYPE_MATRIX_LAYER; -// } -// else if (m_chartTimeSeriesTypeRadioButton->isChecked()) { -// chartDataType = ChartDataTypeEnum::CHART_DATA_TYPE_LINE_TIME_SERIES; -// } -// else if (m_chartMatrixSeriesTypeRadioButton->isChecked()) { -// chartDataType = ChartDataTypeEnum::CHART_DATA_TYPE_MATRIX_SERIES; -// } -// else { -// CaretAssertMessage(0, "Has a new chart radio button been added?"); -// } + ChartOneDataTypeEnum::Enum chartDataType = m_chartTypeRadioButtons[buttonIndex].first; m_parentToolBar->getTabContentFromSelectedTab(); BrowserTabContent* btc = m_parentToolBar->getTabContentFromSelectedTab(); - ModelChart* chartModel = btc->getDisplayedChartModel(); + ModelChart* chartModel = btc->getDisplayedChartOneModel(); const int32_t tabIndex = btc->getTabNumber(); - chartModel->setSelectedChartDataType(tabIndex, + chartModel->setSelectedChartOneDataType(tabIndex, chartDataType); - //updateContent(btc); - invalidateColoringAndUpdateGraphicsWindow(); m_parentToolBar->updateUserInterface(); } @@ -169,18 +129,18 @@ BrainBrowserWindowToolBarChartType::updateContent(BrowserTabContent* browserTabC m_chartTypeButtonGroup->blockSignals(true); - const ModelChart* chartModel = browserTabContent->getDisplayedChartModel(); + const ModelChart* chartModel = browserTabContent->getDisplayedChartOneModel(); if (chartModel != NULL) { const int32_t tabIndex = browserTabContent->getTabNumber(); - const ChartDataTypeEnum::Enum selectedChartType = chartModel->getSelectedChartDataType(tabIndex); + const ChartOneDataTypeEnum::Enum selectedChartType = chartModel->getSelectedChartOneDataType(tabIndex); - std::vector validChartDataTypes; - chartModel->getValidChartDataTypes(validChartDataTypes); + std::vector validChartDataTypes; + chartModel->getValidChartOneDataTypes(validChartDataTypes); - for (std::vector >::iterator buttIter = m_chartTypeRadioButtons.begin(); + for (std::vector >::iterator buttIter = m_chartTypeRadioButtons.begin(); buttIter != m_chartTypeRadioButtons.end(); buttIter++) { - ChartDataTypeEnum::Enum chartType = buttIter->first; + ChartOneDataTypeEnum::Enum chartType = buttIter->first; QRadioButton* radioButton = buttIter->second; const bool validTypeFlag = (std::find(validChartDataTypes.begin(), @@ -192,58 +152,6 @@ BrainBrowserWindowToolBarChartType::updateContent(BrowserTabContent* browserTabC radioButton->setChecked(true); } } - -// bool dataSeriesValidFlag = false; -// bool matrixLayerValidFlag = false; -// bool matrixSeriesValidFlag = false; -// bool timeSeriesValidFlag = false; -// for (std::vector::iterator iter = validChartDataTypes.begin(); -// iter != validChartDataTypes.end(); -// iter++) { -// switch (*iter) { -// case ChartDataTypeEnum::CHART_DATA_TYPE_INVALID: -// break; -// case ChartDataTypeEnum::CHART_DATA_TYPE_MATRIX_LAYER: -// matrixLayerValidFlag = true; -// break; -// case ChartDataTypeEnum::CHART_DATA_TYPE_MATRIX_SERIES: -// matrixSeriesValidFlag = true; -// break; -// case ChartDataTypeEnum::CHART_DATA_TYPE_LINE_DATA_SERIES: -// dataSeriesValidFlag = true; -// break; -// case ChartDataTypeEnum::CHART_DATA_TYPE_LINE_FREQUENCY_SERIES: -// CaretAssertToDoFatal(); -// break; -// case ChartDataTypeEnum::CHART_DATA_TYPE_LINE_TIME_SERIES: -// timeSeriesValidFlag = true; -// break; -// } -// } -// -// m_chartDataSeriesTypeRadioButton->setEnabled(dataSeriesValidFlag); -// m_chartMatrixLayerTypeRadioButton->setEnabled(matrixLayerValidFlag); -// m_chartTimeSeriesTypeRadioButton->setEnabled(timeSeriesValidFlag); -// -// switch (chartType) { -// case ChartDataTypeEnum::CHART_DATA_TYPE_INVALID: -// break; -// case ChartDataTypeEnum::CHART_DATA_TYPE_MATRIX_LAYER: -// m_chartMatrixLayerTypeRadioButton->setChecked(true); -// break; -// case ChartDataTypeEnum::CHART_DATA_TYPE_MATRIX_SERIES: -// m_chartMatrixSeriesTypeRadioButton->setChecked(true); -// break; -// case ChartDataTypeEnum::CHART_DATA_TYPE_LINE_DATA_SERIES: -// m_chartDataSeriesTypeRadioButton->setChecked(true); -// break; -// case ChartDataTypeEnum::CHART_DATA_TYPE_LINE_FREQUENCY_SERIES: -// CaretAssertToDoFatal(); -// break; -// case ChartDataTypeEnum::CHART_DATA_TYPE_LINE_TIME_SERIES: -// m_chartTimeSeriesTypeRadioButton->setChecked(true); -// break; -// } } m_chartTypeButtonGroup->blockSignals(false); diff --git a/src/GuiQt/BrainBrowserWindowToolBarChartType.h b/src/GuiQt/BrainBrowserWindowToolBarChartType.h index eebf96f2f63f4df057f28ce3c2001e749d14da0b..f468511becd38766c906978ace807b51bc20a819 100644 --- a/src/GuiQt/BrainBrowserWindowToolBarChartType.h +++ b/src/GuiQt/BrainBrowserWindowToolBarChartType.h @@ -26,7 +26,7 @@ class QButtonGroup; class QRadioButton; #include "BrainBrowserWindowToolBarComponent.h" -#include "ChartDataTypeEnum.h" +#include "ChartOneDataTypeEnum.h" namespace caret { @@ -59,7 +59,7 @@ namespace caret { BrainBrowserWindowToolBar* m_parentToolBar; - std::vector > m_chartTypeRadioButtons; + std::vector > m_chartTypeRadioButtons; QButtonGroup* m_chartTypeButtonGroup; diff --git a/src/GuiQt/BrainBrowserWindowToolBarClipping.cxx b/src/GuiQt/BrainBrowserWindowToolBarClipping.cxx index 5036749ca26d849385877f67e0d740981523a0fd..9c3beeb22c6fc40f9dcc6f4e709ae0bfa21335c1 100644 --- a/src/GuiQt/BrainBrowserWindowToolBarClipping.cxx +++ b/src/GuiQt/BrainBrowserWindowToolBarClipping.cxx @@ -82,11 +82,13 @@ m_parentToolBar(parentToolBar) setupToolButton->setText("Setup"); QObject::connect(setupToolButton, SIGNAL(clicked()), this, SLOT(setupClippingPushButtonClicked())); + WuQtUtilities::setToolButtonStyleForQt5Mac(setupToolButton); QGridLayout* gridLayout = new QGridLayout(this); +// WuQtUtilities::setLayoutSpacingAndMargins(gridLayout, 2, 0); gridLayout->setHorizontalSpacing(6); - gridLayout->setVerticalSpacing(8); - gridLayout->setContentsMargins(0, 0, 0, 0); + gridLayout->setVerticalSpacing(4); + gridLayout->setContentsMargins(1, 1, 1, 1); int32_t rowIndex = gridLayout->rowCount(); gridLayout->addWidget(m_xClippingEnabledCheckBox, rowIndex, 0); gridLayout->addWidget(m_yClippingEnabledCheckBox, rowIndex, 1); @@ -179,8 +181,7 @@ BrainBrowserWindowToolBarClipping::clippingCheckBoxCheckStatusChanged() m_volumeClippingEnabledCheckBox->isChecked(), m_featuresClippingEnabledCheckBox->isChecked()); - this->updateGraphicsWindow(); - this->updateOtherYokedWindows(); + this->updateGraphicsWindowAndYokedWindows(); } } diff --git a/src/GuiQt/BrainBrowserWindowToolBarComponent.cxx b/src/GuiQt/BrainBrowserWindowToolBarComponent.cxx index 508e8d8f1fcd8d5446c86d7ac3c82692f4250857..3ac368a5211caaa1a13c2ff4128f0cccb4567700 100644 --- a/src/GuiQt/BrainBrowserWindowToolBarComponent.cxx +++ b/src/GuiQt/BrainBrowserWindowToolBarComponent.cxx @@ -137,12 +137,13 @@ BrainBrowserWindowToolBarComponent::updateGraphicsWindow() } /** - * Update toolbars in other yoked windows. + * If this window is yoked, update all windows since they may + * be yoked to this window. If NOT yoked, just update this window. */ void -BrainBrowserWindowToolBarComponent::updateOtherYokedWindows() +BrainBrowserWindowToolBarComponent::updateGraphicsWindowAndYokedWindows() { - m_parentToolBar->updateOtherYokedWindows(); + m_parentToolBar->updateGraphicsWindowAndYokedWindows(); } /** diff --git a/src/GuiQt/BrainBrowserWindowToolBarComponent.h b/src/GuiQt/BrainBrowserWindowToolBarComponent.h index 64b982c8ce4363b2644717ca11d2ec968de604cb..7c7763257cb0f4f5bfd06ac9f4ca001d6bd7ba78 100644 --- a/src/GuiQt/BrainBrowserWindowToolBarComponent.h +++ b/src/GuiQt/BrainBrowserWindowToolBarComponent.h @@ -51,7 +51,7 @@ namespace caret { void updateGraphicsWindow(); - void updateOtherYokedWindows(); + void updateGraphicsWindowAndYokedWindows(); void updateUserInterface(); @@ -78,6 +78,7 @@ namespace caret { // ADD_NEW_MEMBERS_HERE + friend class BrainBrowserWindowToolBarChartTwoOrientation; }; #ifdef __BRAIN_BROWSER_WINDOW_TOOL_BAR_COMPONENT_DECLARE__ diff --git a/src/GuiQt/BrainBrowserWindowToolBarSlicePlane.cxx b/src/GuiQt/BrainBrowserWindowToolBarSlicePlane.cxx index 7633265c562e223fd232a5cbb0ed0cdac287384d..5c9acd8c083f09d4915732b9efd95a755a15f1e1 100644 --- a/src/GuiQt/BrainBrowserWindowToolBarSlicePlane.cxx +++ b/src/GuiQt/BrainBrowserWindowToolBarSlicePlane.cxx @@ -26,6 +26,8 @@ #include #include #include +#include +#include #include #include "BrainBrowserWindowToolBar.h" @@ -93,9 +95,11 @@ m_parentToolBar(parentToolBar) } m_volumePlaneAllToolButtonAction = WuQtUtilities::createAction(VolumeSliceViewPlaneEnum::toGuiNameAbbreviation(VolumeSliceViewPlaneEnum::ALL), - "View the PARASAGITTAL, CORONAL, and AXIAL slices", + "View the PARASAGITTAL, CORONAL, and AXIAL slices\n" + "Press arrow to display menu for layout selection", this); m_volumePlaneAllToolButtonAction->setCheckable(true); + m_volumePlaneAllToolButtonAction->setMenu(createViewAllSlicesLayoutMenu()); m_volumePlaneActionGroup = new QActionGroup(this); @@ -117,18 +121,23 @@ m_parentToolBar(parentToolBar) QToolButton* volumePlaneParasagittalToolButton = new QToolButton(); volumePlaneParasagittalToolButton->setDefaultAction(m_volumePlaneParasagittalToolButtonAction); + WuQtUtilities::setToolButtonStyleForQt5Mac(volumePlaneParasagittalToolButton); QToolButton* volumePlaneCoronalToolButton = new QToolButton(); volumePlaneCoronalToolButton->setDefaultAction(m_volumePlaneCoronalToolButtonAction); + WuQtUtilities::setToolButtonStyleForQt5Mac(volumePlaneCoronalToolButton); QToolButton* volumePlaneAxialToolButton = new QToolButton(); volumePlaneAxialToolButton->setDefaultAction(m_volumePlaneAxialToolButtonAction); + WuQtUtilities::setToolButtonStyleForQt5Mac(volumePlaneAxialToolButton); QToolButton* volumePlaneAllToolButton = new QToolButton(); volumePlaneAllToolButton->setDefaultAction(m_volumePlaneAllToolButtonAction); + WuQtUtilities::setToolButtonStyleForQt5Mac(volumePlaneAllToolButton); QToolButton* volumePlaneResetToolButton = new QToolButton(); volumePlaneResetToolButton->setDefaultAction(m_volumePlaneResetToolButtonAction); + WuQtUtilities::setToolButtonStyleForQt5Mac(volumePlaneResetToolButton); WuQtUtilities::matchWidgetHeights(volumePlaneParasagittalToolButton, volumePlaneCoronalToolButton, @@ -143,6 +152,32 @@ m_parentToolBar(parentToolBar) slicePlaneCustomToolButton->setDefaultAction(m_parentToolBar->customViewAction); slicePlaneCustomToolButton->setSizePolicy(QSizePolicy::Minimum, QSizePolicy::Fixed); + WuQtUtilities::setToolButtonStyleForQt5Mac(slicePlaneCustomToolButton); + + m_volumeAxisCrosshairsToolButtonAction = new QAction(""); + m_volumeAxisCrosshairsToolButtonAction->setCheckable(true); + m_volumeAxisCrosshairsToolButtonAction->setToolTip("Show crosshairs on slice planes"); + QObject::connect(m_volumeAxisCrosshairsToolButtonAction, &QAction::triggered, + this, &BrainBrowserWindowToolBarSlicePlane::volumeAxisCrosshairsTriggered); + QToolButton* volumeCrosshairsToolButton = new QToolButton(); + QPixmap xhairPixmap = createCrosshairsIcon(volumeCrosshairsToolButton); + volumeCrosshairsToolButton->setDefaultAction(m_volumeAxisCrosshairsToolButtonAction); + m_volumeAxisCrosshairsToolButtonAction->setIcon(QIcon(xhairPixmap)); + volumeCrosshairsToolButton->setIconSize(xhairPixmap.size()); + WuQtUtilities::setToolButtonStyleForQt5Mac(volumeCrosshairsToolButton); + + m_volumeAxisCrosshairLabelsToolButtonAction = new QAction(""); + m_volumeAxisCrosshairLabelsToolButtonAction->setCheckable(true); + m_volumeAxisCrosshairLabelsToolButtonAction->setToolTip("Show crosshair slice plane labels"); + QObject::connect(m_volumeAxisCrosshairLabelsToolButtonAction, &QAction::triggered, + this, &BrainBrowserWindowToolBarSlicePlane::volumeAxisCrosshairLabelsTriggered); + QToolButton* volumeCrosshairLabelsToolButton = new QToolButton(); + volumeCrosshairLabelsToolButton->setDefaultAction(m_volumeAxisCrosshairLabelsToolButtonAction); + QPixmap labelsPixmap = createCrosshairLabelsIcon(volumeCrosshairLabelsToolButton); + m_volumeAxisCrosshairLabelsToolButtonAction->setIcon(QIcon(labelsPixmap)); + volumeCrosshairLabelsToolButton->setIconSize(labelsPixmap.size()); + WuQtUtilities::setToolButtonStyleForQt5Mac(volumeCrosshairLabelsToolButton); + QGridLayout* gridLayout = new QGridLayout(this); WuQtUtilities::setLayoutSpacingAndMargins(gridLayout, 0, 0); @@ -156,6 +191,9 @@ m_parentToolBar(parentToolBar) gridLayout->addWidget(volumePlaneResetToolButton, rowIndex, 0, 1, 2, Qt::AlignHCenter); rowIndex++; gridLayout->addWidget(slicePlaneCustomToolButton, rowIndex, 0, 1, 2, Qt::AlignHCenter); + rowIndex++; + gridLayout->addWidget(volumeCrosshairsToolButton, rowIndex, 0, Qt::AlignRight); + gridLayout->addWidget(volumeCrosshairLabelsToolButton, rowIndex, 1, Qt::AlignLeft); m_volumePlaneWidgetGroup = new WuQWidgetObjectGroup(this); m_volumePlaneWidgetGroup->add(m_volumePlaneActionGroup); @@ -195,9 +233,83 @@ BrainBrowserWindowToolBarSlicePlane::updateContent(BrowserTabContent* browserTab break; } + updateViewAllSlicesLayoutMenu(browserTabContent); + + m_volumeAxisCrosshairsToolButtonAction->setChecked(browserTabContent->isVolumeAxesCrosshairsDisplayed()); + m_volumeAxisCrosshairLabelsToolButtonAction->setChecked(browserTabContent->isVolumeAxesCrosshairLabelsDisplayed()); + m_volumePlaneWidgetGroup->blockAllSignals(false); } +/** + * @return A new instance of the view all slices layout menu. + */ +QMenu* +BrainBrowserWindowToolBarSlicePlane::createViewAllSlicesLayoutMenu() +{ + std::vector allLayouts; + VolumeSliceViewAllPlanesLayoutEnum::getAllEnums(allLayouts); + + QMenu* menu = new QMenu(); + QActionGroup* actionGroup = new QActionGroup(this); + + for (auto layout : allLayouts) { + QAction* action = menu->addAction(VolumeSliceViewAllPlanesLayoutEnum::toGuiName(layout)); + action->setData((int)VolumeSliceViewAllPlanesLayoutEnum::toIntegerCode(layout)); + action->setCheckable(true); + m_viewAllSliceLayoutMenuActions.push_back(action); + + actionGroup->addAction(action); + } + + QObject::connect(menu, &QMenu::triggered, + this, &BrainBrowserWindowToolBarSlicePlane::viewAllSliceLayoutMenuTriggered); + return menu; +} + +/** + * Gets called when the user selects an item on the view all slices layout menu. + * + * @param action + * Action of menu item selected. + */ +void +BrainBrowserWindowToolBarSlicePlane::viewAllSliceLayoutMenuTriggered(QAction* action) +{ + const int layoutInt = action->data().toInt(); + bool validFlag = false; + const VolumeSliceViewAllPlanesLayoutEnum::Enum layout = VolumeSliceViewAllPlanesLayoutEnum::fromIntegerCode(layoutInt, &validFlag); + if (validFlag) { + BrowserTabContent* btc = getTabContentFromSelectedTab(); + + btc->setSlicePlanesAllViewLayout(layout); + + m_parentToolBar->updateVolumeIndicesWidget(btc); + updateGraphicsWindowAndYokedWindows(); + } + else { + CaretLogSevere("Invalid layout in menu item: " + + action->text()); + } +} + +/** + * Update the view all slices layout menu. + */ +void +BrainBrowserWindowToolBarSlicePlane::updateViewAllSlicesLayoutMenu(BrowserTabContent* browserTabContent) +{ + const VolumeSliceViewAllPlanesLayoutEnum::Enum layout = browserTabContent->getSlicePlanesAllViewLayout(); + const int layoutIntValue = VolumeSliceViewAllPlanesLayoutEnum::toIntegerCode(layout); + + for (auto action : m_viewAllSliceLayoutMenuActions) { + if (action->data().toInt() == layoutIntValue) { + action->setChecked(true); + break; + } + } +} + /** * Called when volume slice plane button is clicked. */ @@ -229,8 +341,7 @@ BrainBrowserWindowToolBarSlicePlane::volumePlaneActionGroupTriggered(QAction* ac btc->setSliceViewPlane(plane); m_parentToolBar->updateVolumeIndicesWidget(btc); - updateGraphicsWindow(); - updateOtherYokedWindows(); + updateGraphicsWindowAndYokedWindows(); } /** @@ -243,7 +354,108 @@ BrainBrowserWindowToolBarSlicePlane::volumePlaneResetToolButtonTriggered(bool /* btc->resetView(); m_parentToolBar->updateVolumeIndicesWidget(btc); - updateGraphicsWindow(); - updateOtherYokedWindows(); + updateGraphicsWindowAndYokedWindows(); } +/** + * Called when volume axis crosshairs button triggered + * + * @param checked + * New checked status + */ +void +BrainBrowserWindowToolBarSlicePlane::volumeAxisCrosshairsTriggered(bool checked) +{ + BrowserTabContent* btc = getTabContentFromSelectedTab(); + btc->setVolumeAxesCrosshairsDisplayed(checked); + updateGraphicsWindowAndYokedWindows(); +} + +/** + * Called when volume axis crosshair labels button triggered + * + * @param checked + * New checked status + */ +void +BrainBrowserWindowToolBarSlicePlane::volumeAxisCrosshairLabelsTriggered(bool checked) +{ + BrowserTabContent* btc = getTabContentFromSelectedTab(); + btc->setVolumeAxesCrosshairLabelsDisplayed(checked); + updateGraphicsWindowAndYokedWindows(); +} + +/** + * Create a pixmap for the crosshairs button. + * + * @param widget + * To color the pixmap with backround and foreground, + * the palette from the given widget is used. + * @return + * The pixmap. + */ +QPixmap +BrainBrowserWindowToolBarSlicePlane::createCrosshairsIcon(const QWidget* widget) +{ + CaretAssert(widget); + const float pixmapSize = 22.0; + + QPixmap pixmap(static_cast(pixmapSize), + static_cast(pixmapSize)); + QSharedPointer painter = WuQtUtilities::createPixmapWidgetPainterOriginCenter(widget, + pixmap); + const int startXY = 4; + const int endXY = 10; + QPen pen(painter->pen()); + pen.setWidth(2); + painter->setPen(pen); + painter->drawLine(-startXY, 0, -endXY, 0); + painter->drawLine( startXY, 0, endXY, 0); + painter->drawLine(0, -startXY, 0, -endXY); + painter->drawLine(0, startXY, 0, endXY); + + return pixmap; +} + +/** + * Create a pixmap for the crosshairs button. + * + * @param widget + * To color the pixmap with backround and foreground, + * the palette from the given widget is used. + * @return + * The pixmap. + */ +QPixmap +BrainBrowserWindowToolBarSlicePlane::createCrosshairLabelsIcon(const QWidget* widget) +{ + CaretAssert(widget); + + const float pixmapSize = 22.0f; + const float fullXY = pixmapSize; + const float halfXY = fullXY / 2.0f; + + const float boxWH = 8.0f; + const float halfBoxWH = boxWH / 2.0f; + + QPixmap pixmap(static_cast(pixmapSize), + static_cast(pixmapSize)); + QSharedPointer painter = WuQtUtilities::createPixmapWidgetPainter(widget, + pixmap); + QFont font = painter->font(); + font.setPixelSize(10); + painter->setFont(font); + + const float edgeOffset = 1.0f; + painter->drawText(QRectF(edgeOffset, halfXY - halfBoxWH, boxWH, boxWH), + "L", QTextOption(Qt::AlignCenter)); + painter->drawText(QRectF(fullXY - boxWH - edgeOffset + 1, halfXY - halfBoxWH - 1, boxWH, boxWH), + "R", QTextOption(Qt::AlignCenter)); + + painter->drawText(QRectF(halfXY - halfBoxWH, edgeOffset, boxWH, boxWH), + "A", QTextOption(Qt::AlignCenter)); + painter->drawText(QRectF(halfXY - halfBoxWH + 1, fullXY - boxWH - edgeOffset, boxWH, boxWH), + "P", QTextOption(Qt::AlignCenter)); + + return pixmap; +} diff --git a/src/GuiQt/BrainBrowserWindowToolBarSlicePlane.h b/src/GuiQt/BrainBrowserWindowToolBarSlicePlane.h index 45795d0151d50eba221aec09f174f5b03ffaa593..f22ed5f140536323eadbc1c5bbeb85726ec39d25 100644 --- a/src/GuiQt/BrainBrowserWindowToolBarSlicePlane.h +++ b/src/GuiQt/BrainBrowserWindowToolBarSlicePlane.h @@ -25,6 +25,8 @@ #include "BrainBrowserWindowToolBarComponent.h" class QActionGroup; +class QMenu; +class QPixmap; namespace caret { @@ -46,14 +48,28 @@ namespace caret { void volumePlaneActionGroupTriggered(QAction*); void volumePlaneResetToolButtonTriggered(bool checked); + void viewAllSliceLayoutMenuTriggered(QAction* action); + void volumeAxisCrosshairsTriggered(bool checked); + void volumeAxisCrosshairLabelsTriggered(bool checked); + private: BrainBrowserWindowToolBarSlicePlane(const BrainBrowserWindowToolBarSlicePlane&); BrainBrowserWindowToolBarSlicePlane& operator=(const BrainBrowserWindowToolBarSlicePlane&); + QMenu* createViewAllSlicesLayoutMenu(); + + void updateViewAllSlicesLayoutMenu(BrowserTabContent* browserTabContent); + + QPixmap createCrosshairsIcon(const QWidget* widget); + + QPixmap createCrosshairLabelsIcon(const QWidget* widget); + BrainBrowserWindowToolBar* m_parentToolBar; + std::vector m_viewAllSliceLayoutMenuActions; + WuQWidgetObjectGroup* m_volumePlaneWidgetGroup; QAction* m_volumePlaneParasagittalToolButtonAction; @@ -61,6 +77,8 @@ namespace caret { QAction* m_volumePlaneAxialToolButtonAction; QAction* m_volumePlaneAllToolButtonAction; QAction* m_volumePlaneResetToolButtonAction; + QAction* m_volumeAxisCrosshairsToolButtonAction; + QAction* m_volumeAxisCrosshairLabelsToolButtonAction; QActionGroup* m_volumePlaneActionGroup; diff --git a/src/GuiQt/BrainBrowserWindowToolBarSliceSelection.cxx b/src/GuiQt/BrainBrowserWindowToolBarSliceSelection.cxx index 69ddde3be3956aee769319776ce5265399881b48..edcbf779fac00b39b618ac6ec0617d5870c62367 100644 --- a/src/GuiQt/BrainBrowserWindowToolBarSliceSelection.cxx +++ b/src/GuiQt/BrainBrowserWindowToolBarSliceSelection.cxx @@ -30,6 +30,7 @@ #include #include #include +#include #include #include @@ -43,6 +44,7 @@ #include "ModelVolume.h" #include "ModelWholeBrain.h" #include "VolumeFile.h" +#include "VolumeSliceInterpolationEdgeEffectsMaskingEnum.h" #include "VolumeSliceProjectionTypeEnum.h" #include "WuQFactory.h" #include "WuQWidgetObjectGroup.h" @@ -73,6 +75,7 @@ m_parentToolBar(parentToolBar) SLOT(volumeIndicesOriginActionTriggered())); QToolButton* volumeIndicesOriginToolButton = new QToolButton; volumeIndicesOriginToolButton->setDefaultAction(volumeIndicesOriginToolButtonAction); + WuQtUtilities::setToolButtonStyleForQt5Mac(volumeIndicesOriginToolButton); QLabel* parasagittalLabel = new QLabel("P:"); QLabel* coronalLabel = new QLabel("C:"); @@ -167,6 +170,7 @@ m_parentToolBar(parentToolBar) } QToolButton* volumeIDToolButton = new QToolButton; volumeIDToolButton->setDefaultAction(m_volumeIdentificationUpdatesSlicesAction); + WuQtUtilities::setToolButtonStyleForQt5Mac(volumeIDToolButton); m_volumeSliceProjectionTypeEnumComboBox = new EnumComboBoxTemplate(this); m_volumeSliceProjectionTypeEnumComboBox->setup(); @@ -176,6 +180,15 @@ m_parentToolBar(parentToolBar) WuQtUtilities::setToolTipAndStatusTip(m_volumeSliceProjectionTypeEnumComboBox->getWidget(), "Chooses viewing orientation (oblique or orthogonal)"); + m_obliqueMaskingAction = new QAction("M"); + m_obliqueMaskingAction->setToolTip(VolumeSliceInterpolationEdgeEffectsMaskingEnum::getToolTip()); + m_obliqueMaskingAction->setCheckable(true); + QObject::connect(m_obliqueMaskingAction, &QAction::triggered, + this, &BrainBrowserWindowToolBarSliceSelection::obliqueMaskingActionTriggered); + QToolButton* obliqueMaskingToolButton = new QToolButton(); + obliqueMaskingToolButton->setDefaultAction(m_obliqueMaskingAction); + WuQtUtilities::setToolButtonStyleForQt5Mac(obliqueMaskingToolButton); + QGridLayout* gridLayout = new QGridLayout(this); WuQtUtilities::setLayoutSpacingAndMargins(gridLayout, 0, 0); gridLayout->addWidget(m_volumeIndicesParasagittalCheckBox, 0, 0); @@ -194,7 +207,8 @@ m_parentToolBar(parentToolBar) gridLayout->addWidget(m_volumeIndicesZcoordSpinBox, 2, 3); gridLayout->addWidget(volumeIDToolButton, 3, 0, 1, 2, Qt::AlignLeft); - gridLayout->addWidget(m_volumeSliceProjectionTypeEnumComboBox->getWidget(), 3, 2, 1, 3, Qt::AlignRight); + gridLayout->addWidget(m_volumeSliceProjectionTypeEnumComboBox->getWidget(), 3, 2, 1, 2, Qt::AlignCenter); + gridLayout->addWidget(obliqueMaskingToolButton, 3, 4); gridLayout->addWidget(volumeIndicesOriginToolButton, 0, 4, 3, 1); @@ -209,6 +223,7 @@ m_parentToolBar(parentToolBar) m_volumeIndicesWidgetGroup->add(m_volumeIndicesXcoordSpinBox); m_volumeIndicesWidgetGroup->add(m_volumeIndicesYcoordSpinBox); m_volumeIndicesWidgetGroup->add(m_volumeIndicesZcoordSpinBox); + m_volumeIndicesWidgetGroup->add(m_volumeSliceProjectionTypeEnumComboBox->getWidget()); m_volumeIndicesWidgetGroup->add(m_volumeIdentificationUpdatesSlicesAction); } @@ -254,11 +269,47 @@ BrainBrowserWindowToolBarSliceSelection::updateContent(BrowserTabContent* browse } if (vf != NULL) { + /* + * Test selected file to see if it is an oblique volume file (not a CIFTI file) + */ + bool obliqueVolumeFlag = false; + VolumeFile* volumeFile = dynamic_cast(vf); + if (volumeFile != NULL) { + if ( ! volumeFile->isPlumb()) { + obliqueVolumeFlag = true; + } + } + + /* + * Update slice projection type for allowed projection types + */ + std::vector validSliceProjections; + validSliceProjections.push_back(VolumeSliceProjectionTypeEnum::VOLUME_SLICE_PROJECTION_OBLIQUE); + if ( ! obliqueVolumeFlag) { + validSliceProjections.push_back(VolumeSliceProjectionTypeEnum::VOLUME_SLICE_PROJECTION_ORTHOGONAL); + } + m_volumeSliceProjectionTypeEnumComboBox->setupWithItems(validSliceProjections); + + /* + * If volume is oblique, change its projection type to oblique + */ + switch (browserTabContent->getSliceProjectionType()) { + case VolumeSliceProjectionTypeEnum::VOLUME_SLICE_PROJECTION_OBLIQUE: + break; + case VolumeSliceProjectionTypeEnum::VOLUME_SLICE_PROJECTION_ORTHOGONAL: + if (obliqueVolumeFlag) { + browserTabContent->setSliceProjectionType(VolumeSliceProjectionTypeEnum::VOLUME_SLICE_PROJECTION_OBLIQUE); + } + break; + } + m_volumeIndicesAxialCheckBox->setChecked(browserTabContent->isSliceAxialEnabled()); m_volumeIndicesCoronalCheckBox->setChecked(browserTabContent->isSliceCoronalEnabled()); m_volumeIndicesParasagittalCheckBox->setChecked(browserTabContent->isSliceParasagittalEnabled()); } + updateObliqueMaskingButton(); + m_volumeSliceProjectionTypeEnumComboBox->setSelectedItem(browserTabContent->getSliceProjectionType()); m_volumeIdentificationUpdatesSlicesAction->setChecked(browserTabContent->isIdentificationUpdatesVolumeSlices()); @@ -310,12 +361,41 @@ BrainBrowserWindowToolBarSliceSelection::updateSliceIndicesAndCoordinatesRanges( int maxCoronalDim = (dimensions[1] > 0) ? (dimensions[1] - 1) : 0; int maxParasagittalDim = (dimensions[0] > 0) ? (dimensions[0] - 1) : 0; - m_volumeIndicesAxialSpinBox->setRange(minAxialDim, - maxAxialDim); - m_volumeIndicesCoronalSpinBox->setRange(minCoronalDim, - maxCoronalDim); - m_volumeIndicesParasagittalSpinBox->setRange(minParasagittalDim, - maxParasagittalDim); + /* + * BUG NOTE: + * On Linux, if the user hold down an arrow key in a spin box + * and the time to process the signal is "slow", qt will emit + * a second signal (QTBUG-14259). In addition, calling any of + * the setMinimum(), setMaximum(), or setRange() methods seems + * to also cause emission of a signal. The result when the + * user releases the arrow key, there are many backlogged signals + * and it may take a while for them to process and the user + * sees the slices scrolling for a while after the arrow key + * is released. + * + * So, do not update range min/max unless the new range + * is different than the range in the spin box. + */ + if (m_volumeIndicesAxialSpinBox->minimum() != minAxialDim) { + m_volumeIndicesAxialSpinBox->setMinimum(minAxialDim); + } + if (m_volumeIndicesAxialSpinBox->maximum() != maxAxialDim) { + m_volumeIndicesAxialSpinBox->setMaximum(maxAxialDim); + } + + if (m_volumeIndicesCoronalSpinBox->minimum() != minCoronalDim) { + m_volumeIndicesCoronalSpinBox->setMinimum(minCoronalDim); + } + if (m_volumeIndicesCoronalSpinBox->maximum() != maxCoronalDim) { + m_volumeIndicesCoronalSpinBox->setMaximum(maxCoronalDim); + } + + if (m_volumeIndicesParasagittalSpinBox->minimum() != minParasagittalDim) { + m_volumeIndicesParasagittalSpinBox->setMinimum(minParasagittalDim); + } + if (m_volumeIndicesParasagittalSpinBox->maximum() != maxParasagittalDim) { + m_volumeIndicesParasagittalSpinBox->setMaximum(maxParasagittalDim); + } /* @@ -331,19 +411,40 @@ BrainBrowserWindowToolBarSliceSelection::updateSliceIndicesAndCoordinatesRanges( vf->indexToSpace(slicesMax, sliceMaxCoords); - m_volumeIndicesXcoordSpinBox->setMinimum(std::min(sliceZeroCoords[0], - sliceMaxCoords[0])); - m_volumeIndicesYcoordSpinBox->setMinimum(std::min(sliceZeroCoords[1], - sliceMaxCoords[1])); - m_volumeIndicesZcoordSpinBox->setMinimum(std::min(sliceZeroCoords[2], - sliceMaxCoords[2])); + const double minX = std::min(sliceZeroCoords[0], + sliceMaxCoords[0]); + const double maxX = std::max(sliceZeroCoords[0], + sliceMaxCoords[0]); + const double minY = std::min(sliceZeroCoords[1], + sliceMaxCoords[1]); + const double maxY = std::max(sliceZeroCoords[1], + sliceMaxCoords[1]); + const double minZ = std::min(sliceZeroCoords[2], + sliceMaxCoords[2]); + const double maxZ = std::max(sliceZeroCoords[2], + sliceMaxCoords[2]); - m_volumeIndicesXcoordSpinBox->setMaximum(std::max(sliceZeroCoords[0], - sliceMaxCoords[0])); - m_volumeIndicesYcoordSpinBox->setMaximum(std::max(sliceZeroCoords[1], - sliceMaxCoords[1])); - m_volumeIndicesZcoordSpinBox->setMaximum(std::max(sliceZeroCoords[2], - sliceMaxCoords[2])); + /* + * See BUG NOTE above. + */ + if (m_volumeIndicesXcoordSpinBox->minimum() != minX) { + m_volumeIndicesXcoordSpinBox->setMinimum(minX); + } + if (m_volumeIndicesXcoordSpinBox->maximum() != maxX) { + m_volumeIndicesXcoordSpinBox->setMaximum(maxX); + } + if (m_volumeIndicesYcoordSpinBox->minimum() != minY) { + m_volumeIndicesYcoordSpinBox->setMinimum(minY); + } + if (m_volumeIndicesYcoordSpinBox->maximum() != maxY) { + m_volumeIndicesYcoordSpinBox->setMaximum(maxY); + } + if (m_volumeIndicesZcoordSpinBox->minimum() != minZ) { + m_volumeIndicesZcoordSpinBox->setMinimum(minZ); + } + if (m_volumeIndicesZcoordSpinBox->maximum() != maxZ) { + m_volumeIndicesZcoordSpinBox->setMaximum(maxZ); + } int64_t slicesOne[3] = { 1, 1, 1 }; float slicesOneCoords[3]; @@ -373,7 +474,6 @@ BrainBrowserWindowToolBarSliceSelection::updateSliceIndicesAndCoordinatesRanges( m_volumeIndicesXcoordSpinBox->setValue(btc->getSliceCoordinateParasagittal()); m_volumeIndicesYcoordSpinBox->setValue(btc->getSliceCoordinateCoronal()); m_volumeIndicesZcoordSpinBox->setValue(btc->getSliceCoordinateAxial()); - } m_volumeIndicesWidgetGroup->blockAllSignals(blockedStatus); @@ -389,8 +489,7 @@ BrainBrowserWindowToolBarSliceSelection::volumeIndicesOriginActionTriggered() btc->setSlicesToOrigin(); updateContent(btc); - this->updateGraphicsWindow(); - this->updateOtherYokedWindows(); + this->updateGraphicsWindowAndYokedWindows(); } /** @@ -401,8 +500,7 @@ BrainBrowserWindowToolBarSliceSelection::volumeIndicesParasagittalCheckBoxStateC { BrowserTabContent* btc = this->getTabContentFromSelectedTab(); btc->setSliceParasagittalEnabled(m_volumeIndicesParasagittalCheckBox->isChecked()); - this->updateGraphicsWindow(); - this->updateOtherYokedWindows(); + this->updateGraphicsWindowAndYokedWindows(); } /** @@ -413,8 +511,7 @@ BrainBrowserWindowToolBarSliceSelection::volumeIndicesCoronalCheckBoxStateChange { BrowserTabContent* btc = this->getTabContentFromSelectedTab(); btc->setSliceCoronalEnabled(m_volumeIndicesCoronalCheckBox->isChecked()); - this->updateGraphicsWindow(); - this->updateOtherYokedWindows(); + this->updateGraphicsWindowAndYokedWindows(); } /** @@ -425,35 +522,47 @@ BrainBrowserWindowToolBarSliceSelection::volumeIndicesAxialCheckBoxStateChanged( { BrowserTabContent* btc = this->getTabContentFromSelectedTab(); btc->setSliceAxialEnabled(m_volumeIndicesAxialCheckBox->isChecked()); - this->updateGraphicsWindow(); - this->updateOtherYokedWindows(); + this->updateGraphicsWindowAndYokedWindows(); } /** * Called when volume indices parasagittal spin box value is changed. + * + * @param sliceIndex + * New index of slice. */ void -BrainBrowserWindowToolBarSliceSelection::volumeIndicesParasagittalSpinBoxValueChanged(int /*i*/) +BrainBrowserWindowToolBarSliceSelection::volumeIndicesParasagittalSpinBoxValueChanged(int sliceIndex) { - this->readVolumeSliceIndicesAndUpdateSliceCoordinates(); + this->readVolumeSliceIndicesAndUpdateSliceCoordinates(VolumeSliceViewPlaneEnum::PARASAGITTAL, + sliceIndex); } /** * Called when volume indices coronal spin box value is changed. + * + * @param sliceIndex + * New index of slice. */ void -BrainBrowserWindowToolBarSliceSelection::volumeIndicesCoronalSpinBoxValueChanged(int /*i*/) +BrainBrowserWindowToolBarSliceSelection::volumeIndicesCoronalSpinBoxValueChanged(int sliceIndex) { - this->readVolumeSliceIndicesAndUpdateSliceCoordinates(); + this->readVolumeSliceIndicesAndUpdateSliceCoordinates(VolumeSliceViewPlaneEnum::CORONAL, + sliceIndex); } /** * Called when volume indices axial spin box value is changed. + * + * @param sliceIndex + * New index of slice. */ void -BrainBrowserWindowToolBarSliceSelection::volumeIndicesAxialSpinBoxValueChanged(int /*i*/) +BrainBrowserWindowToolBarSliceSelection::volumeIndicesAxialSpinBoxValueChanged(int sliceIndex) { - this->readVolumeSliceIndicesAndUpdateSliceCoordinates(); + + this->readVolumeSliceIndicesAndUpdateSliceCoordinates(VolumeSliceViewPlaneEnum::AXIAL, + sliceIndex); } /** @@ -491,9 +600,15 @@ BrainBrowserWindowToolBarSliceSelection::volumeIndicesZcoordSpinBoxValueChanged( /** * Read the slice indices and update the slice coordinates. + * + * @param viewPlane + * View plane whose slice index was changed + * @param sliceIndex + * New slice index. */ void -BrainBrowserWindowToolBarSliceSelection::readVolumeSliceIndicesAndUpdateSliceCoordinates() +BrainBrowserWindowToolBarSliceSelection::readVolumeSliceIndicesAndUpdateSliceCoordinates(const VolumeSliceViewPlaneEnum::Enum viewPlane, + const int64_t sliceIndex) { BrowserTabContent* btc = this->getTabContentFromSelectedTab(); const int32_t tabIndex = btc->getTabNumber(); @@ -514,18 +629,54 @@ BrainBrowserWindowToolBarSliceSelection::readVolumeSliceIndicesAndUpdateSliceCoo } if (underlayVolumeFile != NULL) { - const int64_t parasagittalSlice = m_volumeIndicesParasagittalSpinBox->value(); - const int64_t coronalSlice = m_volumeIndicesCoronalSpinBox->value(); - const int64_t axialSlice = m_volumeIndicesAxialSpinBox->value(); - btc->setSliceIndexAxial(underlayVolumeFile, axialSlice); - btc->setSliceIndexCoronal(underlayVolumeFile, coronalSlice); - btc->setSliceIndexParasagittal(underlayVolumeFile, parasagittalSlice); + const VolumeSliceProjectionTypeEnum::Enum sliceProjectionType = m_volumeSliceProjectionTypeEnumComboBox->getSelectedItem(); + switch (sliceProjectionType) { + case VolumeSliceProjectionTypeEnum::VOLUME_SLICE_PROJECTION_OBLIQUE: + { + switch (viewPlane) { + case VolumeSliceViewPlaneEnum::ALL: + CaretAssert(0); + break; + case VolumeSliceViewPlaneEnum::AXIAL: + btc->setSliceIndexAxial(underlayVolumeFile, + sliceIndex); + break; + case VolumeSliceViewPlaneEnum::CORONAL: + btc->setSliceIndexCoronal(underlayVolumeFile, + sliceIndex); + break; + case VolumeSliceViewPlaneEnum::PARASAGITTAL: + btc->setSliceIndexParasagittal(underlayVolumeFile, + sliceIndex); + break; + } + } + break; + case VolumeSliceProjectionTypeEnum::VOLUME_SLICE_PROJECTION_ORTHOGONAL: + switch (viewPlane) { + case VolumeSliceViewPlaneEnum::ALL: + CaretAssert(0); + break; + case VolumeSliceViewPlaneEnum::AXIAL: + btc->setSliceIndexAxial(underlayVolumeFile, + sliceIndex); + break; + case VolumeSliceViewPlaneEnum::CORONAL: + btc->setSliceIndexCoronal(underlayVolumeFile, + sliceIndex); + break; + case VolumeSliceViewPlaneEnum::PARASAGITTAL: + btc->setSliceIndexParasagittal(underlayVolumeFile, + sliceIndex); + break; + } + break; + } } this->updateSliceIndicesAndCoordinatesRanges(); - this->updateGraphicsWindow(); - this->updateOtherYokedWindows(); + this->updateGraphicsWindowAndYokedWindows(); } /** @@ -555,9 +706,9 @@ BrainBrowserWindowToolBarSliceSelection::readVolumeSliceCoordinatesAndUpdateSlic if (underlayVolumeFile != NULL) { float sliceCoords[3] = { - m_volumeIndicesXcoordSpinBox->value(), - m_volumeIndicesYcoordSpinBox->value(), - m_volumeIndicesZcoordSpinBox->value() + (float)m_volumeIndicesXcoordSpinBox->value(), + (float)m_volumeIndicesYcoordSpinBox->value(), + (float)m_volumeIndicesZcoordSpinBox->value() }; btc->selectSlicesAtCoordinate(sliceCoords); @@ -565,8 +716,7 @@ BrainBrowserWindowToolBarSliceSelection::readVolumeSliceCoordinatesAndUpdateSlic this->updateSliceIndicesAndCoordinatesRanges(); - this->updateGraphicsWindow(); - this->updateOtherYokedWindows(); + this->updateGraphicsWindowAndYokedWindows(); } /** @@ -578,8 +728,8 @@ BrainBrowserWindowToolBarSliceSelection::volumeSliceProjectionTypeEnumComboBoxIt BrowserTabContent* btc = this->getTabContentFromSelectedTab(); const VolumeSliceProjectionTypeEnum::Enum sliceProjectionType = m_volumeSliceProjectionTypeEnumComboBox->getSelectedItem(); btc->setSliceProjectionType(sliceProjectionType); - this->updateGraphicsWindow(); - this->updateOtherYokedWindows(); + this->updateGraphicsWindowAndYokedWindows(); + updateObliqueMaskingButton(); EventManager::get()->sendEvent(EventUpdateVolumeEditingToolBar().getPointer()); } @@ -599,4 +749,84 @@ BrainBrowserWindowToolBarSliceSelection::volumeIdentificationToggled(bool value) browserTabContent->setIdentificationUpdatesVolumeSlices(value); } +/** + * Called when the oblique masking action is triggered. + */ +void +BrainBrowserWindowToolBarSliceSelection::obliqueMaskingActionTriggered(bool) +{ + BrowserTabContent* browserTabContent = this->getTabContentFromSelectedTab(); + if (browserTabContent == NULL) { + return; + } + std::vector allMaskEnums; + VolumeSliceInterpolationEdgeEffectsMaskingEnum::getAllEnums(allMaskEnums); + + QMenu obliqueMaskingMenu("Oblique Sampling"); + QActionGroup* maskActionGroup = new QActionGroup(this); + maskActionGroup->setExclusive(true); + QAction* selectedAction = NULL; + for (auto maskEnum : allMaskEnums) { + QAction* action = maskActionGroup->addAction(VolumeSliceInterpolationEdgeEffectsMaskingEnum::toGuiName(maskEnum)); + action->setCheckable(true); + action->setData(VolumeSliceInterpolationEdgeEffectsMaskingEnum::toIntegerCode(maskEnum)); + if (maskEnum == browserTabContent->getVolumeSliceInterpolationEdgeEffectsMaskingType()) { + selectedAction = action; + } + obliqueMaskingMenu.addAction(action); + } + if (selectedAction != NULL) { + selectedAction->setChecked(true); + } + + selectedAction = obliqueMaskingMenu.exec(QCursor::pos()); + if (selectedAction != NULL) { + const int32_t intValue = selectedAction->data().toInt(); + bool validFlag = false; + VolumeSliceInterpolationEdgeEffectsMaskingEnum::Enum maskType = VolumeSliceInterpolationEdgeEffectsMaskingEnum::fromIntegerCode(intValue, + &validFlag); + CaretAssert(validFlag); + browserTabContent->setVolumeSliceInterpolationEdgeEffectsMaskingType(maskType); + + this->updateGraphicsWindowAndYokedWindows(); + EventManager::get()->sendEvent(EventUpdateVolumeEditingToolBar().getPointer()); + } + + updateObliqueMaskingButton(); +} + +/** + * Update the oblique masking button so that it enabled only + * when oblique slice drawing is selected and it is "checked" + * when a masking is applied. + */ +void +BrainBrowserWindowToolBarSliceSelection::updateObliqueMaskingButton() +{ + BrowserTabContent* browserTabContent = this->getTabContentFromSelectedTab(); + if (browserTabContent == NULL) { + return; + } + + QSignalBlocker obliqueBlocker(m_obliqueMaskingAction); + switch (browserTabContent->getVolumeSliceInterpolationEdgeEffectsMaskingType()) { + case VolumeSliceInterpolationEdgeEffectsMaskingEnum::OFF: + m_obliqueMaskingAction->setChecked(false); + break; + case VolumeSliceInterpolationEdgeEffectsMaskingEnum::LOOSE: + case VolumeSliceInterpolationEdgeEffectsMaskingEnum::TIGHT: + m_obliqueMaskingAction->setChecked(true); + break; + } + + switch (browserTabContent->getSliceProjectionType()) { + case VolumeSliceProjectionTypeEnum::VOLUME_SLICE_PROJECTION_OBLIQUE: + m_obliqueMaskingAction->setEnabled(true); + break; + case VolumeSliceProjectionTypeEnum::VOLUME_SLICE_PROJECTION_ORTHOGONAL: + m_obliqueMaskingAction->setEnabled(false); + break; + } +} + diff --git a/src/GuiQt/BrainBrowserWindowToolBarSliceSelection.h b/src/GuiQt/BrainBrowserWindowToolBarSliceSelection.h index 472d4dd928f28630a063c16f570653e4f6da0d06..a1c7b966fb0920f4eccfc7efc5d1a815d1d08cb6 100644 --- a/src/GuiQt/BrainBrowserWindowToolBarSliceSelection.h +++ b/src/GuiQt/BrainBrowserWindowToolBarSliceSelection.h @@ -23,6 +23,7 @@ #include "BrainBrowserWindowToolBarComponent.h" +#include "VolumeSliceViewPlaneEnum.h" class QAction; class QCheckBox; @@ -52,14 +53,15 @@ namespace caret { void volumeIndicesParasagittalCheckBoxStateChanged(int state); void volumeIndicesCoronalCheckBoxStateChanged(int state); void volumeIndicesAxialCheckBoxStateChanged(int state); - void volumeIndicesParasagittalSpinBoxValueChanged(int i); - void volumeIndicesCoronalSpinBoxValueChanged(int i); - void volumeIndicesAxialSpinBoxValueChanged(int i); + void volumeIndicesParasagittalSpinBoxValueChanged(int sliceIndex); + void volumeIndicesCoronalSpinBoxValueChanged(int sliceIndex); + void volumeIndicesAxialSpinBoxValueChanged(int sliceIndex); void volumeIndicesXcoordSpinBoxValueChanged(double d); void volumeIndicesYcoordSpinBoxValueChanged(double d); void volumeIndicesZcoordSpinBoxValueChanged(double d); void volumeSliceProjectionTypeEnumComboBoxItemActivated(); void volumeIdentificationToggled(bool value); + void obliqueMaskingActionTriggered(bool); private: BrainBrowserWindowToolBarSliceSelection(const BrainBrowserWindowToolBarSliceSelection&); @@ -67,9 +69,12 @@ namespace caret { BrainBrowserWindowToolBarSliceSelection& operator=(const BrainBrowserWindowToolBarSliceSelection&); void readVolumeSliceCoordinatesAndUpdateSliceIndices(); - void readVolumeSliceIndicesAndUpdateSliceCoordinates(); + void readVolumeSliceIndicesAndUpdateSliceCoordinates(const VolumeSliceViewPlaneEnum::Enum viewPlane, + const int64_t sliceIndex); void updateSliceIndicesAndCoordinatesRanges(); + void updateObliqueMaskingButton(); + BrainBrowserWindowToolBar* m_parentToolBar; WuQWidgetObjectGroup* m_volumeIndicesWidgetGroup; @@ -84,6 +89,7 @@ namespace caret { QDoubleSpinBox* m_volumeIndicesXcoordSpinBox; QDoubleSpinBox* m_volumeIndicesYcoordSpinBox; QDoubleSpinBox* m_volumeIndicesZcoordSpinBox; + QAction* m_obliqueMaskingAction; EnumComboBoxTemplate* m_volumeSliceProjectionTypeEnumComboBox; // ADD_NEW_MEMBERS_HERE diff --git a/src/GuiQt/BrainBrowserWindowToolBarTab.cxx b/src/GuiQt/BrainBrowserWindowToolBarTab.cxx index 21bf0268f0b1cfe8e9b41598007c95e1643b52e2..d632cf1474e8dbe3ec71d9d257ca2bed22972221 100644 --- a/src/GuiQt/BrainBrowserWindowToolBarTab.cxx +++ b/src/GuiQt/BrainBrowserWindowToolBarTab.cxx @@ -24,8 +24,9 @@ #undef __BRAIN_BROWSER_WINDOW_TOOL_BAR_TAB_DECLARE__ #include -#include #include +#include +#include #include #include @@ -39,6 +40,8 @@ #include "WuQtUtilities.h" #include "WuQWidgetObjectGroup.h" #include "YokingGroupEnum.h" +#include "WuQDataEntryDialog.h" +#include "WuQtUtilities.h" using namespace caret; @@ -53,21 +56,18 @@ using namespace caret; * * @param browserWindowIndex * Index of browser window. - * @param windowAspectRatioLockedAction - * Action for locking window aspect ratio. - * @param tabAspectRatioLockedAction - * Action for locking tab aspect ratio. + * @param toolBarLockWindowAndAllTabAspectRatioButton + * Button for locking window and all tab aspect ratio. * @param parentToolBar * Parent toolbar. */ BrainBrowserWindowToolBarTab::BrainBrowserWindowToolBarTab(const int32_t browserWindowIndex, - QAction* windowAspectRatioLockedAction, - QAction* tabAspectRatioLockedAction, + QToolButton* toolBarLockWindowAndAllTabAspectRatioButton, BrainBrowserWindowToolBar* parentToolBar) : BrainBrowserWindowToolBarComponent(parentToolBar), m_browserWindowIndex(browserWindowIndex), m_parentToolBar(parentToolBar), -m_tabAspectRatioLockedAction(tabAspectRatioLockedAction) +m_lockWindowAndAllTabAspectButton(toolBarLockWindowAndAllTabAspectRatioButton) { m_yokingGroupComboBox = new EnumComboBoxTemplate(this); m_yokingGroupComboBox->setup(); @@ -77,26 +77,28 @@ m_tabAspectRatioLockedAction(tabAspectRatioLockedAction) "Surface Yoking is applied to Surface, Surface Montage\n" "and Whole Brain. Volume Yoking is applied to Volumes.")); - QLabel* yokeToLabel = new QLabel("Yoking:"); + m_yokeToLabel = new QLabel("Yoking:"); QObject::connect(m_yokingGroupComboBox, SIGNAL(itemActivated()), this, SLOT(yokeToGroupComboBoxIndexChanged())); - QToolButton* windowAspectRatioLockedToolButton = new QToolButton(); - windowAspectRatioLockedToolButton->setDefaultAction(windowAspectRatioLockedAction); - - QToolButton* tabAspectRatioLockedToolButton = new QToolButton(); - tabAspectRatioLockedToolButton->setDefaultAction(tabAspectRatioLockedAction); + const AString lightToolTip = WuQtUtilities::createWordWrappedToolTipText("Enables shading on surfaces. Shading affects " + "palette colors (but not by much) and in rare circumstances it " + "may be helpful to turn shading off."); + m_lightingEnabledCheckBox = new QCheckBox("Shading"); + m_lightingEnabledCheckBox->setToolTip(lightToolTip); + QObject::connect(m_lightingEnabledCheckBox, &QCheckBox::toggled, + this, &BrainBrowserWindowToolBarTab::lightingEnabledCheckBoxToggled); QVBoxLayout* layout = new QVBoxLayout(this); WuQtUtilities::setLayoutSpacingAndMargins(layout, 4, 0); - layout->addWidget(yokeToLabel); + layout->addWidget(m_yokeToLabel); layout->addWidget(m_yokingGroupComboBox->getWidget()); - layout->addSpacing(15); - layout->addWidget(windowAspectRatioLockedToolButton); - layout->addWidget(tabAspectRatioLockedToolButton); + layout->addWidget(m_lockWindowAndAllTabAspectButton); + layout->addWidget(m_lightingEnabledCheckBox); - addToWidgetGroup(yokeToLabel); + addToWidgetGroup(m_yokeToLabel); addToWidgetGroup(m_yokingGroupComboBox->getWidget()); + addToWidgetGroup(m_lightingEnabledCheckBox); } /** @@ -115,16 +117,64 @@ BrainBrowserWindowToolBarTab::~BrainBrowserWindowToolBarTab() void BrainBrowserWindowToolBarTab::updateContent(BrowserTabContent* browserTabContent) { + if (browserTabContent == NULL) { + return; + } + blockAllSignals(true); - m_yokingGroupComboBox->setSelectedItem(browserTabContent->getYokingGroup()); - m_tabAspectRatioLockedAction->blockSignals(true); - m_tabAspectRatioLockedAction->setChecked(browserTabContent->isAspectRatioLocked()); - m_tabAspectRatioLockedAction->blockSignals(false); + bool chartFlag = false; + switch (browserTabContent->getSelectedModelType()) { + case ModelTypeEnum::MODEL_TYPE_CHART: + chartFlag = true; + break; + case ModelTypeEnum::MODEL_TYPE_CHART_TWO: + chartFlag = true; + break; + case ModelTypeEnum::MODEL_TYPE_INVALID: + break; + case ModelTypeEnum::MODEL_TYPE_SURFACE: + break; + case ModelTypeEnum::MODEL_TYPE_SURFACE_MONTAGE: + break; + case ModelTypeEnum::MODEL_TYPE_VOLUME_SLICES: + break; + case ModelTypeEnum::MODEL_TYPE_WHOLE_BRAIN: + break; + } + + if (chartFlag) { + m_yokeToLabel->setText("Chart Yoking:"); + m_yokingGroupComboBox->setSelectedItem(browserTabContent->getChartModelYokingGroup()); + } + else { + m_yokeToLabel->setText("Yoking:"); + m_yokingGroupComboBox->setSelectedItem(browserTabContent->getBrainModelYokingGroup()); + } + + m_lightingEnabledCheckBox->setChecked(browserTabContent->isLightingEnabled()); blockAllSignals(false); } +/** + * Called when lighting checkbox is toggled by user + * + * @param checked + * New status of lighting. + */ +void +BrainBrowserWindowToolBarTab::lightingEnabledCheckBoxToggled(bool checked) +{ + BrowserTabContent* browserTabContent = this->getTabContentFromSelectedTab(); + if (browserTabContent == NULL) { + return; + } + + browserTabContent->setLightingEnabled(checked); + this->updateGraphicsWindow(); +} + /** * Called when window yoke to tab combo box is selected. */ @@ -136,8 +186,33 @@ BrainBrowserWindowToolBarTab::yokeToGroupComboBoxIndexChanged() return; } + bool chartFlag = false; + switch (browserTabContent->getSelectedModelType()) { + case ModelTypeEnum::MODEL_TYPE_CHART: + chartFlag = true; + break; + case ModelTypeEnum::MODEL_TYPE_CHART_TWO: + chartFlag = true; + break; + case ModelTypeEnum::MODEL_TYPE_INVALID: + break; + case ModelTypeEnum::MODEL_TYPE_SURFACE: + break; + case ModelTypeEnum::MODEL_TYPE_SURFACE_MONTAGE: + break; + case ModelTypeEnum::MODEL_TYPE_VOLUME_SLICES: + break; + case ModelTypeEnum::MODEL_TYPE_WHOLE_BRAIN: + break; + } + YokingGroupEnum::Enum yokingGroup = m_yokingGroupComboBox->getSelectedItem(); - browserTabContent->setYokingGroup(yokingGroup); + if (chartFlag) { + browserTabContent->setChartModelYokingGroup(yokingGroup); + } + else { + browserTabContent->setBrainModelYokingGroup(yokingGroup); + } m_parentToolBar->updateToolBarComponents(browserTabContent); diff --git a/src/GuiQt/BrainBrowserWindowToolBarTab.h b/src/GuiQt/BrainBrowserWindowToolBarTab.h index 0c11823a9ea3c5dad3fc079676e41a0b11099107..4b959553721b9551b425a37313995372fe198f6d 100644 --- a/src/GuiQt/BrainBrowserWindowToolBarTab.h +++ b/src/GuiQt/BrainBrowserWindowToolBarTab.h @@ -25,6 +25,8 @@ #include "BrainBrowserWindowToolBarComponent.h" +class QCheckBox; +class QLabel; class QToolButton; namespace caret { @@ -36,8 +38,7 @@ namespace caret { public: BrainBrowserWindowToolBarTab(const int32_t browserWindowIndex, - QAction* windowAspectRatioLockedAction, - QAction* tabAspectRatioLockedAction, + QToolButton* toolBarLockWindowAndAllTabAspectRatioButton, BrainBrowserWindowToolBar* parentToolBar); virtual ~BrainBrowserWindowToolBarTab(); @@ -50,18 +51,24 @@ namespace caret { private slots: void yokeToGroupComboBoxIndexChanged(); + void lightingEnabledCheckBoxToggled(bool checked); + private: BrainBrowserWindowToolBarTab(const BrainBrowserWindowToolBarTab&); BrainBrowserWindowToolBarTab& operator=(const BrainBrowserWindowToolBarTab&); + QLabel* m_yokeToLabel; + EnumComboBoxTemplate* m_yokingGroupComboBox; const int32_t m_browserWindowIndex; BrainBrowserWindowToolBar* m_parentToolBar; - QAction* m_tabAspectRatioLockedAction; + QToolButton* m_lockWindowAndAllTabAspectButton; + + QCheckBox* m_lightingEnabledCheckBox; // ADD_NEW_MEMBERS_HERE diff --git a/src/GuiQt/BrainBrowserWindowToolBarTabPopUpMenu.cxx b/src/GuiQt/BrainBrowserWindowToolBarTabPopUpMenu.cxx new file mode 100644 index 0000000000000000000000000000000000000000..5433697d3f34e8e81ce9ea338c1e7030a3c06e4c --- /dev/null +++ b/src/GuiQt/BrainBrowserWindowToolBarTabPopUpMenu.cxx @@ -0,0 +1,327 @@ + +/*LICENSE_START*/ +/* + * Copyright (C) 2017 Washington University School of Medicine + * + * 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 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ +/*LICENSE_END*/ + +#define __BRAIN_BROWSER_WINDOW_TOOL_BAR_TAB_POP_UP_MENU_DECLARE__ +#include "BrainBrowserWindowToolBarTabPopUpMenu.h" +#undef __BRAIN_BROWSER_WINDOW_TOOL_BAR_TAB_POP_UP_MENU_DECLARE__ + +#include "BrainBrowserWindowToolBar.h" +#include "CaretAssert.h" +using namespace caret; + + + +/** + * \class caret::BrainBrowserWindowToolBarTabPopUpMenu + * \brief Pop up (context-sensitive) menu for tab in toolbar to add new tabs + * \ingroup GuiQt + * + * This menu is displayed when the user performs a right-click + * with the mouse over a tab in the brain browswer window toolbar. + * The menu allows users to add/duplicate/remove tabs. + */ + +/** + * Constructor. + * + * @param toolBar + * The toolbar. + * @param tabIndexUnderMouse + * Index of tab over which mouse was clicked + * @param parent + * Optional parent widget. + */ +BrainBrowserWindowToolBarTabPopUpMenu::BrainBrowserWindowToolBarTabPopUpMenu(BrainBrowserWindowToolBar* toolBar, + const int32_t tabIndexUnderMouse, + QWidget* parent) +: QMenu(parent), +m_toolBar(toolBar), +m_tabIndexUnderMouse(tabIndexUnderMouse) +{ + CaretAssert(m_toolBar); + CaretAssert(m_toolBar->tabBar); + + m_numberOfTabs = m_toolBar->tabBar->count(); + m_activeTabIndex = m_toolBar->tabBar->currentIndex(); + + addItem(MenuItem::CREATE_NEW_TAB_BEFORE); + addItem(MenuItem::CREATE_NEW_TAB_AFTER); + addSeparator(); + addItem(MenuItem::DUPLICATE_TAB_AT_BEGINNING); + addItem(MenuItem::DUPLICATE_TAB_BEFORE); + addItem(MenuItem::DUPLICATE_TAB_AFTER); + addItem(MenuItem::DUPLICATE_TAB_AT_END); + addSeparator(); + addItem(MenuItem::MOVE_TAB_TO_BEGINNING); + addItem(MenuItem::MOVE_TAB_BEFORE); + addItem(MenuItem::MOVE_TAB_AFTER); + addItem(MenuItem::MOVE_TAB_TO_END); + addSeparator(); + addItem(MenuItem::DELETE_TAB); + + QObject::connect(this, &QMenu::triggered, + this, &BrainBrowserWindowToolBarTabPopUpMenu::menuItemSelected); +} + +/** + * Destructor. + */ +BrainBrowserWindowToolBarTabPopUpMenu::~BrainBrowserWindowToolBarTabPopUpMenu() +{ +} + +/** + * Add an item to the menu. + * + * @param menuItem + * Item to be added to menu. + */ +void +BrainBrowserWindowToolBarTabPopUpMenu::addItem(const MenuItem menuItem) +{ + + QString thisTabName = "This Tab"; + QString activeTabName = "Active Tab"; + + const bool includeNamesInMenuFlag = false; + if (includeNamesInMenuFlag) { + if (m_tabIndexUnderMouse >= 0) { + thisTabName = ("Tab \"" + m_toolBar->tabBar->tabText(m_tabIndexUnderMouse) + "\""); + } + const int32_t activeTabIndex = m_toolBar->tabBar->currentIndex(); + if (activeTabIndex >= 0) { + activeTabName = ("Tab \"" + m_toolBar->tabBar->tabText(activeTabIndex) + "\""); + } + } + + QString text; + switch (menuItem) { + case MenuItem::NONE: + CaretAssert(0); + break; + case MenuItem::CREATE_NEW_TAB_BEFORE: + text = "Create New Tab Before " + thisTabName; + break; + case MenuItem::CREATE_NEW_TAB_AFTER: + text = "Create New Tab After " + thisTabName; + break; + case MenuItem::DUPLICATE_TAB_AT_BEGINNING: + text = "Duplicate " + thisTabName + " at Beginning"; + break; + case MenuItem::DUPLICATE_TAB_BEFORE: + text = "Duplicate " + thisTabName + " Before " + activeTabName; + break; + case MenuItem::DUPLICATE_TAB_AFTER: + text = "Duplicate " + thisTabName + " After " + activeTabName; + break; + case MenuItem::DUPLICATE_TAB_AT_END: + text = "Duplicate " + thisTabName + " at End"; + break; + case MenuItem::MOVE_TAB_TO_BEGINNING: + text = "Move " + thisTabName + " to Beginning"; + break; + case MenuItem::MOVE_TAB_BEFORE: + text = "Move " + thisTabName + " to Before " + activeTabName; + break; + case MenuItem::MOVE_TAB_AFTER: + text = "Move " + thisTabName + " to After " + activeTabName; + break; + case MenuItem::MOVE_TAB_TO_END: + text = "Move " + thisTabName + " to End"; + break; + case MenuItem::DELETE_TAB: + text = "Delete " + thisTabName; + break; + } + + QAction* action = addAction(text); + action->setData(static_cast(menuItem)); + action->setEnabled(isEnabled(menuItem)); +} + +/** + * Called when a menu item is selected. + * + * @param action + * Action for item in menu that was selected. + */ +void +BrainBrowserWindowToolBarTabPopUpMenu::menuItemSelected(QAction* action) +{ + if (action != NULL) { + const BrowserTabContent* activeTabContent = m_toolBar->getTabContentFromTab(m_activeTabIndex); + + MenuItem menuItem = static_cast(action->data().toInt()); + + CaretAssert(isEnabled(menuItem)); + + int32_t createNewIndex = -1; + int32_t deleteIndex = -1; + int32_t duplicateToIndex = -1; + int32_t moveToIndex = -1; + + switch (menuItem) { + case MenuItem::NONE: + CaretAssert(0); + break; + case MenuItem::CREATE_NEW_TAB_BEFORE: + createNewIndex = m_tabIndexUnderMouse; + break; + case MenuItem::CREATE_NEW_TAB_AFTER: + createNewIndex = m_tabIndexUnderMouse + 1; + break; + case MenuItem::DUPLICATE_TAB_AT_BEGINNING: + duplicateToIndex = 0; + break; + case MenuItem::DUPLICATE_TAB_BEFORE: + duplicateToIndex = m_activeTabIndex; + break; + case MenuItem::DUPLICATE_TAB_AFTER: + duplicateToIndex = m_activeTabIndex + 1; + break; + case MenuItem::DUPLICATE_TAB_AT_END: + duplicateToIndex = m_numberOfTabs; + break; + case MenuItem::MOVE_TAB_TO_BEGINNING: + moveToIndex = 0; + break; + case MenuItem::MOVE_TAB_BEFORE: + if (m_tabIndexUnderMouse < m_activeTabIndex) { + moveToIndex = m_activeTabIndex - 1; + } + else { + moveToIndex = m_activeTabIndex; + } + break; + case MenuItem::MOVE_TAB_AFTER: + if (m_tabIndexUnderMouse < m_activeTabIndex) { + moveToIndex = m_activeTabIndex; + } + else { + moveToIndex = m_activeTabIndex + 1; + } + break; + case MenuItem::MOVE_TAB_TO_END: + moveToIndex = m_numberOfTabs - 1; + break; + case MenuItem::DELETE_TAB: + deleteIndex = m_tabIndexUnderMouse; + break; + } + + if (createNewIndex >= 0) { + m_toolBar->insertNewTabAtTabBarIndex(createNewIndex); + } + else if (deleteIndex >= 0) { + m_toolBar->tabCloseSelected(deleteIndex); + } + else if (duplicateToIndex >= 0) { + BrowserTabContent* tabContent = m_toolBar->getTabContentFromTab(m_tabIndexUnderMouse); + CaretAssert(tabContent); + m_toolBar->insertAndCloneTabContentAtTabBarIndex(tabContent, duplicateToIndex); + } + else if (moveToIndex >= 0) { + if (moveToIndex != m_tabIndexUnderMouse) { + m_toolBar->tabBar->moveTab(m_tabIndexUnderMouse, moveToIndex); + } + } + + /* + * Ensure active tab remains active. Note that number of tabs may have changed. + */ + int32_t updatedActiveTabIndex = -1; + const int32_t numTabs = m_toolBar->tabBar->count(); + for (int32_t iTab = 0; iTab < numTabs; iTab++) { + if (activeTabContent == m_toolBar->getTabContentFromTab(iTab)) { + updatedActiveTabIndex = iTab; + break; + } + } + if (updatedActiveTabIndex >= 0) { + m_toolBar->tabBar->setCurrentIndex(updatedActiveTabIndex); + } + } +} + +/** + * @return True if the menu item is enabled. + * Items are enabled if action would do nothing such as + * moving a tab to itself. + * + * @param item + * Item for testing enabled. + */ +bool +BrainBrowserWindowToolBarTabPopUpMenu::isEnabled(const MenuItem menuItem) const +{ + bool enabledFlag = false; + + BrowserTabContent* tabContent = m_toolBar->getTabContentFromTab(m_tabIndexUnderMouse); + + switch (menuItem) { + case MenuItem::NONE: + CaretAssert(0); + break; + case MenuItem::CREATE_NEW_TAB_BEFORE: + enabledFlag = true; + break; + case MenuItem::CREATE_NEW_TAB_AFTER: + enabledFlag = true; + break; + case MenuItem::DUPLICATE_TAB_AT_BEGINNING: + case MenuItem::DUPLICATE_TAB_BEFORE: + case MenuItem::DUPLICATE_TAB_AFTER: + case MenuItem::DUPLICATE_TAB_AT_END: + if (tabContent != NULL) { + enabledFlag = true; + } + break; + case MenuItem::MOVE_TAB_TO_BEGINNING: + if (m_tabIndexUnderMouse > 0) { + enabledFlag = true; + } + break; + case MenuItem::MOVE_TAB_BEFORE: + if ((m_tabIndexUnderMouse != m_activeTabIndex) + && (m_tabIndexUnderMouse != (m_activeTabIndex - 1))) { + enabledFlag = true; + } + break; + case MenuItem::MOVE_TAB_AFTER: + if ((m_tabIndexUnderMouse != m_activeTabIndex) + && (m_tabIndexUnderMouse != (m_activeTabIndex + 1))) { + enabledFlag = true; + } + break; + case MenuItem::MOVE_TAB_TO_END: + if (m_tabIndexUnderMouse != (m_numberOfTabs - 1)) { + enabledFlag = true; + } + break; + case MenuItem::DELETE_TAB: + enabledFlag = true; + break; + } + + return enabledFlag; +} + + diff --git a/src/GuiQt/BrainBrowserWindowToolBarTabPopUpMenu.h b/src/GuiQt/BrainBrowserWindowToolBarTabPopUpMenu.h new file mode 100644 index 0000000000000000000000000000000000000000..60ed6a911b23264eabc1f0e433e037e69edbd6c1 --- /dev/null +++ b/src/GuiQt/BrainBrowserWindowToolBarTabPopUpMenu.h @@ -0,0 +1,91 @@ +#ifndef __BRAIN_BROWSER_WINDOW_TOOL_BAR_TAB_POP_UP_MENU_H__ +#define __BRAIN_BROWSER_WINDOW_TOOL_BAR_TAB_POP_UP_MENU_H__ + +/*LICENSE_START*/ +/* + * Copyright (C) 2017 Washington University School of Medicine + * + * 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 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ +/*LICENSE_END*/ + +#include + +#include + + + +namespace caret { + + class BrainBrowserWindowToolBar; + + class BrainBrowserWindowToolBarTabPopUpMenu : public QMenu { + Q_OBJECT + + public: + BrainBrowserWindowToolBarTabPopUpMenu(BrainBrowserWindowToolBar* toolBar, + const int32_t tabIndexUnderMouse, + QWidget* parent = 0); + + virtual ~BrainBrowserWindowToolBarTabPopUpMenu(); + + + // ADD_NEW_METHODS_HERE + + private slots: + void menuItemSelected(QAction* action); + + private: + enum class MenuItem { + NONE, + CREATE_NEW_TAB_BEFORE, + CREATE_NEW_TAB_AFTER, + DUPLICATE_TAB_AT_BEGINNING, + DUPLICATE_TAB_BEFORE, + DUPLICATE_TAB_AFTER, + DUPLICATE_TAB_AT_END, + MOVE_TAB_TO_BEGINNING, + MOVE_TAB_BEFORE, + MOVE_TAB_AFTER, + MOVE_TAB_TO_END, + DELETE_TAB + }; + + BrainBrowserWindowToolBarTabPopUpMenu(const BrainBrowserWindowToolBarTabPopUpMenu&); + + BrainBrowserWindowToolBarTabPopUpMenu& operator=(const BrainBrowserWindowToolBarTabPopUpMenu&); + + void addItem(const MenuItem menuItem); + + bool isEnabled(const MenuItem menuItem) const; + + BrainBrowserWindowToolBar* m_toolBar; + + int32_t m_tabIndexUnderMouse; + + int32_t m_numberOfTabs; + + int32_t m_activeTabIndex; + + // ADD_NEW_MEMBERS_HERE + + }; + +#ifdef __BRAIN_BROWSER_WINDOW_TOOL_BAR_TAB_POP_UP_MENU_DECLARE__ + // +#endif // __BRAIN_BROWSER_WINDOW_TOOL_BAR_TAB_POP_UP_MENU_DECLARE__ + +} // namespace +#endif //__BRAIN_BROWSER_WINDOW_TOOL_BAR_TAB_POP_UP_MENU_H__ diff --git a/src/GuiQt/BrainBrowserWindowToolBarVolumeMontage.cxx b/src/GuiQt/BrainBrowserWindowToolBarVolumeMontage.cxx index d5f9d8127e8b5b23d5ab53d682fed7236b2295e7..c4ad51df164a92aaf73eedca7ca97e2195195b5a 100644 --- a/src/GuiQt/BrainBrowserWindowToolBarVolumeMontage.cxx +++ b/src/GuiQt/BrainBrowserWindowToolBarVolumeMontage.cxx @@ -24,6 +24,7 @@ #undef __BRAIN_BROWSER_WINDOW_TOOL_BAR_VOLUME_MONTAGE_DECLARE__ #include +#include #include #include #include @@ -43,8 +44,6 @@ using namespace caret; * \class caret::BrainBrowserWindowToolBarVolumeMontage * \brief Toolbar component for volume montage slice selections * \ingroup GuiQt - * - * */ /** @@ -54,36 +53,56 @@ BrainBrowserWindowToolBarVolumeMontage::BrainBrowserWindowToolBarVolumeMontage(B : BrainBrowserWindowToolBarComponent(parentToolBar), m_parentToolBar(parentToolBar) { - QLabel* rowsLabel = new QLabel("Rows:"); - QLabel* columnsLabel = new QLabel("Cols:"); - QLabel* spacingLabel = new QLabel("Step:"); const int spinBoxWidth = 48; + QLabel* rowsLabel = new QLabel("Rows:"); + rowsLabel->setToolTip("Select the number of rows in montage of volume slices"); m_montageRowsSpinBox = WuQFactory::newSpinBox(); m_montageRowsSpinBox->setRange(1, 20); m_montageRowsSpinBox->setMaximumWidth(spinBoxWidth); - WuQtUtilities::setToolTipAndStatusTip(m_montageRowsSpinBox, - "Select the number of rows in montage of volume slices"); + m_montageRowsSpinBox->setToolTip(rowsLabel->toolTip()); QObject::connect(m_montageRowsSpinBox, SIGNAL(valueChanged(int)), this, SLOT(montageRowsSpinBoxValueChanged(int))); + QLabel* columnsLabel = new QLabel("Cols:"); + columnsLabel->setToolTip("Select the number of columns in montage of volume slices"); m_montageColumnsSpinBox = WuQFactory::newSpinBox(); m_montageColumnsSpinBox->setRange(1, 20); m_montageColumnsSpinBox->setMaximumWidth(spinBoxWidth); - WuQtUtilities::setToolTipAndStatusTip(m_montageColumnsSpinBox, - "Select the number of columns in montage of volume slices"); + m_montageColumnsSpinBox->setToolTip(columnsLabel->toolTip()); QObject::connect(m_montageColumnsSpinBox, SIGNAL(valueChanged(int)), this, SLOT(montageColumnsSpinBoxValueChanged(int))); + QLabel* spacingLabel = new QLabel("Step:"); + spacingLabel->setToolTip("Select the number of slices stepped (incremented) between displayed montage slices"); m_montageSpacingSpinBox = WuQFactory::newSpinBox(); m_montageSpacingSpinBox->setRange(1, 2500); m_montageSpacingSpinBox->setMaximumWidth(spinBoxWidth); - WuQtUtilities::setToolTipAndStatusTip(m_montageSpacingSpinBox, - "Select the number of slices stepped (incremented) between displayed montage slices"); + m_montageSpacingSpinBox->setToolTip(spacingLabel->toolTip()); QObject::connect(m_montageSpacingSpinBox, SIGNAL(valueChanged(int)), this, SLOT(montageSpacingSpinBoxValueChanged(int))); + m_showSliceCoordinateAction = new QAction("XYZ"); + m_showSliceCoordinateAction->setText("XYZ"); + m_showSliceCoordinateAction->setCheckable(true); + m_showSliceCoordinateAction->setToolTip("Show coordinates on slices"); + QObject::connect(m_showSliceCoordinateAction, &QAction::triggered, + this, &BrainBrowserWindowToolBarVolumeMontage::showSliceCoordinateToolButtonClicked); + + QToolButton* showSliceCoordToolButton = new QToolButton; + showSliceCoordToolButton->setDefaultAction(m_showSliceCoordinateAction); + WuQtUtilities::setToolButtonStyleForQt5Mac(showSliceCoordToolButton); + + QLabel* decimalsLabel = new QLabel("Prec:"); + decimalsLabel->setToolTip("Digits right of decimal in slice coordinates"); + m_sliceCoordinatePrecisionSpinBox = WuQFactory::newSpinBox(); + m_sliceCoordinatePrecisionSpinBox->setRange(0, 10); + m_sliceCoordinatePrecisionSpinBox->setMaximumWidth(spinBoxWidth); + m_sliceCoordinatePrecisionSpinBox->setToolTip(decimalsLabel->toolTip()); + QObject::connect(m_sliceCoordinatePrecisionSpinBox, static_cast(&QSpinBox::valueChanged), + this, &BrainBrowserWindowToolBarVolumeMontage::slicePrecisionSpinBoxValueChanged); + m_montageEnabledAction = WuQtUtilities::createAction("On", "View a montage of parallel slices", @@ -93,7 +112,8 @@ m_parentToolBar(parentToolBar) m_montageEnabledAction->setCheckable(true); QToolButton* montageEnabledToolButton = new QToolButton(); montageEnabledToolButton->setDefaultAction(m_montageEnabledAction); - + WuQtUtilities::setToolButtonStyleForQt5Mac(montageEnabledToolButton); + QGridLayout* gridLayout = new QGridLayout(this); WuQtUtilities::setLayoutSpacingAndMargins(gridLayout, 0, 0); gridLayout->setVerticalSpacing(2); @@ -103,13 +123,19 @@ m_parentToolBar(parentToolBar) gridLayout->addWidget(m_montageColumnsSpinBox, 1, 1); gridLayout->addWidget(spacingLabel, 2, 0); gridLayout->addWidget(m_montageSpacingSpinBox, 2, 1); - gridLayout->addWidget(montageEnabledToolButton, 3, 0, 1, 2, Qt::AlignHCenter); + gridLayout->addWidget(decimalsLabel, 3, 0); + gridLayout->addWidget(m_sliceCoordinatePrecisionSpinBox, 3, 1); + gridLayout->addWidget(showSliceCoordToolButton, 4, 0); + gridLayout->addWidget(montageEnabledToolButton, 4, 1); +// gridLayout->addWidget(montageEnabledToolButton, 4, 0, 1, 2, Qt::AlignHCenter); m_volumeMontageWidgetGroup = new WuQWidgetObjectGroup(this); m_volumeMontageWidgetGroup->add(m_montageRowsSpinBox); m_volumeMontageWidgetGroup->add(m_montageColumnsSpinBox); m_volumeMontageWidgetGroup->add(m_montageSpacingSpinBox); m_volumeMontageWidgetGroup->add(m_montageEnabledAction); + m_volumeMontageWidgetGroup->add(m_showSliceCoordinateAction); + m_volumeMontageWidgetGroup->add(m_sliceCoordinatePrecisionSpinBox); } /** @@ -142,6 +168,9 @@ BrainBrowserWindowToolBarVolumeMontage::updateContent(BrowserTabContent* browser m_montageColumnsSpinBox->setValue(browserTabContent->getMontageNumberOfColumns()); m_montageSpacingSpinBox->setValue(browserTabContent->getMontageSliceSpacing()); + m_showSliceCoordinateAction->setChecked(browserTabContent->isVolumeMontageAxesCoordinatesDisplayed()); + m_sliceCoordinatePrecisionSpinBox->setValue(browserTabContent->getVolumeMontageCoordinatePrecision()); + m_volumeMontageWidgetGroup->blockAllSignals(false); } @@ -161,8 +190,7 @@ BrainBrowserWindowToolBarVolumeMontage::montageEnabledActionToggled(bool) btc->setSliceDrawingType(drawingType); - this->updateGraphicsWindow(); - this->updateOtherYokedWindows(); + this->updateGraphicsWindowAndYokedWindows(); } @@ -176,8 +204,7 @@ BrainBrowserWindowToolBarVolumeMontage::montageRowsSpinBoxValueChanged(int /*i*/ btc->setMontageNumberOfRows(m_montageRowsSpinBox->value()); - this->updateGraphicsWindow(); - this->updateOtherYokedWindows(); + this->updateGraphicsWindowAndYokedWindows(); } /** @@ -189,8 +216,7 @@ BrainBrowserWindowToolBarVolumeMontage::montageColumnsSpinBoxValueChanged(int /* BrowserTabContent* btc = this->getTabContentFromSelectedTab(); btc->setMontageNumberOfColumns(m_montageColumnsSpinBox->value()); - this->updateGraphicsWindow(); - this->updateOtherYokedWindows(); + this->updateGraphicsWindowAndYokedWindows(); } /** @@ -203,7 +229,39 @@ BrainBrowserWindowToolBarVolumeMontage::montageSpacingSpinBoxValueChanged(int /* btc->setMontageSliceSpacing(m_montageSpacingSpinBox->value()); - this->updateGraphicsWindow(); - this->updateOtherYokedWindows(); + this->updateGraphicsWindowAndYokedWindows(); +} + +/** + * Called when show slice coordinate tool button is clicked + * + * @param checked + * New checked status + */ +void +BrainBrowserWindowToolBarVolumeMontage::showSliceCoordinateToolButtonClicked(bool checked) +{ + BrowserTabContent* btc = this->getTabContentFromSelectedTab(); + + btc->setVolumeMontageAxesCoordinatesDisplayed(checked); + + this->updateGraphicsWindowAndYokedWindows(); +} + + +/** + * Called when montage slice precision spin box value is changed. + * + * @param value + * New value + */ +void +BrainBrowserWindowToolBarVolumeMontage::slicePrecisionSpinBoxValueChanged(int value) +{ + BrowserTabContent* btc = this->getTabContentFromSelectedTab(); + + btc->setVolumeMontageCoordinatePrecision(value); + + this->updateGraphicsWindowAndYokedWindows(); } diff --git a/src/GuiQt/BrainBrowserWindowToolBarVolumeMontage.h b/src/GuiQt/BrainBrowserWindowToolBarVolumeMontage.h index ad1d0d7267baa9dae70e83fcf7c75169ad6920c0..d0ce2fcc1ec8a29d3ef51aa584c87e217117b06d 100644 --- a/src/GuiQt/BrainBrowserWindowToolBarVolumeMontage.h +++ b/src/GuiQt/BrainBrowserWindowToolBarVolumeMontage.h @@ -24,6 +24,8 @@ #include "BrainBrowserWindowToolBarComponent.h" +class QAction; +class QCheckBox; class QSpinBox; namespace caret { @@ -48,6 +50,9 @@ namespace caret { void montageEnabledActionToggled(bool); + void showSliceCoordinateToolButtonClicked(bool); + void slicePrecisionSpinBoxValueChanged(int); + private: BrainBrowserWindowToolBarVolumeMontage(const BrainBrowserWindowToolBarVolumeMontage&); @@ -59,6 +64,9 @@ namespace caret { QSpinBox* m_montageColumnsSpinBox; QSpinBox* m_montageSpacingSpinBox; + QAction* m_showSliceCoordinateAction; + QSpinBox* m_sliceCoordinatePrecisionSpinBox; + QAction* m_montageEnabledAction; WuQWidgetObjectGroup* m_volumeMontageWidgetGroup; diff --git a/src/GuiQt/BrainOpenGLWidget.cxx b/src/GuiQt/BrainOpenGLWidget.cxx index fe2cc2509a0a188ad79d48287ea59f0bf3cf70f5..456604ba7ad730b1e4021decc7090513009a8cf8 100644 --- a/src/GuiQt/BrainOpenGLWidget.cxx +++ b/src/GuiQt/BrainOpenGLWidget.cxx @@ -18,25 +18,27 @@ */ /*LICENSE_END*/ +#define __BRAIN_OPENGL_WIDGET_DEFINE__ +#include "BrainOpenGLWidget.h" +#undef __BRAIN_OPENGL_WIDGET_DEFINE__ + #include #include #include #include #include +#ifdef WORKBENCH_USE_QT5_QOPENGL_WIDGET +#endif +#include #include #include -#define __BRAIN_OPENGL_WIDGET_DEFINE__ -#include "BrainOpenGLWidget.h" -#undef __BRAIN_OPENGL_WIDGET_DEFINE__ - #include "Border.h" #include "Brain.h" #include "BrainBrowserWindow.h" #include "BrainOpenGLFixedPipeline.h" #include "BrainOpenGLShape.h" -#include "BrainOpenGLTextureManager.h" #include "BrainOpenGLViewportContent.h" #include "BrainStructure.h" #include "BrowserTabContent.h" @@ -49,14 +51,12 @@ #include "EventImageCapture.h" #include "EventModelGetAll.h" #include "EventManager.h" -#include "EventBrowserWindowContentGet.h" +#include "EventBrowserWindowDrawingContent.h" #include "EventBrowserWindowGraphicsRedrawn.h" -#include "EventGetBrainOpenGLTextRenderer.h" #include "EventGraphicsUpdateAllWindows.h" #include "EventGraphicsUpdateOneWindow.h" #include "EventGetOrSetUserInputModeProcessor.h" #include "EventIdentificationRequest.h" -#include "EventOpenGLTexture.h" #include "EventUserInterfaceUpdate.h" #include "FtglFontTextRenderer.h" #include "GuiManager.h" @@ -65,7 +65,7 @@ #include "Matrix4x4.h" #include "Model.h" #include "MouseEvent.h" -#include "QGLWidgetTextRenderer.h" +#include "OffScreenOpenGLRenderer.h" #include "SelectionManager.h" #include "SelectionItemAnnotation.h" #include "SelectionItemSurfaceNode.h" @@ -79,7 +79,7 @@ #include "UserInputModeImage.h" #include "UserInputModeView.h" #include "UserInputModeVolumeEdit.h" - +#include "WuQMessageBox.h" using namespace caret; @@ -95,12 +95,16 @@ using namespace caret; * The parent widget. */ BrainOpenGLWidget::BrainOpenGLWidget(QWidget* parent, + const BrainOpenGLWidget* shareWidget, const int32_t windowIndex) -: QGLWidget(parent), -windowIndex(windowIndex), -m_textRenderer(NULL) +#ifdef WORKBENCH_USE_QT5_QOPENGL_WIDGET +: QOpenGLWidget(parent), +#else +: QGLWidget(parent, + shareWidget), +#endif +windowIndex(windowIndex) { - this->openGL = NULL; this->borderBeingDrawn = new Border(); m_mousePositionValid = false; @@ -142,10 +146,46 @@ m_textRenderer(NULL) EventManager::get()->addEventListener(this, EventTypeEnum::EVENT_GRAPHICS_UPDATE_ALL_WINDOWS); EventManager::get()->addEventListener(this, EventTypeEnum::EVENT_GRAPHICS_UPDATE_ONE_WINDOW); EventManager::get()->addEventListener(this, EventTypeEnum::EVENT_GET_OR_SET_USER_INPUT_MODE); - EventManager::get()->addEventListener(this, EventTypeEnum::EVENT_GET_TEXT_RENDERER_FOR_WINDOW); EventManager::get()->addEventListener(this, EventTypeEnum::EVENT_IDENTIFICATION_REQUEST); EventManager::get()->addEventListener(this, EventTypeEnum::EVENT_IMAGE_CAPTURE); EventManager::get()->addEventListener(this, EventTypeEnum::EVENT_USER_INTERFACE_UPDATE); + + m_openGLContextSharingValid = true; +#ifdef WORKBENCH_USE_QT5_QOPENGL_WIDGET + /* + * Default format is set by desktop.cxx call to initializeDefaultGLFormat(). + */ + setFormat(QSurfaceFormat::defaultFormat()); +#else + /* + * Test to see if OpenGL context sharing is valid. + * If there is no sharingWidget, then this is the + * first window opened. + */ + if (shareWidget != NULL) { + if ( ! isSharing()) { + m_openGLContextSharingValid = false; + } + } +#endif + +// std::cout << "Share widget: " << std::hex << (uint64_t)shareWidget +// << ", Context Pointer: " << std::hex << (uint64_t)context() +// << ", Share Group: " << std::hex << (uint64_t)context()->contextHandle()->shareGroup() +// << ", Sharing enabled: " << (isSharing() ? "Yes" : "No") +// << std::endl; + +#ifdef WORKBENCH_USE_QT5_QOPENGL_WIDGET + /* Context is not yet valid for QOpenGLWidget */ + m_contextShareGroupPointer = NULL; +#else + CaretAssert(context()); + CaretAssert(context()->contextHandle()); + m_contextShareGroupPointer = context()->contextHandle()->shareGroup(); + CaretAssert(m_contextShareGroupPointer); +#endif + + s_brainOpenGLWidgets.insert(this); } /** @@ -155,52 +195,68 @@ BrainOpenGLWidget::~BrainOpenGLWidget() { makeCurrent(); - /** DO NOT delete since not owned by this */ - m_textRenderer = NULL; - this->clearDrawingViewportContents(); - if (this->openGL != NULL) { - delete this->openGL; - this->openGL = NULL; - } delete this->userInputViewModeProcessor; delete this->userInputAnnotationsModeProcessor; delete this->userInputBordersModeProcessor; delete this->userInputFociModeProcessor; delete this->userInputImageModeProcessor; delete this->userInputVolumeEditModeProcessor; - this->selectedUserInputProcessor = NULL; // DO NOT DELETE since it does not own the object to which it points + this->selectedUserInputProcessor = NULL; /* DO NOT DELETE since it does not own the object to which it points */ delete this->borderBeingDrawn; EventManager::get()->removeAllEventsFromListener(this); + + s_brainOpenGLWidgets.erase(this); + if (s_brainOpenGLWidgets.empty()) { + if (s_singletonOpenGL != NULL) { + delete s_singletonOpenGL; + s_singletonOpenGL = NULL; + } + } } +/** + * @return True if OpenGL Context Sharing is valid among + * multiple graphics windows. + * Note: If there is one window, we declare sharing valid. + */ +bool +BrainOpenGLWidget::isOpenGLContextSharingValid() const +{ + return m_openGLContextSharingValid; +} + + /** * Initializes graphics. */ void BrainOpenGLWidget::initializeGL() { - if (this->openGL == NULL) { - this->openGL = new BrainOpenGLFixedPipeline(this->windowIndex, - createTextRenderer()); - } - else { + if (s_singletonOpenGL == NULL) { /* - * You would think Qt would need to call initializedGL() only once. - * However, if QGLWidget::renderPixmap() is called, it calls - * this method since it needs to create a new OpenGL context. - * Since the OpenGL context contains textures (and the font - * rendering may use textures), The text renderer must be - * recreated. Otherwise, fonts will be garbage in the - * image produced by QGLWidget::renderPixmap(). + * OpenGL drawing will take ownership of the text renderer + * and handle deletion of the text renderer. */ - this->openGL->setTextRenderer(createTextRenderer()); + BrainOpenGLTextRenderInterface* textRenderer = new FtglFontTextRenderer(); + if (! textRenderer->isValid()) { + delete textRenderer; + textRenderer = NULL; + CaretLogWarning("Unable to create FTGL Font Renderer.\n" + "No text will be available in graphics window."); + } + if (textRenderer == NULL) { + textRenderer = new DummyFontTextRenderer(); + } + CaretAssert(textRenderer); + + s_singletonOpenGL = new BrainOpenGLFixedPipeline(textRenderer); } - this->openGL->initializeOpenGL(); + s_singletonOpenGL->initializeOpenGL(); this->lastMouseX = 0; this->lastMouseY = 0; @@ -212,7 +268,7 @@ BrainOpenGLWidget::initializeGL() CaretLogConfig(getOpenGLInformation()); if (s_defaultGLFormatInitialized == false) { - CaretLogSevere("PROGRAM ERROR: The default QGLFormat has not been set.\n" + CaretLogSevere("PROGRAM ERROR: The default OpenGL has not been set.\n" "Need to call BrainOpenGLWidget::initializeDefaultGLFormat() prior to " "instantiating an instance of this class."); } @@ -225,9 +281,63 @@ QString BrainOpenGLWidget::getOpenGLInformation() { AString info; +#if QT_VERSION >= 0x050000 +#ifdef WORKBENCH_USE_QT5_QOPENGL_WIDGET + info += ("Rendering with Qt5 QOpenGLWidget.\n"); +#else + info += ("Rendering with Qt5 QGLWidget (deprecated).\n"); +#endif + info += ("\n"); +#endif + +#ifdef WORKBENCH_USE_QT5_QOPENGL_WIDGET + QSurfaceFormat format = this->format(); + AString swapInfo("Swap Behavior: "); + switch (format.swapBehavior()) { + case QSurfaceFormat::DefaultSwapBehavior: + swapInfo += ("DefaultSwapBehavior"); + break; + case QSurfaceFormat::SingleBuffer: + swapInfo += ("SingleBuffer"); + break; + case QSurfaceFormat::DoubleBuffer: + swapInfo += ("DoubleBuffer"); + break; + case QSurfaceFormat::TripleBuffer: + swapInfo += ("TripleBuffer"); + break; + } + + info += ("QOpenGLWidget Context:" + "\n Alpha: " + AString::fromBool(format.hasAlpha()) + + "\n Alpha size: " + AString::number(format.alphaBufferSize()) + + "\n Depth size: " + AString::number(format.depthBufferSize()) + + "\n Red size: " + AString::number(format.redBufferSize()) + + "\n Green size: " + AString::number(format.greenBufferSize()) + + "\n Blue size: " + AString::number(format.blueBufferSize()) + + "\n Samples size: " + AString::number(format.samples()) + + "\n Stencil size: " + AString::number(format.stencilBufferSize()) + + "\n " + swapInfo + + "\n Swap Interval: " + AString::number(format.swapInterval()) + + "\n Stereo: " + AString::fromBool(format.stereo()) + + "\n Major Version: " + AString::number(format.majorVersion()) + + "\n Minor Version: " + AString::number(format.minorVersion())); + switch (format.profile()) { + case QSurfaceFormat::NoProfile: + info += ("\nOpenGL NoProfile - OpenGL version is lower than 3.2. For 3.2 and newer this is same as CoreProfile."); + break; + case QSurfaceFormat::CoreProfile: + info += ("\nOpenGL CoreProfile - Functionality deprecated in OpenGL version 3.0 is not available."); + break; + case QSurfaceFormat::CompatibilityProfile: + info += ("\nOpenGL CompatibilityProfile - Functionality from earlier OpenGL versions is available."); + break; + } + +#else QGLFormat format = this->format(); - info += ("OpenGL Context:" + info += ("QGLWidget Context:" "\n Accum: " + AString::fromBool(format.accum()) + "\n Accum size: " + AString::number(format.accumBufferSize()) + "\n Alpha: " + AString::fromBool(format.alpha()) @@ -248,8 +358,9 @@ BrainOpenGLWidget::getOpenGLInformation() + "\n Stereo: " + AString::fromBool(format.stereo()) + "\n Major Version: " + AString::number(format.majorVersion()) + "\n Minor Version: " + AString::number(format.minorVersion())); +#endif - info += ("\n\n" + this->openGL->getOpenGLInformation()); + info += ("\n\n" + s_singletonOpenGL->getOpenGLInformation()); #if BRAIN_OPENGL_INFO_SUPPORTS_DISPLAY_LISTS int32_t numDisplayLists = 0; @@ -262,65 +373,11 @@ BrainOpenGLWidget::getOpenGLInformation() + AString::number(numDisplayLists) + " display lists are allocated in first OpenGL context."); #endif // BRAIN_OPENGL_INFO_SUPPORTS_DISPLAY_LISTS -#ifdef BRAIN_OPENGL_INFO_SUPPORTS_VERTEX_BUFFERS - int32_t numVertexBuffers = 0; - for (GLuint iBuff = 1; iBuff < 1000; iBuff++) { - if (glIsBuffer(iBuff)) { - numVertexBuffers++; - } - } - info += ("\nAt least " - + AString::number(numVertexBuffers) - + " vertex buffers are allocated"); -#endif // BRAIN_OPENGL_INFO_SUPPORTS_VERTEX_BUFFERS info += "\n"; return info; } -/** - * @return A new text renderer. - */ -BrainOpenGLTextRenderInterface* -BrainOpenGLWidget::createTextRenderer() -{ - BrainOpenGLTextRenderInterface* textRendererOut = NULL; - - /* - * Create a FTGL font renderer - */ - if (textRendererOut == NULL){ - textRendererOut = new FtglFontTextRenderer(); - if ( ! textRendererOut->isValid()) { - CaretLogWarning("Failed to create FTGL text renderer."); - delete textRendererOut; - textRendererOut = NULL; - } - } - - /* - * Create a Qt text renderer - */ - if (textRendererOut == NULL) { - textRendererOut = new QGLWidgetTextRenderer(this); - if ( ! textRendererOut->isValid()) { - CaretLogWarning("Failed to create Qt text renderer."); - delete textRendererOut; - textRendererOut = NULL; - } - } - - if (textRendererOut == NULL) { - CaretLogSevere("Unable to create a text renderer for OpenGL. " - "No text will be drawn (dummy text renderer)."); - textRendererOut = new DummyFontTextRenderer(); - } - - m_textRenderer = textRendererOut; - - return textRendererOut; -} - /** * Called when widget is resized. */ @@ -346,11 +403,7 @@ BrainOpenGLWidget::getBorderBeingDrawn() void BrainOpenGLWidget::clearDrawingViewportContents() { - const int32_t num = static_cast(this->drawingViewportContents.size()); - for (int32_t i = 0; i < num; i++) { - delete this->drawingViewportContents[i]; - } - this->drawingViewportContents.clear(); + m_windowContent.clear(); } /** @@ -369,32 +422,93 @@ BrainOpenGLWidget::updateCursor() } /** - * Paints the graphics. + * Perform experimental off screen image capture. */ -void -BrainOpenGLWidget::paintGL() +QImage +BrainOpenGLWidget::performOffScreenImageCapture(const int32_t imageWidth, + const int32_t imageHeight) { - updateCursor(); + makeCurrent(); - this->clearDrawingViewportContents(); + QImage image; - int windowViewport[4] = { - 0, - 0, - this->windowWidth[this->windowIndex], - this->windowHeight[this->windowIndex] - }; + OffScreenOpenGLRenderer offscreen(this, + imageWidth, + imageHeight); + if (offscreen.isError()) { + WuQMessageBox::errorOk(this, + offscreen.getErrorMessage()); + doneCurrent(); + return image; + } + + const int32_t viewport[4] = { 0, 0, imageWidth, imageHeight }; + + BrainOpenGLWindowContent windowContent; + getDrawingWindowContent(viewport, + windowContent); + + s_singletonOpenGL->drawModels(this->windowIndex, + GuiManager::get()->getBrain(), + m_contextShareGroupPointer, + windowContent.getAllTabViewports()); + + if (offscreen.isError()) { + WuQMessageBox::errorOk(this, + offscreen.getErrorMessage()); + doneCurrent(); + return image; + } + + return offscreen.getImage(); +} + +/** + * Get the content for drawing the window. + * + * @param windowViewport + * Viewport of the window. + * @param windowContent + * Window content that is updated with window and its tabs. + */ +void +BrainOpenGLWidget::getDrawingWindowContent(const int32_t windowViewportIn[4], + BrainOpenGLWindowContent& windowContent) const +{ + windowContent.clear(); BrainBrowserWindow* bbw = GuiManager::get()->getBrowserWindowByWindowIndex(this->windowIndex); - EventBrowserWindowContentGet getModelEvent(this->windowIndex); + /* + * When restoring scene, the first browser window may get closed. + * This occurs when the user creates a scene that does not contain + * the first browser window. The close() method in QWindow may not execute + * destruction of the window so this widget may still be valid but + * GuiManager no longer has a pointer to the window since it is being closed. + * In this case, the window will be NULL. + * + * This problem is related to the moving window properties to the SessionManager, + * in Feb 2018. + */ + if (bbw == NULL) { + return; + } + + EventBrowserWindowDrawingContent getModelEvent(this->windowIndex); EventManager::get()->sendEvent(getModelEvent.getPointer()); if (getModelEvent.isError()) { return; } - if (bbw->isAspectRatioLocked()) { + int32_t windowViewport[4] = { + windowViewportIn[0], + windowViewportIn[1], + windowViewportIn[2], + windowViewportIn[3] + }; + + if (bbw->isWindowAspectRatioLocked()) { const float aspectRatio = bbw->getAspectRatio(); if (aspectRatio > 0.0) { BrainOpenGLViewportContent::adjustViewportForAspectRatio(windowViewport, @@ -405,22 +519,21 @@ BrainOpenGLWidget::paintGL() /* * Highlighting of border points */ - this->openGL->setDrawHighlightedEndPoints(false); + s_singletonOpenGL->setDrawHighlightedEndPoints(false); if (this->selectedUserInputProcessor == this->userInputBordersModeProcessor) { - this->openGL->setDrawHighlightedEndPoints(this->userInputBordersModeProcessor->isHighlightBorderEndPoints()); + s_singletonOpenGL->setDrawHighlightedEndPoints(this->userInputBordersModeProcessor->isHighlightBorderEndPoints()); } const GapsAndMargins* gapsAndMargins = GuiManager::get()->getBrain()->getGapsAndMargins(); - const int32_t numToDraw = getModelEvent.getNumberOfItemsToDraw(); - if (numToDraw == 1) { - BrainOpenGLViewportContent* vc = BrainOpenGLViewportContent::createViewportForSingleTab(getModelEvent.getTabContentToDraw(0), - gapsAndMargins, - this->windowIndex, - windowViewport); - this->drawingViewportContents.push_back(vc); + const int32_t numberOfTabs = getModelEvent.getNumberOfBrowserTabs(); + std::vector allTabs; + for (int32_t i = 0; i < numberOfTabs; i++) { + allTabs.push_back(getModelEvent.getBrowserTab(i)); } - else if (numToDraw > 1) { + + if (getModelEvent.isTileTabsSelected() + && (numberOfTabs > 1)) { const int32_t windowWidth = windowViewport[2]; const int32_t windowHeight = windowViewport[3]; @@ -436,43 +549,91 @@ BrainOpenGLWidget::paintGL() /* * Get the sizes of the tab tiles from the tile tabs configuration */ - if ( ! tileTabsConfiguration->getRowHeightsAndColumnWidthsForWindowSize(windowWidth, - windowHeight, - numToDraw, - rowHeights, - columnsWidths)) { - CaretLogSevere("Tile Tabs Row/Column sizing failed !!!"); - return; + if (tileTabsConfiguration->getRowHeightsAndColumnWidthsForWindowSize(windowWidth, + windowHeight, + numberOfTabs, + rowHeights, + columnsWidths)) { + + /* + * Create the viewport drawing contents for all tabs + */ + + std::vector tabViewportContent = BrainOpenGLViewportContent::createViewportContentForTileTabs(allTabs, + tileTabsConfiguration, + gapsAndMargins, + windowIndex, + windowViewport, + getModelEvent.getTabIndexForTileTabsHighlighting()); + for (auto tabvp : tabViewportContent) { + windowContent.addTabViewport(tabvp); + } } - - /* - * Create the viewport drawing contents for all tabs - */ - std::vector allTabs; - for (int32_t i = 0; i < getModelEvent.getNumberOfItemsToDraw(); i++) { - allTabs.push_back(getModelEvent.getTabContentToDraw(i)); + else { + CaretLogSevere("Tile Tabs Row/Column sizing failed !!!"); } - - this->drawingViewportContents = BrainOpenGLViewportContent::createViewportContentForTileTabs(allTabs, - tileTabsConfiguration, - gapsAndMargins, - windowIndex, - windowViewport, - getModelEvent.getTabIndexForTileTabsHighlighting()); + } + else if (numberOfTabs >= 1) { + BrainOpenGLViewportContent* vc = BrainOpenGLViewportContent::createViewportForSingleTab(allTabs, + getModelEvent.getSelectedBrowserTabContent(), + gapsAndMargins, + this->windowIndex, + windowViewport); + windowContent.addTabViewport(vc); } + std::vector emptyTabVector; + BrainOpenGLViewportContent* windowViewportContent = BrainOpenGLViewportContent::createViewportForSingleTab(emptyTabVector, + NULL, + gapsAndMargins, + windowIndex, + windowViewport); + windowContent.setWindowViewport(windowViewportContent); +} + + +/** + * Paints the graphics. + */ +void +BrainOpenGLWidget::paintGL() +{ +#ifdef WORKBENCH_USE_QT5_QOPENGL_WIDGET + if (m_contextShareGroupPointer == NULL) { + m_contextShareGroupPointer = context()->shareGroup(); + CaretAssert(m_contextShareGroupPointer); + } +#endif + + updateCursor(); + + this->clearDrawingViewportContents(); + + const int windowViewport[4] = { + 0, + 0, + this->windowWidth[this->windowIndex], + this->windowHeight[this->windowIndex] + }; + + getDrawingWindowContent(windowViewport, + m_windowContent); + if (this->selectedUserInputProcessor == userInputBordersModeProcessor) { - this->openGL->setBorderBeingDrawn(this->borderBeingDrawn); + s_singletonOpenGL->setBorderBeingDrawn(this->borderBeingDrawn); } else { - this->openGL->setBorderBeingDrawn(NULL); + s_singletonOpenGL->setBorderBeingDrawn(NULL); } - this->openGL->drawModels(GuiManager::get()->getBrain(), - this->drawingViewportContents); + s_singletonOpenGL->drawModels(this->windowIndex, + GuiManager::get()->getBrain(), + m_contextShareGroupPointer, + m_windowContent.getAllTabViewports()); /* * Issue browser window redrawn event */ + BrainBrowserWindow* bbw = GuiManager::get()->getBrowserWindowByWindowIndex(this->windowIndex); if (bbw != NULL) { EventManager::get()->sendEvent(EventBrowserWindowGraphicsRedrawn(bbw).getPointer()); } @@ -516,7 +677,11 @@ BrainOpenGLWidget::event(QEvent* event) } } +#ifdef WORKBENCH_USE_QT5_QOPENGL_WIDGET + return QOpenGLWidget::event(event); +#else return QGLWidget::event(event); +#endif } @@ -531,7 +696,7 @@ BrainOpenGLWidget::contextMenuEvent(QContextMenuEvent* contextMenuEvent) const int mouseX = contextMenuEvent->x(); const int mouseY = this->height() - contextMenuEvent->y(); - BrainOpenGLViewportContent* viewportContent = this->getViewportContentAtXY(mouseX, + const BrainOpenGLViewportContent* viewportContent = this->getViewportContentAtXY(mouseX, mouseY); if (viewportContent != NULL) { MouseEvent mouseEvent(viewportContent, @@ -573,7 +738,7 @@ BrainOpenGLWidget::wheelEvent(QWheelEvent* we) * out of its viewport without releasing the mouse * button. */ - BrainOpenGLViewportContent* viewportContent = this->getViewportContentAtXY(wheelX, + const BrainOpenGLViewportContent* viewportContent = this->getViewportContentAtXY(wheelX, wheelY); if (viewportContent != NULL) { MouseEvent mouseEvent(viewportContent, @@ -724,7 +889,7 @@ BrainOpenGLWidget::mousePressEvent(QMouseEvent* me) this->isMousePressedNearToolBox = true; } - BrainOpenGLViewportContent* viewportContent = this->getViewportContentAtXY(mouseX, + const BrainOpenGLViewportContent* viewportContent = this->getViewportContentAtXY(mouseX, mouseY); if (viewportContent != NULL) { MouseEvent mouseEvent(viewportContent, @@ -742,7 +907,7 @@ BrainOpenGLWidget::mousePressEvent(QMouseEvent* me) this->selectedUserInputProcessor->mouseLeftPress(mouseEvent); } else if (keyModifiers == Qt::ShiftModifier) { - // not implemented this->selectedUserInputProcessor->mouseLeftPressWithShift(mouseEvent); + /* not implemented this->selectedUserInputProcessor->mouseLeftPressWithShift(mouseEvent); */ } } } @@ -789,7 +954,7 @@ BrainOpenGLWidget::mouseReleaseEvent(QMouseEvent* me) /* * Mouse button RELEASE event */ - BrainOpenGLViewportContent* viewportContent = this->getViewportContentAtXY(mouseX, + const BrainOpenGLViewportContent* viewportContent = this->getViewportContentAtXY(mouseX, mouseY); if (viewportContent != NULL) { MouseEvent mouseEvent(viewportContent, @@ -812,7 +977,7 @@ BrainOpenGLWidget::mouseReleaseEvent(QMouseEvent* me) * out of its viewport without releasing the mouse * button. */ - BrainOpenGLViewportContent* viewportContent = this->getViewportContentAtXY(this->mousePressX, + const BrainOpenGLViewportContent* viewportContent = this->getViewportContentAtXY(this->mousePressX, this->mousePressY); if (viewportContent != NULL) { if ((absDX <= BrainOpenGLWidget::MOUSE_MOVEMENT_TOLERANCE) @@ -877,7 +1042,7 @@ BrainOpenGLWidget::mouseDoubleClickEvent(QMouseEvent* me) * out of its viewport without releasing the mouse * button. */ - BrainOpenGLViewportContent* viewportContent = this->getViewportContentAtXY(mouseX, + const BrainOpenGLViewportContent* viewportContent = this->getViewportContentAtXY(mouseX, mouseY); if (viewportContent != NULL) { MouseEvent mouseEvent(viewportContent, @@ -924,24 +1089,21 @@ BrainOpenGLWidget::leaveEvent(QEvent* /*e*/) * @param y * Y-coordinate. */ -BrainOpenGLViewportContent* +const BrainOpenGLViewportContent* BrainOpenGLWidget::getViewportContentAtXY(const int x, const int y) { - BrainOpenGLViewportContent* viewportContent = NULL; - const int32_t num = static_cast(this->drawingViewportContents.size()); - for (int32_t i = 0; i < num; i++) { - int viewport[4]; - this->drawingViewportContents[i]->getTabViewportBeforeApplyingMargins(viewport); - if ((x >= viewport[0]) - && (x < (viewport[0] + viewport[2])) - && (y >= viewport[1]) - && (y < (viewport[1] + viewport[3]))) { - viewportContent = this->drawingViewportContents[i]; - break; - } + const BrainOpenGLViewportContent* tabViewportContent = m_windowContent.getTabViewportWithLockAspectXY(x, y); + if (tabViewportContent != NULL) { + return tabViewportContent; } - return viewportContent; + + /* + * If not in a tab, then use the window viewport information. + * This allows selection of annotations in window space that are not + * within a tab (tab may be small in height due to lock aspect). + */ + return m_windowContent.getWindowViewport(); } /** @@ -954,8 +1116,7 @@ BrainOpenGLWidget::getViewportContentAtXY(const int x, std::vector BrainOpenGLWidget::getViewportContent() const { - std::vector contentOut(this->drawingViewportContents.begin(), - this->drawingViewportContents.end()); + std::vector contentOut = m_windowContent.getAllTabViewports(); return contentOut; } @@ -982,7 +1143,7 @@ BrainOpenGLWidget::performIdentification(const int x, const int y, const bool applySelectionBackgroundFiltering) { - BrainOpenGLViewportContent* idViewport = this->getViewportContentAtXY(x, y); + const BrainOpenGLViewportContent* idViewport = this->getViewportContentAtXY(x, y); this->makeCurrent(); CaretLogFine("Performing selection"); @@ -992,12 +1153,31 @@ BrainOpenGLWidget::performIdentification(const int x, idManager->getVoxelEditingIdentification()->setEnabledForSelection(false); if (idViewport != NULL) { - this->openGL->selectModel(GuiManager::get()->getBrain(), + s_singletonOpenGL->selectModel(this->windowIndex, + GuiManager::get()->getBrain(), + m_contextShareGroupPointer, idViewport, x, y, applySelectionBackgroundFiltering); } + +#ifdef WORKBENCH_USE_QT5_QOPENGL_WIDGET + /* + * Note: The QOpenGLWidget always renders in a + * frame buffer object (see its documentation) so + * there is no "back" or "front buffer". Since + * identification is encoded in the framebuffer, + * it is necessary to repaint (udpates graphics + * immediately) to redraw the models. Otherwise, + * the graphics flash with strange looking drawing. + */ + this->repaint(); + this->doneCurrent(); +#else + this->repaint(); +#endif + return idManager; } @@ -1018,7 +1198,7 @@ SelectionItemAnnotation* BrainOpenGLWidget::performIdentificationAnnotations(const int x, const int y) { - BrainOpenGLViewportContent* idViewport = this->getViewportContentAtXY(x, y); + const BrainOpenGLViewportContent* idViewport = this->getViewportContentAtXY(x, y); this->makeCurrent(); CaretLogFine("Performing selection"); @@ -1037,12 +1217,30 @@ BrainOpenGLWidget::performIdentificationAnnotations(const int x, const int idX = x - vp[0]; const int idY = y - vp[1]; */ - this->openGL->selectModel(GuiManager::get()->getBrain(), + s_singletonOpenGL->selectModel(this->windowIndex, + GuiManager::get()->getBrain(), + m_contextShareGroupPointer, idViewport, x, y, true); } + +#ifdef WORKBENCH_USE_QT5_QOPENGL_WIDGET + /* + * Note: The QOpenGLWidget always renders in a + * frame buffer object (see its documentation) so + * there is no "back" or "front buffer". Since + * identification is encoded in the framebuffer, + * it is necessary to repaint (udpates graphics + * immediately) to redraw the models. Otherwise, + * the graphics flash with strange looking drawing. + */ + this->repaint(); + this->doneCurrent(); +#else + this->repaint(); +#endif return annotationID; } @@ -1065,7 +1263,7 @@ BrainOpenGLWidget::performIdentificationVoxelEditing(VolumeFile* editingVolumeFi const int x, const int y) { - BrainOpenGLViewportContent* idViewport = this->getViewportContentAtXY(x, y); + const BrainOpenGLViewportContent* idViewport = this->getViewportContentAtXY(x, y); this->makeCurrent(); CaretLogFine("Performing selection"); @@ -1085,12 +1283,31 @@ BrainOpenGLWidget::performIdentificationVoxelEditing(VolumeFile* editingVolumeFi const int idX = x - vp[0]; const int idY = y - vp[1]; */ - this->openGL->selectModel(GuiManager::get()->getBrain(), + s_singletonOpenGL->selectModel(this->windowIndex, + GuiManager::get()->getBrain(), + m_contextShareGroupPointer, idViewport, x, y, true); } + +#ifdef WORKBENCH_USE_QT5_QOPENGL_WIDGET + /* + * Note: The QOpenGLWidget always renders in a + * frame buffer object (see its documentation) so + * there is no "back" or "front buffer". Since + * identification is encoded in the framebuffer, + * it is necessary to repaint (udpates graphics + * immediately) to redraw the models. Otherwise, + * the graphics flash with strange looking drawing. + */ + this->repaint(); + this->doneCurrent(); +#else + this->repaint(); +#endif + return idManager; } @@ -1109,26 +1326,34 @@ BrainOpenGLWidget::performProjection(const int x, const int y, SurfaceProjectedItem& projectionOut) { - BrainOpenGLViewportContent* projectionViewport = this->getViewportContentAtXY(x, y); + const BrainOpenGLViewportContent* projectionViewport = this->getViewportContentAtXY(x, y); this->makeCurrent(); CaretLogFine("Performing projection"); if (projectionViewport != NULL) { - /* - * ID coordinate needs to be relative to the viewport - * - int vp[4]; - idViewport->getViewport(vp); - const int idX = x - vp[0]; - const int idY = y - vp[1]; - */ - this->openGL->projectToModel(GuiManager::get()->getBrain(), + s_singletonOpenGL->projectToModel(this->windowIndex, + GuiManager::get()->getBrain(), + m_contextShareGroupPointer, projectionViewport, x, y, projectionOut); } + +#ifdef WORKBENCH_USE_QT5_QOPENGL_WIDGET + /* + * Note: The QOpenGLWidget always renders in a + * frame buffer object (see its documentation) so + * there is no "back" or "front buffer". Since + * identification is encoded in the framebuffer, + * it is necessary to repaint (udpates graphics + * immediately) to redraw the models. Otherwise, + * the graphics flash with strange looking drawing. + */ + this->repaint(); + this->doneCurrent(); +#endif } /** @@ -1173,7 +1398,7 @@ BrainOpenGLWidget::mouseMoveEvent(QMouseEvent* me) * out of its viewport without releasing the mouse * button. */ - BrainOpenGLViewportContent* viewportContent = this->getViewportContentAtXY(this->mousePressX, + const BrainOpenGLViewportContent* viewportContent = this->getViewportContentAtXY(this->mousePressX, this->mousePressY); if (viewportContent != NULL) { MouseEvent mouseEvent(viewportContent, @@ -1212,7 +1437,7 @@ BrainOpenGLWidget::mouseMoveEvent(QMouseEvent* me) this->lastMouseY = mouseY; } else if (mouseButtons == Qt::NoButton) { - BrainOpenGLViewportContent* viewportContent = this->getViewportContentAtXY(mouseX, + const BrainOpenGLViewportContent* viewportContent = this->getViewportContentAtXY(mouseX, mouseY); if (viewportContent != NULL) { MouseEvent mouseEvent(viewportContent, @@ -1237,7 +1462,7 @@ BrainOpenGLWidget::mouseMoveEvent(QMouseEvent* me) } } - BrainOpenGLViewportContent* viewportContent = this->getViewportContentAtXY(mouseX, + const BrainOpenGLViewportContent* viewportContent = this->getViewportContentAtXY(mouseX, mouseY); if (viewportContent != NULL) { m_mousePositionEvent.grabNew(new MouseEvent(viewportContent, @@ -1278,15 +1503,6 @@ BrainOpenGLWidget::receiveEvent(Event* event) brainResetEvent->setEventProcessed(); } - else if (event->getEventType() == EventTypeEnum::EVENT_GET_TEXT_RENDERER_FOR_WINDOW) { - EventGetBrainOpenGLTextRenderer* textRenderEvent = dynamic_cast(event); - CaretAssert(textRenderEvent); - - if (this->windowIndex == textRenderEvent->getWindowIndex()) { - textRenderEvent->setTextRenderer(m_textRenderer); - textRenderEvent->setEventProcessed(); - } - } else if (event->getEventType() == EventTypeEnum::EVENT_GRAPHICS_UPDATE_ALL_WINDOWS) { EventGraphicsUpdateAllWindows* updateAllEvent = dynamic_cast(event); @@ -1298,7 +1514,11 @@ BrainOpenGLWidget::receiveEvent(Event* event) this->repaint(); } else { +#ifdef WORKBENCH_USE_QT5_QOPENGL_WIDGET + this->update(); +#else this->updateGL(); +#endif } } else if (event->getEventType() == EventTypeEnum::EVENT_GRAPHICS_UPDATE_ONE_WINDOW) { @@ -1309,13 +1529,17 @@ BrainOpenGLWidget::receiveEvent(Event* event) if (updateOneEvent->getWindowIndex() == this->windowIndex) { updateOneEvent->setEventProcessed(); +#ifdef WORKBENCH_USE_QT5_QOPENGL_WIDGET + this->update(); +#else this->updateGL(); +#endif } else { /* * If a window is yoked, update its graphics. */ - EventBrowserWindowContentGet getModelEvent(this->windowIndex); + EventBrowserWindowDrawingContent getModelEvent(this->windowIndex); EventManager::get()->sendEvent(getModelEvent.getPointer()); if (getModelEvent.isError()) { @@ -1324,7 +1548,11 @@ BrainOpenGLWidget::receiveEvent(Event* event) bool needUpdate = false; if (needUpdate) { +#ifdef WORKBENCH_USE_QT5_QOPENGL_WIDGET + this->update(); +#else this->updateGL(); +#endif } } } @@ -1423,12 +1651,30 @@ BrainOpenGLWidget::captureImage(EventImageCapture* imageCaptureEvent) * Note that a size of zero indicates capture graphics in its * current size. */ - const int captureOffsetX = imageCaptureEvent->getCaptureOffsetX(); - const int captureOffsetY = imageCaptureEvent->getCaptureOffsetY(); - const int captureWidth = imageCaptureEvent->getCaptureWidth(); - const int captureHeight = imageCaptureEvent->getCaptureHeight(); - const int outputImageWidth = imageCaptureEvent->getOutputWidth(); - const int outputImageHeight = imageCaptureEvent->getOutputHeight(); + int captureOffsetX = imageCaptureEvent->getCaptureOffsetX(); + int captureOffsetY = imageCaptureEvent->getCaptureOffsetY(); + int captureWidth = imageCaptureEvent->getCaptureWidth(); + int captureHeight = imageCaptureEvent->getCaptureHeight(); + int outputImageWidth = imageCaptureEvent->getOutputWidth(); + int outputImageHeight = imageCaptureEvent->getOutputHeight(); + + if ((outputImageWidth <= 0) + || (outputImageHeight <= 0)) { + BrainBrowserWindow* browserWindow = GuiManager::get()->getBrowserWindowByWindowIndex(this->windowIndex); + + if (browserWindow != NULL) { + int32_t widgetWidth(0), widgetHeight(0); + browserWindow->getGraphicsWidgetSize(captureOffsetX, + captureOffsetY, + captureWidth, + captureHeight, + widgetWidth, + widgetHeight, + true); /* true => apply lock aspect ratio */ + outputImageWidth = captureWidth; + outputImageHeight = captureHeight; + } + } /* * Force immediate mode since problems with display lists @@ -1451,49 +1697,36 @@ BrainOpenGLWidget::captureImage(EventImageCapture* imageCaptureEvent) * update() is a scheduled update). */ repaint(); +#ifdef WORKBENCH_USE_QT5_QOPENGL_WIDGET + image = grabFramebuffer(); +#else image = grabFrameBuffer(); - - if ((captureOffsetX > 0) - || (captureOffsetY > 0)) { - if ((captureWidth > 0) - && (captureHeight > 0)) { - image = image.copy(captureOffsetX, - captureOffsetY, - captureWidth, - captureHeight); - } +#endif + if ((captureWidth > 0) + && (captureHeight > 0)) { + image = image.copy(captureOffsetX, + captureOffsetY, + captureWidth, + captureHeight); } - /* - * If image was captured successfully and the caller has - * requested that the image be a specific size, scale - * the image to the requested size. - */ - if ((image.width() > 0) - && (image.height() > 0)) { - if ((outputImageWidth > 0) - && (outputImageHeight > 0)) { - if ((image.width() != outputImageWidth) - || (image.height() != outputImageHeight)) { - image = image.scaled(outputImageWidth, - outputImageHeight, - Qt::IgnoreAspectRatio, - Qt::SmoothTransformation); - } - } + if ((outputImageWidth != image.width()) + || (outputImageHeight != image.height())) { + image = image.scaled(outputImageWidth, + outputImageHeight, + Qt::IgnoreAspectRatio, + Qt::SmoothTransformation); } + } break; case ImageCaptureMethodEnum::IMAGE_CAPTURE_WITH_RENDER_PIXMAP: { +#ifdef WORKBENCH_USE_QT5_QOPENGL_WIDGET + WuQMessageBox::errorOk(this, "Render PixMap not implemented with Qt5"); +#else /* - * Note 1: QGLWidget::renderPixmap() creates a new OpenGL - * Context which destroys any textures in the existing - * OpenGL context. So, after execution of - * QGLWidget::renderPixmap(), we need to create a - * new text renderer. - * - * Note 2: When the user chooses to exclude regions + * When the user chooses to exclude regions * caused by locking of tab/window aspect ratio, * the pixmap is rendered to the output width and * height and in the correct aspect ratio so when @@ -1501,15 +1734,18 @@ BrainOpenGLWidget::captureImage(EventImageCapture* imageCaptureEvent) * region caused by aspect locking that needs to * be excluded. */ - BrainOpenGLTextureManager* textureManager = this->openGL->getTextureManager(); - CaretAssert(textureManager); - textureManager->deleteAllTexturesForWindow(this->windowIndex); QPixmap pixmap = this->renderPixmap(outputImageWidth, - outputImageHeight); + outputImageHeight, + true); image = pixmap.toImage(); - textureManager->deleteAllTexturesForWindow(this->windowIndex); - this->openGL->setTextRenderer(createTextRenderer()); +#endif + } + break; + case ImageCaptureMethodEnum::IMAGE_CAPTURE_WITH_OFFSCREEN_FRAME_BUFFER: + { + image = performOffScreenImageCapture(outputImageWidth, + outputImageHeight); } break; } @@ -1522,7 +1758,7 @@ BrainOpenGLWidget::captureImage(EventImageCapture* imageCaptureEvent) imageCaptureEvent->setImage(image); uint8_t backgroundColor[3]; - this->openGL->getBackgroundColor(backgroundColor); + s_singletonOpenGL->getBackgroundColor(backgroundColor); imageCaptureEvent->setBackgroundColor(backgroundColor); } @@ -1540,6 +1776,23 @@ BrainOpenGLWidget::captureImage(EventImageCapture* imageCaptureEvent) void BrainOpenGLWidget::initializeDefaultGLFormat() { +#ifdef WORKBENCH_USE_QT5_QOPENGL_WIDGET + QSurfaceFormat glfmt; + glfmt.setAlphaBufferSize(8); + glfmt.setBlueBufferSize(8); + glfmt.setDepthBufferSize(24); + glfmt.setGreenBufferSize(8); + glfmt.setProfile(QSurfaceFormat::CompatibilityProfile); + glfmt.setRedBufferSize(8); + glfmt.setRenderableType(QSurfaceFormat::OpenGL); + glfmt.setSamples(6); + glfmt.setStereo(false); + glfmt.setSwapBehavior(QSurfaceFormat::DoubleBuffer); + + glfmt.setMajorVersion(2); + glfmt.setMinorVersion(1); + QSurfaceFormat::setDefaultFormat(glfmt); +#else QGLFormat glfmt; glfmt.setAccum(false); glfmt.setAlpha(true); @@ -1549,7 +1802,9 @@ BrainOpenGLWidget::initializeDefaultGLFormat() glfmt.setDirectRendering(true); glfmt.setDoubleBuffer(true); glfmt.setOverlay(false); - glfmt.setSampleBuffers(false); + glfmt.setProfile(QGLFormat::CompatibilityProfile); + glfmt.setVersion(2, 1); + glfmt.setSampleBuffers(true); glfmt.setStencil(false); glfmt.setStereo(false); @@ -1558,6 +1813,7 @@ BrainOpenGLWidget::initializeDefaultGLFormat() glfmt.setGreenBufferSize(8); glfmt.setBlueBufferSize(8); QGLFormat::setDefaultFormat(glfmt); +#endif s_defaultGLFormatInitialized = true; } diff --git a/src/GuiQt/BrainOpenGLWidget.h b/src/GuiQt/BrainOpenGLWidget.h index 124a5ea34aa0d90f56fe66faa3326f875af1ba68..ef7f45817a3b2648427d72f7e722da8e8e5c89d6 100644 --- a/src/GuiQt/BrainOpenGLWidget.h +++ b/src/GuiQt/BrainOpenGLWidget.h @@ -22,23 +22,37 @@ */ /*LICENSE_END*/ +#include +#include + +/* + * When GLEW is used, CaretOpenGLInclude.h will include "Gl/glew.h". + * Gl/glew.h MUST BE BEFORE Gl/gl.h and Gl/gl.h is included by + * QGLWidget so, we must include CaretOpenGL.h before QGLWidget. + */ #include "CaretOpenGLInclude.h" +#ifdef WORKBENCH_USE_QT5_QOPENGL_WIDGET +#include +#else #include +#endif + #include #include #include "BrainConstants.h" +#include "BrainOpenGLWindowContent.h" #include "CaretPointer.h" #include "EventListenerInterface.h" class QMouseEvent; +class QWidget; namespace caret { class Border; class BrainOpenGL; - class BrainOpenGLTextRenderInterface; class BrainOpenGLViewportContent; class BrowserTabContent; class EventImageCapture; @@ -56,11 +70,16 @@ namespace caret { class UserInputModeAbstract; class VolumeFile; +#ifdef WORKBENCH_USE_QT5_QOPENGL_WIDGET + class BrainOpenGLWidget : public QOpenGLWidget, public EventListenerInterface { +#else class BrainOpenGLWidget : public QGLWidget, public EventListenerInterface { +#endif Q_OBJECT public: BrainOpenGLWidget(QWidget* parent, + const BrainOpenGLWidget* shareWidget, const int32_t windowIndex); ~BrainOpenGLWidget(); @@ -92,6 +111,11 @@ namespace caret { std::vector getViewportContent() const; + bool isOpenGLContextSharingValid() const; + + QImage performOffScreenImageCapture(const int32_t imageWidth, + const int32_t imageHeight); + protected: virtual void initializeGL(); @@ -123,11 +147,14 @@ namespace caret { private: - BrainOpenGLTextRenderInterface* createTextRenderer(); + std::vector getDrawingViewportContent(const int32_t windowViewportIn[4]) const; + + void getDrawingWindowContent(const int32_t windowViewportIn[4], + BrainOpenGLWindowContent& windowContent) const; void clearDrawingViewportContents(); - BrainOpenGLViewportContent* getViewportContentAtXY(const int x, + const BrainOpenGLViewportContent* getViewportContentAtXY(const int x, const int y); void checkForMiddleMouseButton(Qt::MouseButtons& mouseButtons, @@ -137,14 +164,9 @@ namespace caret { void captureImage(EventImageCapture* imageCaptureEvent); - BrainOpenGL* openGL; - const int32_t windowIndex; - /** Do not own text renderer so DO NOT delete */ - BrainOpenGLTextRenderInterface* m_textRenderer; - - std::vector drawingViewportContents; + BrainOpenGLWindowContent m_windowContent; int32_t windowWidth[BrainConstants::MAXIMUM_NUMBER_OF_BROWSER_WINDOWS]; int32_t windowHeight[BrainConstants::MAXIMUM_NUMBER_OF_BROWSER_WINDOWS]; @@ -181,12 +203,24 @@ namespace caret { bool m_mousePositionValid; CaretPointer m_mousePositionEvent; + bool m_openGLContextSharingValid = false; + + void* m_contextShareGroupPointer = NULL; + static bool s_defaultGLFormatInitialized; + + static std::set s_brainOpenGLWidgets; + + static BrainOpenGL* s_singletonOpenGL; + + }; #ifdef __BRAIN_OPENGL_WIDGET_DEFINE__ - const int32_t BrainOpenGLWidget::MOUSE_MOVEMENT_TOLERANCE = 0; //10; - bool BrainOpenGLWidget::s_defaultGLFormatInitialized = false; + const int32_t BrainOpenGLWidget::MOUSE_MOVEMENT_TOLERANCE = 0; + bool BrainOpenGLWidget::s_defaultGLFormatInitialized = false; + std::set BrainOpenGLWidget::s_brainOpenGLWidgets; + BrainOpenGL* BrainOpenGLWidget::s_singletonOpenGL = NULL; #endif // __BRAIN_OPENGL_WIDGET_DEFINE__ } // namespace diff --git a/src/GuiQt/CMakeLists.txt b/src/GuiQt/CMakeLists.txt index 74664828851f72e63374574c54b93719526ce857..03e3ee03fe5d45f89cd916552daab7a95f7ddb0e 100644 --- a/src/GuiQt/CMakeLists.txt +++ b/src/GuiQt/CMakeLists.txt @@ -5,191 +5,228 @@ PROJECT (GuiQt) # -# Use OpenGL from QT +# QT include files # -SET(QT_USE_QTNETWORK TRUE) -SET(QT_USE_QTOPENGL TRUE) -#SET(QT_USE_QTWEBKIT TRUE) +if(Qt5_FOUND) + include_directories(${Qt5Core_INCLUDE_DIRS}) + include_directories(${Qt5OpenGL_INCLUDE_DIRS}) + include_directories(${Qt5Network_INCLUDE_DIRS}) + include_directories(${Qt5Widgets_INCLUDE_DIRS}) +endif() +IF (QT4_FOUND) + SET(QT_USE_QTNETWORK TRUE) + SET(QT_USE_QTOPENGL TRUE) + INCLUDE(${QT_USE_FILE}) +ENDIF () # -# QT include files +# Let CMake find files for moc and uic. # -INCLUDE(${QT_USE_FILE}) # -# Headers that must be processed with QT's "moc" -# Any class that derives from a QT class and contains -# the "Q_OBJECT" macro must be listed here. -# -# Also LIST each of these headers in the -# source files section so that they show -# up in development tools such as XCode. +# With AUTOMOC, do not need to specify files +# that contain Q_OBJECT macro for Qt to process with 'moc' +# (meta-object compiler). # -SET(MOC_INPUT_HEADER_FILES -AboutWorkbenchDialog.h -AnnotationChangeCoordinateDialog.h -AnnotationColorWidget.h -AnnotationCoordinateSelectionWidget.h -AnnotationCoordinateSpaceWidget.h -AnnotationCoordinateWidget.h -AnnotationCreateDialog.h -AnnotationDeleteWidget.h -AnnotationFontWidget.h -AnnotationFormatWidget.h -AnnotationInsertNewWidget.h -AnnotationLineArrowTipsWidget.h -AnnotationMenuArrange.h -AnnotationMenuFileSelection.h -AnnotationRedoUndoWidget.h -AnnotationRotationWidget.h -AnnotationSelectionViewController.h -AnnotationPasteDialog.h -AnnotationTextAlignmentWidget.h -AnnotationTextEditorDialog.h -AnnotationTextEditorWidget.h -AnnotationTextOrientationWidget.h -AnnotationWidthHeightWidget.h -BalsaDatabaseUploadSceneFileDialog.h -BorderEditingSelectionDialog.h -BorderFileSplitDialog.h -BorderOptimizeDialog.h -BorderPropertiesEditorDialog.h -BorderSelectionViewController.h -BrainBrowserWindow.h -BrainBrowserWindowComboBox.h -BrainBrowserWindowOrientedToolBox.h -BrainBrowserWindowToolBar.h -BrainBrowserWindowToolBarChartAttributes.h -BrainBrowserWindowToolBarChartAxes.h -BrainBrowserWindowToolBarChartType.h -BrainBrowserWindowToolBarClipping.h -BrainBrowserWindowToolBarComponent.h -BrainBrowserWindowToolBarSlicePlane.h -BrainBrowserWindowToolBarSliceSelection.h -BrainBrowserWindowToolBarSurfaceMontage.h -BrainBrowserWindowToolBarTab.h -BrainBrowserWindowToolBarVolumeMontage.h -BrainOpenGLWidget.h -BugReportDialog.h -CaretColorEnumComboBox.h -CaretColorEnumMenu.h -CaretDataFileSelectionComboBox.h -CaretFileDialog.h -CaretFileDialogExtendable.h -CaretFileRemoteDialog.h -CaretMappableDataFileAndMapSelector.h -CaretMappableDataFileAndMapSelectorObject.h -ChartHistoryViewController.h -ChartLinesSelectionViewController.h -ChartMatrixParcelSelectionViewController.h -ChartMatrixSeriesSelectionViewController.h -ChartSelectionViewController.h -ChartToolBoxViewController.h -CiftiConnectivityMatrixViewController.h -CiftiParcelSelectionComboBox.h -ClippingPlanesDialog.h -ColorEditorWidget.h -CustomViewDialog.h -DataFileContentCopyMoveDialog.h -DisplayGroupAndTabItemViewController.h -DisplayGroupEnumComboBox.h -EnumComboBoxTemplate.h -FiberOrientationSelectionViewController.h -FiberSamplesOpenGLWidget.h -FociProjectionDialog.h -FociPropertiesEditorDialog.h -FociSelectionViewController.h -GapsAndMarginsDialog.h -GiftiLabelTableEditor.h -GiftiLabelTableSelectionComboBox.h -GroupAndNameHierarchyViewController.h -GuiManager.h -HelpViewerDialog.h -HyperLinkTextBrowser.h -IdentifyBrainordinateDialog.h -ImageCaptureDialog.h -ImageFileConvertToVolumeFileDialog.h -ImageSelectionViewController.h -InformationDisplayDialog.h -InformationDisplayPropertiesDialog.h -LabelSelectionViewController.h -MacApplication.h -MacDockMenu.h -MapSettingsColorBarWidget.h -MapSettingsFiberTrajectoryWidget.h -MapSettingsLabelsWidget.h -MapSettingsLayerWidget.h -MapSettingsPaletteColorMappingWidget.h -MapSettingsParcelsWidget.h -MapYokingGroupComboBox.h -MetaDataEditorDialog.h -MetaDataEditorWidget.h -MovieDialog.h -OverlaySetViewController.h -OverlaySettingsEditorDialog.h -OverlayViewController.h -PaletteColorMappingEditorDialog.h -PlotMagnifier.h -PlotPanner.h -PreferencesDialog.h -ProgressReportingDialog.h -ProgressReportingFromEvent.h -ProgressReportingWithSlots.h -RegionOfInterestCreateFromBorderDialog.h -SceneCreateReplaceDialog.h -SceneDialog.h -ScenePreviewDialog.h -SpecFileManagementDialog.h -SplashScreen.h -StructureEnumComboBox.h -StructureSurfaceSelectionControl.h -SurfacePropertiesEditorDialog.h -SurfaceSelectionViewController.h -TileTabsConfigurationDialog.h -UserInputModeAnnotationsContextMenu.h -UserInputModeAnnotationsWidget.h -UserInputModeBordersWidget.h -UserInputModeFociWidget.h -UserInputModeImageWidget.h -UserInputModeViewContextMenu.h -UserInputModeVolumeEditWidget.h -UsernamePasswordWidget.h -VolumeFileCreateDialog.h -VolumeSurfaceOutlineColorOrTabViewController.h -VolumeSurfaceOutlineSetViewController.h -VolumeSurfaceOutlineViewController.h -WuQCollapsibleWidget.h -WuQDataEntryDialog.h -WuQDialog.h -WuQDialogModal.h -WuQDialogNonModal.h -WuQDoubleSlider.h -WuQEventBlockingFilter.h -WuQGridLayoutGroup.h -WuQGroupBoxExclusiveWidget.h -WuQImageLabel.h -WuQListWidget.h -WuQMessageBox.h -WuQwtPlot.h -WuQSpecialIncrementDoubleSpinBox.h -WuQSpinBoxGroup.h -WuQSpinBoxOddValue.h -WuQTabWidget.h -WuQTimedMessageDisplay.h -WuQTreeWidget.h -WuQTrueFalseComboBox.h -#WuQWebView.h -WuQWidget.h -WuQWidgetObjectGroup.h -ZipSceneFileDialog.h -) +IF(WORKBENCH_USE_CMAKE_AUTOMOC) + SET(CMAKE_AUTOMOC ON) + SET(CMAKE_AUTOUIC ON) +ELSE() + SET(FORMS + MovieDialog.ui + ) + + SET(MOC_INPUT_HEADER_FILES + AboutWorkbenchDialog.h + AnnotationChangeCoordinateDialog.h + AnnotationColorWidget.h + AnnotationCoordinateSelectionWidget.h + AnnotationCoordinateSpaceWidget.h + AnnotationCoordinateWidget.h + AnnotationCreateDialog.h + AnnotationDeleteWidget.h + AnnotationFontWidget.h + AnnotationFormatWidget.h + AnnotationInsertNewWidget.h + AnnotationLineArrowTipsWidget.h + AnnotationMenuArrange.h + AnnotationMenuFileSelection.h + AnnotationRedoUndoWidget.h + AnnotationRotationWidget.h + AnnotationSelectionViewController.h + AnnotationPasteDialog.h + AnnotationTextAlignmentWidget.h + AnnotationTextEditorDialog.h + AnnotationTextEditorWidget.h + AnnotationTextOrientationWidget.h + AnnotationWidthHeightWidget.h + BalsaDatabaseUploadSceneFileDialog.h + BalsaStudySelectionDialog.h + BorderEditingSelectionDialog.h + BorderFileSplitDialog.h + BorderOptimizeDialog.h + BorderPropertiesEditorDialog.h + BorderSelectionViewController.h + BrainBrowserWindow.h + BrainBrowserWindowComboBox.h + BrainBrowserWindowOrientedToolBox.h + BrainBrowserWindowToolBar.h + BrainBrowserWindowToolBarChartAttributes.h + BrainBrowserWindowToolBarChartAxes.h + BrainBrowserWindowToolBarChartTwoAttributes.h + BrainBrowserWindowToolBarChartTwoAxes.h + BrainBrowserWindowToolBarChartTwoOrientation.h + BrainBrowserWindowToolBarChartTwoTitle.h + BrainBrowserWindowToolBarChartTwoType.h + BrainBrowserWindowToolBarChartType.h + BrainBrowserWindowToolBarClipping.h + BrainBrowserWindowToolBarComponent.h + BrainBrowserWindowToolBarSlicePlane.h + BrainBrowserWindowToolBarSliceSelection.h + BrainBrowserWindowToolBarSurfaceMontage.h + BrainBrowserWindowToolBarTab.h + BrainBrowserWindowToolBarTabPopUpMenu.h + BrainBrowserWindowToolBarVolumeMontage.h + BrainOpenGLWidget.h + BugReportDialog.h + CaretColorEnumComboBox.h + CaretColorEnumMenu.h + CaretDataFileSelectionComboBox.h + CaretFileDialog.h + CaretFileDialogExtendable.h + CaretFileRemoteDialog.h + CaretMappableDataFileAndMapSelector.h + CaretMappableDataFileAndMapSelectorObject.h + ChartHistoryViewController.h + ChartLinesSelectionViewController.h + ChartMatrixParcelSelectionViewController.h + ChartMatrixSeriesSelectionViewController.h + ChartTwoOverlaySetViewController.h + ChartTwoOverlayViewController.h + ChartSelectionViewController.h + ChartToolBoxViewController.h + CiftiConnectivityMatrixViewController.h + CiftiParcelSelectionComboBox.h + ClippingPlanesDialog.h + ColorEditorWidget.h + CopyPaletteColorMappingToFilesDialog.h + CustomViewDialog.h + DataFileContentCopyMoveDialog.h + DisplayGroupAndTabItemViewController.h + DisplayGroupEnumComboBox.h + EnumComboBoxTemplate.h + FiberOrientationSelectionViewController.h + FiberSamplesOpenGLWidget.h + FociProjectionDialog.h + FociPropertiesEditorDialog.h + FociSelectionViewController.h + GapsAndMarginsDialog.h + GiftiLabelTableEditor.h + GiftiLabelTableSelectionComboBox.h + GroupAndNameHierarchyViewController.h + GuiManager.h + HelpViewerDialog.h + HyperLinkTextBrowser.h + IdentifyBrainordinateDialog.h + ImageCaptureDialog.h + ImageFileConvertToVolumeFileDialog.h + ImageSelectionViewController.h + InformationDisplayDialog.h + InformationDisplayPropertiesDialog.h + LabelSelectionViewController.h + LockAspectWarningDialog.h + MacApplication.h + MacDockMenu.h + MapSettingsChartTwoLineHistoryWidget.h + MapSettingsColorBarPaletteOptionsWidget.h + MapSettingsColorBarWidget.h + MapSettingsFiberTrajectoryWidget.h + MapSettingsLabelsWidget.h + MapSettingsLayerWidget.h + MapSettingsPaletteColorMappingWidget.h + MapSettingsParcelsWidget.h + MapYokingGroupComboBox.h + MetaDataEditorDialog.h + MetaDataEditorWidget.h + MovieDialog.h + OffScreenOpenGLRenderer.h + OverlaySetViewController.h + OverlaySettingsEditorDialog.h + OverlayViewController.h + PaletteColorMappingEditorDialog.h + PlotMagnifier.h + PlotPanner.h + PreferencesDialog.h + ProgressReportingDialog.h + ProgressReportingFromEvent.h + ProgressReportingWithSlots.h + RegionOfInterestCreateFromBorderDialog.h + SceneBasePathWidget.h + SceneCreateReplaceDialog.h + SceneDialog.h + ScenePreviewDialog.h + SceneShowOptionsDialog.h + SpecFileManagementDialog.h + SplashScreen.h + StructureEnumComboBox.h + StructureSurfaceSelectionControl.h + SurfacePropertiesEditorDialog.h + SurfaceSelectionViewController.h + ThresholdingSetMapsDialog.h + TileTabsConfigurationDialog.h + UserInputModeAnnotationsContextMenu.h + UserInputModeAnnotationsWidget.h + UserInputModeBordersWidget.h + UserInputModeFociWidget.h + UserInputModeImageWidget.h + UserInputModeViewContextMenu.h + UserInputModeVolumeEditWidget.h + UserInputTileTabsContextMenu.h + UsernamePasswordWidget.h + VolumeFileCreateDialog.h + VolumeSurfaceOutlineColorOrTabViewController.h + VolumeSurfaceOutlineSetViewController.h + VolumeSurfaceOutlineViewController.h + WuQCollapsibleWidget.h + WuQDataEntryDialog.h + WuQDialog.h + WuQDialogModal.h + WuQDialogNonModal.h + WuQDoubleSlider.h + WuQDoubleSpinBox.h + WuQEventBlockingFilter.h + WuQGridLayoutGroup.h + WuQGroupBoxExclusiveWidget.h + WuQImageLabel.h + WuQListWidget.h + WuQMessageBox.h + WuQwtPlot.h + WuQSpecialIncrementDoubleSpinBox.h + WuQSpinBoxGroup.h + WuQSpinBoxOddValue.h + WuQTabWidget.h + WuQTextEditorDialog.h + WuQTimedMessageDisplay.h + WuQTreeWidget.h + WuQTrueFalseComboBox.h + #WuQWebView.h + WuQWidget.h + WuQWidgetObjectGroup.h + ZipSceneFileDialog.h + ) + + IF(Qt5_FOUND) + QT5_WRAP_CPP(MOC_SOURCE_FILES ${MOC_INPUT_HEADER_FILES}) + QT5_WRAP_UI(FORMS_HEADERS ${FORMS}) + ENDIF() + IF (QT4_FOUND) + QT4_WRAP_CPP(MOC_SOURCE_FILES ${MOC_INPUT_HEADER_FILES}) + QT4_WRAP_UI(FORMS_HEADERS ${FORMS}) + ENDIF () +ENDIF() # # Header files -# -# If the header file is qt 'moc' file -# also list it in the section above -# named MOC_INPUT_HEADER_FILES # SET(SOURCE_FILES AboutWorkbenchDialog.h @@ -219,6 +256,9 @@ AnnotationWidgetParentEnum.h AnnotationWidthHeightWidget.h BalsaDatabaseManager.h BalsaDatabaseUploadSceneFileDialog.h +BalsaStudySelectionDialog.h +BalsaStudyInformation.h +BalsaUserRoles.h BorderEditingSelectionDialog.h BorderFileSplitDialog.h BorderOptimizeDialog.h @@ -232,6 +272,11 @@ BrainBrowserWindowOrientedToolBox.h BrainBrowserWindowToolBar.h BrainBrowserWindowToolBarChartAttributes.h BrainBrowserWindowToolBarChartAxes.h +BrainBrowserWindowToolBarChartTwoAttributes.h +BrainBrowserWindowToolBarChartTwoAxes.h +BrainBrowserWindowToolBarChartTwoOrientation.h +BrainBrowserWindowToolBarChartTwoTitle.h +BrainBrowserWindowToolBarChartTwoType.h BrainBrowserWindowToolBarChartType.h BrainBrowserWindowToolBarClipping.h BrainBrowserWindowToolBarComponent.h @@ -239,6 +284,7 @@ BrainBrowserWindowToolBarSlicePlane.h BrainBrowserWindowToolBarSliceSelection.h BrainBrowserWindowToolBarSurfaceMontage.h BrainBrowserWindowToolBarTab.h +BrainBrowserWindowToolBarTabPopUpMenu.h BrainBrowserWindowToolBarVolumeMontage.h BrainOpenGLWidget.h BugReportDialog.h @@ -254,12 +300,15 @@ ChartHistoryViewController.h ChartLinesSelectionViewController.h ChartMatrixParcelSelectionViewController.h ChartMatrixSeriesSelectionViewController.h +ChartTwoOverlaySetViewController.h +ChartTwoOverlayViewController.h ChartSelectionViewController.h ChartToolBoxViewController.h CiftiConnectivityMatrixViewController.h CiftiParcelSelectionComboBox.h ClippingPlanesDialog.h ColorEditorWidget.h +CopyPaletteColorMappingToFilesDialog.h CursorDisplayScoped.h CursorEnum.h CursorManager.h @@ -271,10 +320,11 @@ DisplayGroupEnumComboBox.h EnumComboBoxTemplate.h EventAnnotationCreateNewType.h EventAnnotationGetDrawnInWindow.h -EventBrowserWindowContentGet.h +EventBrowserWindowDrawingContent.h EventBrowserWindowCreateTabs.h EventBrowserWindowGraphicsRedrawn.h EventBrowserWindowNew.h +EventBrowserWindowTileTabOperation.h EventGetOrSetUserInputModeProcessor.h EventGraphicsUpdateAllWindows.h EventGraphicsUpdateOneWindow.h @@ -285,6 +335,7 @@ EventMacDockMenuUpdate.h EventOperatingSystemRequestOpenDataFile.h EventOverlaySettingsEditorDialogRequest.h EventPaletteColorMappingEditorDialogRequest.h +EventShowDataFileReadWarningsDialog.h EventUpdateInformationWindows.h EventUpdateVolumeEditingToolBar.h EventUpdateYokedWindows.h @@ -310,8 +361,11 @@ InformationDisplayDialog.h InformationDisplayPropertiesDialog.h KeyEvent.h LabelSelectionViewController.h +LockAspectWarningDialog.h MacApplication.h MacDockMenu.h +MapSettingsChartTwoLineHistoryWidget.h +MapSettingsColorBarPaletteOptionsWidget.h MapSettingsColorBarWidget.h MapSettingsFiberTrajectoryWidget.h MapSettingsLabelsWidget.h @@ -323,6 +377,7 @@ MetaDataEditorDialog.h MetaDataEditorWidget.h MouseEvent.h MovieDialog.h +OffScreenOpenGLRenderer.h OverlaySetViewController.h OverlaySettingsEditorDialog.h OverlayViewController.h @@ -335,9 +390,11 @@ ProgressReportingFromEvent.h ProgressReportingWithSlots.h QGLWidgetTextRenderer.h RegionOfInterestCreateFromBorderDialog.h +SceneBasePathWidget.h SceneCreateReplaceDialog.h SceneDialog.h ScenePreviewDialog.h +SceneShowOptionsDialog.h SceneWindowGeometry.h SpecFileManagementDialog.h SplashScreen.h @@ -345,6 +402,7 @@ StructureEnumComboBox.h StructureSurfaceSelectionControl.h SurfacePropertiesEditorDialog.h SurfaceSelectionViewController.h +ThresholdingSetMapsDialog.h TileTabsConfigurationDialog.h UserInputModeAbstract.h UserInputModeAnnotations.h @@ -360,6 +418,7 @@ UserInputModeView.h UserInputModeViewContextMenu.h UserInputModeVolumeEdit.h UserInputModeVolumeEditWidget.h +UserInputTileTabsContextMenu.h UsernamePasswordWidget.h ViewModeEnum.h VolumeFileCreateDialog.h @@ -372,6 +431,7 @@ WuQDialog.h WuQDialogModal.h WuQDialogNonModal.h WuQDoubleSlider.h +WuQDoubleSpinBox.h WuQEventBlockingFilter.h WuQFactory.h WuQGridLayoutGroup.h @@ -384,11 +444,13 @@ WuQSpecialIncrementDoubleSpinBox.h WuQSpinBoxGroup.h WuQSpinBoxOddValue.h WuQTabWidget.h +WuQTextEditorDialog.h WuQTimedMessageDisplay.h WuQTreeWidget.h WuQTrueFalseComboBox.h #WuQWebView.h WuQWidget.h +WuQWidgetDisabler.h WuQWidgetObjectGroup.h WuQtUtilities.h ZipSceneFileDialog.h @@ -420,6 +482,9 @@ AnnotationWidgetParentEnum.cxx AnnotationWidthHeightWidget.cxx BalsaDatabaseManager.cxx BalsaDatabaseUploadSceneFileDialog.cxx +BalsaStudyInformation.cxx +BalsaUserRoles.cxx +BalsaStudySelectionDialog.cxx BorderEditingSelectionDialog.cxx BorderFileSplitDialog.cxx BorderOptimizeDialog.cxx @@ -433,6 +498,11 @@ BrainBrowserWindowOrientedToolBox.cxx BrainBrowserWindowToolBar.cxx BrainBrowserWindowToolBarChartAttributes.cxx BrainBrowserWindowToolBarChartAxes.cxx +BrainBrowserWindowToolBarChartTwoAttributes.cxx +BrainBrowserWindowToolBarChartTwoAxes.cxx +BrainBrowserWindowToolBarChartTwoOrientation.cxx +BrainBrowserWindowToolBarChartTwoTitle.cxx +BrainBrowserWindowToolBarChartTwoType.cxx BrainBrowserWindowToolBarChartType.cxx BrainBrowserWindowToolBarClipping.cxx BrainBrowserWindowToolBarComponent.cxx @@ -440,6 +510,7 @@ BrainBrowserWindowToolBarSlicePlane.cxx BrainBrowserWindowToolBarSliceSelection.cxx BrainBrowserWindowToolBarSurfaceMontage.cxx BrainBrowserWindowToolBarTab.cxx +BrainBrowserWindowToolBarTabPopUpMenu.cxx BrainBrowserWindowToolBarVolumeMontage.cxx BrainOpenGLWidget.cxx BugReportDialog.cxx @@ -455,12 +526,15 @@ ChartHistoryViewController.cxx ChartLinesSelectionViewController.cxx ChartMatrixParcelSelectionViewController.cxx ChartMatrixSeriesSelectionViewController.cxx +ChartTwoOverlaySetViewController.cxx +ChartTwoOverlayViewController.cxx ChartSelectionViewController.cxx ChartToolBoxViewController.cxx CiftiConnectivityMatrixViewController.cxx CiftiParcelSelectionComboBox.cxx ClippingPlanesDialog.cxx ColorEditorWidget.cxx +CopyPaletteColorMappingToFilesDialog.cxx CursorDisplayScoped.cxx CursorEnum.cxx CursorManager.cxx @@ -471,10 +545,11 @@ DisplayGroupAndTabItemViewController.cxx DisplayGroupEnumComboBox.cxx EventAnnotationCreateNewType.cxx EventAnnotationGetDrawnInWindow.cxx -EventBrowserWindowContentGet.cxx +EventBrowserWindowDrawingContent.cxx EventBrowserWindowCreateTabs.cxx EventBrowserWindowGraphicsRedrawn.cxx EventBrowserWindowNew.cxx +EventBrowserWindowTileTabOperation.cxx EventGetOrSetUserInputModeProcessor.cxx EventGraphicsUpdateAllWindows.cxx EventGraphicsUpdateOneWindow.cxx @@ -485,6 +560,7 @@ EventMacDockMenuUpdate.cxx EventOperatingSystemRequestOpenDataFile.cxx EventOverlaySettingsEditorDialogRequest.cxx EventPaletteColorMappingEditorDialogRequest.cxx +EventShowDataFileReadWarningsDialog.cxx EventUpdateInformationWindows.cxx EventUpdateVolumeEditingToolBar.cxx EventUpdateYokedWindows.cxx @@ -510,8 +586,11 @@ InformationDisplayDialog.cxx InformationDisplayPropertiesDialog.cxx KeyEvent.cxx LabelSelectionViewController.cxx +LockAspectWarningDialog.cxx MacApplication.cxx MacDockMenu.cxx +MapSettingsChartTwoLineHistoryWidget.cxx +MapSettingsColorBarPaletteOptionsWidget.cxx MapSettingsColorBarWidget.cxx MapSettingsFiberTrajectoryWidget.cxx MapSettingsLabelsWidget.cxx @@ -523,6 +602,7 @@ MetaDataEditorDialog.cxx MetaDataEditorWidget.cxx MouseEvent.cxx MovieDialog.cxx +OffScreenOpenGLRenderer.cxx OverlaySetViewController.cxx OverlayViewController.cxx OverlaySettingsEditorDialog.cxx @@ -535,9 +615,11 @@ ProgressReportingFromEvent.cxx ProgressReportingWithSlots.cxx QGLWidgetTextRenderer.cxx RegionOfInterestCreateFromBorderDialog.cxx +SceneBasePathWidget.cxx SceneCreateReplaceDialog.cxx SceneDialog.cxx ScenePreviewDialog.cxx +SceneShowOptionsDialog.cxx SceneWindowGeometry.cxx SpecFileManagementDialog.cxx SplashScreen.cxx @@ -545,6 +627,7 @@ StructureEnumComboBox.cxx StructureSurfaceSelectionControl.cxx SurfacePropertiesEditorDialog.cxx SurfaceSelectionViewController.cxx +ThresholdingSetMapsDialog.cxx TileTabsConfigurationDialog.cxx UserInputModeAbstract.cxx UserInputModeAnnotations.cxx @@ -560,6 +643,7 @@ UserInputModeView.cxx UserInputModeViewContextMenu.cxx UserInputModeVolumeEdit.cxx UserInputModeVolumeEditWidget.cxx +UserInputTileTabsContextMenu.cxx UsernamePasswordWidget.cxx ViewModeEnum.cxx VolumeFileCreateDialog.cxx @@ -572,6 +656,7 @@ WuQDialog.cxx WuQDialogModal.cxx WuQDialogNonModal.cxx WuQDoubleSlider.cxx +WuQDoubleSpinBox.cxx WuQEventBlockingFilter.cxx WuQFactory.cxx WuQGridLayoutGroup.cxx @@ -584,38 +669,35 @@ WuQSpecialIncrementDoubleSpinBox.cxx WuQSpinBoxGroup.cxx WuQSpinBoxOddValue.cxx WuQTabWidget.cxx +WuQTextEditorDialog.cxx WuQTimedMessageDisplay.cxx WuQTreeWidget.cxx WuQTrueFalseComboBox.cxx #WuQWebView.cxx WuQWidget.cxx +WuQWidgetDisabler.cxx WuQWidgetObjectGroup.cxx WuQtUtilities.cxx ZipSceneFileDialog.cxx ) -SET(FORMS - MovieDialog.ui) - - # # Process the header files with moc producing moc_*.cpp files # - INCLUDE_DIRECTORIES(${CMAKE_CURRENT_BINARY_DIR} ${CMAKE_SOURCE_DIR}/GuiQt) -QT4_WRAP_UI(FORMS_HEADERS ${FORMS}) -QT4_WRAP_CPP(MOC_SOURCE_FILES ${MOC_INPUT_HEADER_FILES}) # # Create the GUI library # ADD_LIBRARY(GuiQt -${SOURCE_FILES} -${MOC_SOURCE_FILES} -${FORMS_HEADERS} + ${FORMS_HEADERS} + ${MOC_SOURCE_FILES} + ${SOURCE_FILES} ) +TARGET_LINK_LIBRARIES(GuiQt ${CARET_QT5_LINK}) + INCLUDE_DIRECTORIES( ${CMAKE_SOURCE_DIR}/GuiQt ${Qwt_INCLUDE_DIRS} @@ -628,6 +710,7 @@ ${CMAKE_SOURCE_DIR}/FilesBase ${CMAKE_SOURCE_DIR}/Files ${CMAKE_SOURCE_DIR}/Cifti ${CMAKE_SOURCE_DIR}/Gifti +${CMAKE_SOURCE_DIR}/Graphics ${CMAKE_SOURCE_DIR}/Nifti ${CMAKE_SOURCE_DIR}/OSMesaDummy ${CMAKE_SOURCE_DIR}/Operations diff --git a/src/GuiQt/CaretColorEnumComboBox.cxx b/src/GuiQt/CaretColorEnumComboBox.cxx index f1f31f4333d6e7c86b0875377f114dbbff0d4d2b..e59e78dc25abdf2c62c466fd911076d7fd64d6e0 100644 --- a/src/GuiQt/CaretColorEnumComboBox.cxx +++ b/src/GuiQt/CaretColorEnumComboBox.cxx @@ -25,6 +25,9 @@ #include +#include "CaretAssert.h" +#include "WuQtUtilities.h" + using namespace caret; @@ -35,36 +38,135 @@ using namespace caret; * * Control for selection of Caret Color enumerated types. */ + /** * Constructor. + * * @param parent * Parent object. */ CaretColorEnumComboBox::CaretColorEnumComboBox(QObject* parent) : WuQWidget(parent) +{ + initializeCaretColorComboBox("", + NULL); +} + +/** + * Constructor. + * + * @param customColorSelectionName + * CaretColorEnum::CUSTOM is added to the combo with this name as the text. + * Text must NOT be empty. + * @param parent + * Parent object. + */ +CaretColorEnumComboBox::CaretColorEnumComboBox(const AString& customColorSelectionName, + QObject* parent) +: WuQWidget(parent) +{ + CaretAssert( ! customColorSelectionName.isEmpty()); + initializeCaretColorComboBox(customColorSelectionName, + NULL); +} + +/** + * Constructor. + * + * @param customColorSelectionName + * CaretColorEnum::CUSTOM is added to the combo with this name as the text. + * Text must NOT be empty. + * @param customColorSelectionIcon + * ICon for custom color. + * @param parent + * Parent object. + */ +CaretColorEnumComboBox::CaretColorEnumComboBox(const AString& customColorSelectionName, + const QIcon& customColorSelectionIcon, + QObject* parent) +: WuQWidget(parent) +{ + CaretAssert( ! customColorSelectionName.isEmpty()); + initializeCaretColorComboBox(customColorSelectionName, + &customColorSelectionIcon); +} + +/** + * Destructor. + */ +CaretColorEnumComboBox::~CaretColorEnumComboBox() +{ + +} + +/** + * Initialize instance that may have optional caret color enums. + * + * @param customColorSelectionName + * If NOT empty, CaretColorEnum::CUSTOM is added with text from this name. + * @param customColorSelectionIcon + * ICON for custom color (ignored if NULL) + */ +void +CaretColorEnumComboBox::initializeCaretColorComboBox(const AString& customColorSelectionName, + const QIcon* customColorSelectionIcon) { this->colorComboBox = new QComboBox(); std::vector colors; - CaretColorEnum::getColorEnums(colors); + + int64_t caretColorOptions = 0; + if ( ! customColorSelectionName.isEmpty()) { + caretColorOptions |= CaretColorEnum::OPTION_INCLUDE_CUSTOM_COLOR; + } + CaretColorEnum::getColorAndOptionalEnums(colors, + caretColorOptions); const int32_t numColors = static_cast(colors.size()); for (int32_t i = 0; i < numColors; i++) { const CaretColorEnum::Enum colorEnum = colors[i]; const int32_t indx = this->colorComboBox->count(); - const AString name = CaretColorEnum::toGuiName(colorEnum); + AString name = CaretColorEnum::toGuiName(colorEnum); + if (colorEnum == CaretColorEnum::CUSTOM) { + if ( ! customColorSelectionName.isEmpty()) { + name = customColorSelectionName; + } + } this->colorComboBox->addItem(name); - this->colorComboBox->setItemData(indx, + this->colorComboBox->setItemData(indx, CaretColorEnum::toIntegerCode(colorEnum)); - - const float* rgb = CaretColorEnum::toRGB(colorEnum); - QPixmap pm(10, 10); - pm.fill(QColor::fromRgbF(rgb[0], - rgb[1], - rgb[2])); - QIcon icon(pm); - this->colorComboBox->setItemIcon(indx, - icon); + + /* + * Create an icon with the color. + */ + float rgba[4]; + CaretColorEnum::toRGBAFloat(colorEnum, rgba); + if (colorEnum == CaretColorEnum::NONE) { + rgba[3] = 0.0; + } + else if (colorEnum == CaretColorEnum::CUSTOM) { + /* + * If NO icon for CUSTOM + */ + if (customColorSelectionIcon == NULL) { + rgba[3] = 0.0; + } + } + else { + rgba[3] = 1.0; + } + + if (rgba[3] > 0.0) { + QPixmap pm(WuQtUtilities::createCaretColorEnumPixmap(getWidget(), 10, 10, colorEnum, rgba, false)); + QIcon icon(pm); + if (colorEnum == CaretColorEnum::CUSTOM) { + if (customColorSelectionIcon != NULL) { + icon = *customColorSelectionIcon; + } + } + this->colorComboBox->setItemIcon(indx, + icon); + } } setSelectedColor(CaretColorEnum::BLACK); @@ -73,18 +175,19 @@ CaretColorEnumComboBox::CaretColorEnumComboBox(QObject* parent) } /** - * Destructor. + * @return The actual widget. */ -CaretColorEnumComboBox::~CaretColorEnumComboBox() +QWidget* +CaretColorEnumComboBox::getWidget() { - + return this->colorComboBox; } /** - * @return The actual widget. + * @return The actual combo box encapsulated in this instance. */ -QWidget* -CaretColorEnumComboBox::getWidget() +QComboBox* +CaretColorEnumComboBox::getComboBox() { return this->colorComboBox; } diff --git a/src/GuiQt/CaretColorEnumComboBox.h b/src/GuiQt/CaretColorEnumComboBox.h index afb69c42bd90b1ee3129838e5c016f39235a2e1f..4e991eeee6a22ad415c4b66eb119d520f246e887 100644 --- a/src/GuiQt/CaretColorEnumComboBox.h +++ b/src/GuiQt/CaretColorEnumComboBox.h @@ -36,6 +36,13 @@ namespace caret { public: CaretColorEnumComboBox(QObject* parent); + CaretColorEnumComboBox(const AString& customColorSelectionName, + QObject* parent); + + CaretColorEnumComboBox(const AString& customColorSelectionName, + const QIcon& customColorSelectionIcon, + QObject* parent); + virtual ~CaretColorEnumComboBox(); CaretColorEnum::Enum getSelectedColor(); @@ -44,6 +51,8 @@ namespace caret { QWidget* getWidget(); + QComboBox* getComboBox(); + signals: void colorSelected(const CaretColorEnum::Enum); @@ -57,6 +66,10 @@ namespace caret { private: QComboBox* colorComboBox; + + void initializeCaretColorComboBox(const AString& customColorSelectionName, + const QIcon* customColorSelectionIcon); + }; #ifdef __CARET_COLOR_ENUM_COMBOBOX_DECLARE__ diff --git a/src/GuiQt/CaretColorEnumMenu.cxx b/src/GuiQt/CaretColorEnumMenu.cxx index 947ccd93bcaf1b6d68c5221bebcf2d602921d207..b4364871449491e1fc8279c5f113c37871057c6a 100644 --- a/src/GuiQt/CaretColorEnumMenu.cxx +++ b/src/GuiQt/CaretColorEnumMenu.cxx @@ -88,7 +88,7 @@ CaretColorEnumMenu::initializeCaretColorEnumMenu(const int64_t caretColorOptions * Create an icon with the color. */ float rgba[4]; - CaretColorEnum::toRGBFloat(colorEnum, rgba); + CaretColorEnum::toRGBAFloat(colorEnum, rgba); if (colorEnum == CaretColorEnum::NONE) { rgba[3] = 0.0; } diff --git a/src/GuiQt/CaretFileDialog.cxx b/src/GuiQt/CaretFileDialog.cxx index 8b0d175bd0734386ebc2ea3822f07973a2e61a2d..fb51fee688cc37e5300baa4fc0c5318a2cd9cb17 100644 --- a/src/GuiQt/CaretFileDialog.cxx +++ b/src/GuiQt/CaretFileDialog.cxx @@ -147,10 +147,19 @@ void CaretFileDialog::initializeCaretFileDialog() { /* - * Create a proxy model that hides files that do not match the file filter. - * On Macs, Qt shows files that do not match the file filter as disabled - * but we don't want them displayed. The dialog will take ownership of - * the proxy model so it does not need to be deleted by this instance. + * We MUST use a non-native dialog. + * Otherwise, the file filter proxy model + * will not work. + */ + setOption(QFileDialog::DontUseNativeDialog); + + /* + * Create a proxy model for filtering the data files. + * The proxy is used to limit the displayed files to + * only those with the proper file extension. In addition, + * since CIFTI and NIFTI files end in ".nii", filtering is + * performed so that CIFTI files are not displayed when + * the user selectes Volume files. */ m_filterFilesProxyModel = new FilterFilesProxyModel(); this->setProxyModel(m_filterFilesProxyModel); @@ -179,7 +188,7 @@ void CaretFileDialog::setVisible(bool visible) { if (visible) { - fileFilterWasChanged(selectedFilter()); + fileFilterWasChanged(selectedNameFilter()); } QFileDialog::setVisible(visible); @@ -239,8 +248,54 @@ CaretFileDialog::getOpenFileNameDialog(QWidget *parent, dir, filter); if (selectedFilter != 0) { - cfd.selectFilter(*selectedFilter); + cfd.selectNameFilter(*selectedFilter); + } + cfd.setOptions(options); + cfd.setAcceptMode(CaretFileDialog::AcceptOpen); + cfd.setFileMode(CaretFileDialog::AnyFile); + + if (cfd.exec() == CaretFileDialog::Accepted) { + QStringList selectedFiles = cfd.selectedFiles(); + if (selectedFiles.size() > 0) { + return selectedFiles[0]; + } } + + return QString(); +} + + +/** + * Like QFileDialog::getOpenFileName() except that this + * NEVER uses the native file dialog thus providing + * a consistent user-interface across platforms. + * + * @param dataFileType + * Type of Workbench data file + * @param parent + * Parent on which this dialog is displayed. + * @param caption + * Caption for dialog (if not provided a default caption is shown) + * @param dir + * Directory show by dialog (Brain's current directory if empty string) + * @param options + * Options (see QFileDialog). + * @return + * Name of file selected or empty string if user cancelled. + */ +QString +CaretFileDialog::getOpenFileNameDialog(const DataFileTypeEnum::Enum dataFileType, + QWidget* parent, + const QString& caption, + const QString& dir, + Options options) +{ + + CaretFileDialog cfd(parent, + caption, + dir, + DataFileTypeEnum::toQFileDialogFilter(dataFileType)); + cfd.selectNameFilter(DataFileTypeEnum::toQFileDialogFilter(dataFileType)); cfd.setOptions(options); cfd.setAcceptMode(CaretFileDialog::AcceptOpen); cfd.setFileMode(CaretFileDialog::AnyFile); @@ -288,7 +343,7 @@ CaretFileDialog::getSaveFileNameDialog(QWidget *parent, dir, filter); if (selectedFilter != 0) { - cfd.selectFilter(*selectedFilter); + cfd.selectNameFilter(*selectedFilter); } cfd.setOptions(options); cfd.setAcceptMode(QFileDialog::AcceptSave); @@ -338,7 +393,7 @@ CaretFileDialog::getSaveFileNameDialog(const DataFileTypeEnum::Enum dataFileType caption, dir, DataFileTypeEnum::toQFileDialogFilter(dataFileType)); - cfd.selectFilter(DataFileTypeEnum::toQFileDialogFilter(dataFileType)); + cfd.selectNameFilter(DataFileTypeEnum::toQFileDialogFilter(dataFileType)); cfd.setOptions(options); cfd.setAcceptMode(QFileDialog::AcceptSave); cfd.setFileMode(CaretFileDialog::AnyFile); @@ -387,8 +442,10 @@ CaretFileDialog::getChooseFileNameDialog(const DataFileTypeEnum::Enum dataFileTy QWidget *parent) { CaretFileDialog fd(parent); - fd.setFilter(DataFileTypeEnum::toQFileDialogFilter(dataFileType)); - fd.selectFilter(DataFileTypeEnum::toQFileDialogFilter(dataFileType)); + if (dataFileType != DataFileTypeEnum::UNKNOWN) { + fd.setNameFilter(DataFileTypeEnum::toQFileDialogFilter(dataFileType)); + } + fd.selectNameFilter(DataFileTypeEnum::toQFileDialogFilter(dataFileType)); fd.setAcceptMode(CaretFileDialog::AcceptSave); fd.setFileMode(CaretFileDialog::AnyFile); fd.setViewMode(CaretFileDialog::List); @@ -497,7 +554,7 @@ CaretFileDialog::getOpenFileNamesDialog(QWidget *parent, dir, filter); if (selectedFilter != 0) { - cfd.selectFilter(*selectedFilter); + cfd.selectNameFilter(*selectedFilter); } cfd.setOptions(options); cfd.setAcceptMode(CaretFileDialog::AcceptOpen); @@ -559,12 +616,12 @@ CaretFileDialog::restoreDialogSettings(const AString& settingsName) setDirectory(previousSettings.m_directoryName); } - QStringList dialogFilters = filters(); + QStringList dialogFilters = nameFilters(); QStringListIterator filterIter(dialogFilters); while (filterIter.hasNext()) { const AString filterName = filterIter.next(); if (filterName == previousSettings.m_fileFilterName) { - selectFilter(previousSettings.m_fileFilterName); + selectNameFilter(previousSettings.m_fileFilterName); break; } } diff --git a/src/GuiQt/CaretFileDialog.h b/src/GuiQt/CaretFileDialog.h index 3a159e2b85e783da064580fb74f7c095195d026e..5a435f20e1487c12d55f17cda2ddf2da564e8d49 100644 --- a/src/GuiQt/CaretFileDialog.h +++ b/src/GuiQt/CaretFileDialog.h @@ -52,6 +52,13 @@ namespace caret { QString *selectedFilter = 0, Options options = 0); + // model method to get open file name + static QString getOpenFileNameDialog(const DataFileTypeEnum::Enum dataFileType, + QWidget* parent = 0, + const QString& caption = QString(), + const QString& dir = QString(), + Options options = 0); + // modal method to get save file name static QString getSaveFileNameDialog(QWidget *parent = 0, const QString &caption = QString(), diff --git a/src/GuiQt/CaretFileDialogExtendable.h b/src/GuiQt/CaretFileDialogExtendable.h index 704bd67a6c630362fd6e61475b9fa2a3bc06f030..e7dee837bb0c301ecdc280e1e45997add4184fa5 100644 --- a/src/GuiQt/CaretFileDialogExtendable.h +++ b/src/GuiQt/CaretFileDialogExtendable.h @@ -78,7 +78,7 @@ namespace caret { QFileDialog::FileMode fileMode() const { return m_caretFileDialog->fileMode(); } - QStringList filters() const { return m_caretFileDialog->filters(); } + QStringList filters() const { return m_caretFileDialog->nameFilters(); } QStringList history() const { return m_caretFileDialog->history(); } @@ -92,7 +92,7 @@ namespace caret { QStringList nameFilters() const { return m_caretFileDialog->nameFilters(); } - void open(QObject* receiver, const char* member) { m_caretFileDialog->open(receiver, member); } + //void open(QObject* receiver, const char* member) { m_caretFileDialog->open(receiver, member); } QFileDialog::Options options() const { return m_caretFileDialog->options(); } @@ -124,7 +124,7 @@ namespace caret { void setFileMode(const QFileDialog::FileMode mode) { m_caretFileDialog->setFileMode(mode); } - void setFilter(const QString& filter) { m_caretFileDialog->setFilter(filter); } +// void setFilter(const QString& filter) { m_caretFileDialog->setNameFilter(filter); } void setHistory(const QStringList& paths) { m_caretFileDialog->setHistory(paths); } diff --git a/src/GuiQt/CaretMappableDataFileAndMapSelectorObject.cxx b/src/GuiQt/CaretMappableDataFileAndMapSelectorObject.cxx index 368510ffbc4dd15ac77aac71b9d4389a6d540c55..17e2c374d0a65c5c6839ef00fcea810078cda6c5 100644 --- a/src/GuiQt/CaretMappableDataFileAndMapSelectorObject.cxx +++ b/src/GuiQt/CaretMappableDataFileAndMapSelectorObject.cxx @@ -84,8 +84,7 @@ CaretMappableDataFileAndMapSelectorObject::CaretMappableDataFileAndMapSelectorOb : QObject(parent) { - m_model = new CaretMappableDataFileAndMapSelectionModel(GuiManager::get()->getBrain(), - dataFileType); + m_model = new CaretMappableDataFileAndMapSelectionModel(dataFileType); initializeConstruction(options); @@ -109,8 +108,7 @@ CaretMappableDataFileAndMapSelectorObject::CaretMappableDataFileAndMapSelectorOb : QObject(parent) { - m_model = new CaretMappableDataFileAndMapSelectionModel(GuiManager::get()->getBrain(), - dataFileTypes); + m_model = new CaretMappableDataFileAndMapSelectionModel(dataFileTypes); initializeConstruction(options); diff --git a/src/GuiQt/ChartHistoryViewController.cxx b/src/GuiQt/ChartHistoryViewController.cxx index 9c1c60b9340198d5928d1a81bf52b1f3441b60d1..89d42473b2657f4a131789f2a3afec9f5ad43d28 100644 --- a/src/GuiQt/ChartHistoryViewController.cxx +++ b/src/GuiQt/ChartHistoryViewController.cxx @@ -438,20 +438,20 @@ ChartHistoryViewController::getSelectedChartModelAndTab(ChartModel* &chartModelO ModelChart* modelChart = brain->getChartModel(); if (modelChart != NULL) { - switch (modelChart->getSelectedChartDataType(tabIndexOut)) { - case ChartDataTypeEnum::CHART_DATA_TYPE_INVALID: + switch (modelChart->getSelectedChartOneDataType(tabIndexOut)) { + case ChartOneDataTypeEnum::CHART_DATA_TYPE_INVALID: break; - case ChartDataTypeEnum::CHART_DATA_TYPE_MATRIX_LAYER: + case ChartOneDataTypeEnum::CHART_DATA_TYPE_MATRIX_LAYER: break; - case ChartDataTypeEnum::CHART_DATA_TYPE_MATRIX_SERIES: + case ChartOneDataTypeEnum::CHART_DATA_TYPE_MATRIX_SERIES: break; - case ChartDataTypeEnum::CHART_DATA_TYPE_LINE_DATA_SERIES: + case ChartOneDataTypeEnum::CHART_DATA_TYPE_LINE_DATA_SERIES: chartModelOut = modelChart->getSelectedDataSeriesChartModel(tabIndexOut); break; - case ChartDataTypeEnum::CHART_DATA_TYPE_LINE_FREQUENCY_SERIES: + case ChartOneDataTypeEnum::CHART_DATA_TYPE_LINE_FREQUENCY_SERIES: chartModelOut = modelChart->getSelectedFrequencySeriesChartModel(tabIndexOut); break; - case ChartDataTypeEnum::CHART_DATA_TYPE_LINE_TIME_SERIES: + case ChartOneDataTypeEnum::CHART_DATA_TYPE_LINE_TIME_SERIES: chartModelOut = modelChart->getSelectedTimeSeriesChartModel(tabIndexOut); break; } diff --git a/src/GuiQt/ChartLinesSelectionViewController.cxx b/src/GuiQt/ChartLinesSelectionViewController.cxx index 9e7e7a05311ce26e210f33cf286dc1b4490e905a..76c21e2b1dfc3918d51c6634ad42534e6d9bf808 100644 --- a/src/GuiQt/ChartLinesSelectionViewController.cxx +++ b/src/GuiQt/ChartLinesSelectionViewController.cxx @@ -136,27 +136,27 @@ ChartLinesSelectionViewController::updateSelectionViewController() } const int32_t browserTabIndex = browserTabContent->getTabNumber(); - ChartDataTypeEnum::Enum chartDataType = ChartDataTypeEnum::CHART_DATA_TYPE_INVALID; + ChartOneDataTypeEnum::Enum chartDataType = ChartOneDataTypeEnum::CHART_DATA_TYPE_INVALID; ModelChart* modelChart = brain->getChartModel(); if (modelChart != NULL) { - chartDataType = modelChart->getSelectedChartDataType(browserTabIndex); + chartDataType = modelChart->getSelectedChartOneDataType(browserTabIndex); } bool validFlag = false; switch (chartDataType) { - case ChartDataTypeEnum::CHART_DATA_TYPE_INVALID: + case ChartOneDataTypeEnum::CHART_DATA_TYPE_INVALID: break; - case ChartDataTypeEnum::CHART_DATA_TYPE_MATRIX_LAYER: + case ChartOneDataTypeEnum::CHART_DATA_TYPE_MATRIX_LAYER: break; - case ChartDataTypeEnum::CHART_DATA_TYPE_MATRIX_SERIES: + case ChartOneDataTypeEnum::CHART_DATA_TYPE_MATRIX_SERIES: break; - case ChartDataTypeEnum::CHART_DATA_TYPE_LINE_DATA_SERIES: + case ChartOneDataTypeEnum::CHART_DATA_TYPE_LINE_DATA_SERIES: validFlag = true; break; - case ChartDataTypeEnum::CHART_DATA_TYPE_LINE_FREQUENCY_SERIES: + case ChartOneDataTypeEnum::CHART_DATA_TYPE_LINE_FREQUENCY_SERIES: validFlag = true; break; - case ChartDataTypeEnum::CHART_DATA_TYPE_LINE_TIME_SERIES: + case ChartOneDataTypeEnum::CHART_DATA_TYPE_LINE_TIME_SERIES: validFlag = true; break; } @@ -164,7 +164,7 @@ ChartLinesSelectionViewController::updateSelectionViewController() if (validFlag) { std::vector chartableLineSeriesFilesVector; - const ChartDataTypeEnum::Enum chartDataType = modelChart->getSelectedChartDataType(browserTabIndex); + const ChartOneDataTypeEnum::Enum chartDataType = modelChart->getSelectedChartOneDataType(browserTabIndex); brain->getAllChartableLineSeriesDataFilesForChartDataType(chartDataType, chartableLineSeriesFilesVector); diff --git a/src/GuiQt/ChartMatrixParcelSelectionViewController.cxx b/src/GuiQt/ChartMatrixParcelSelectionViewController.cxx index ce268fc416078f93f603120f1157658d101e0df1..856f89e905353668641a9617ec4505f471f26af7 100644 --- a/src/GuiQt/ChartMatrixParcelSelectionViewController.cxx +++ b/src/GuiQt/ChartMatrixParcelSelectionViewController.cxx @@ -334,13 +334,13 @@ ChartMatrixParcelSelectionViewController::updateSelectionViewController() } const int32_t browserTabIndex = browserTabContent->getTabNumber(); - ChartDataTypeEnum::Enum chartDataType = ChartDataTypeEnum::CHART_DATA_TYPE_INVALID; + ChartOneDataTypeEnum::Enum chartDataType = ChartOneDataTypeEnum::CHART_DATA_TYPE_INVALID; ModelChart* modelChart = brain->getChartModel(); if (modelChart != NULL) { - chartDataType = modelChart->getSelectedChartDataType(browserTabIndex); + chartDataType = modelChart->getSelectedChartOneDataType(browserTabIndex); } - if (chartDataType == ChartDataTypeEnum::CHART_DATA_TYPE_MATRIX_LAYER) { + if (chartDataType == ChartOneDataTypeEnum::CHART_DATA_TYPE_MATRIX_LAYER) { updateMatrixParcelChartWidget(brain, modelChart, browserTabIndex); @@ -819,10 +819,10 @@ ChartMatrixParcelSelectionViewController::getChartMatrixAndProperties(CaretMappa ModelChart* modelChart = brain->getChartModel(); if (modelChart != NULL) { - switch (modelChart->getSelectedChartDataType(browserTabIndexOut)) { - case ChartDataTypeEnum::CHART_DATA_TYPE_INVALID: + switch (modelChart->getSelectedChartOneDataType(browserTabIndexOut)) { + case ChartOneDataTypeEnum::CHART_DATA_TYPE_INVALID: break; - case ChartDataTypeEnum::CHART_DATA_TYPE_MATRIX_LAYER: + case ChartOneDataTypeEnum::CHART_DATA_TYPE_MATRIX_LAYER: { CaretDataFileSelectionModel* parcelFileSelectionModel = modelChart->getChartableMatrixParcelFileSelectionModel(browserTabIndexOut); //m_matrixParcelFileSelectionComboBox->updateComboBox(parcelFileSelectionModel); @@ -839,7 +839,7 @@ ChartMatrixParcelSelectionViewController::getChartMatrixAndProperties(CaretMappa } } break; - case ChartDataTypeEnum::CHART_DATA_TYPE_MATRIX_SERIES: + case ChartOneDataTypeEnum::CHART_DATA_TYPE_MATRIX_SERIES: { CaretDataFileSelectionModel* seriesFileSelectionModel = modelChart->getChartableMatrixSeriesFileSelectionModel(browserTabIndexOut); CaretDataFile* caretSeriesFile = seriesFileSelectionModel->getSelectedFile(); @@ -856,11 +856,11 @@ ChartMatrixParcelSelectionViewController::getChartMatrixAndProperties(CaretMappa } } break; - case ChartDataTypeEnum::CHART_DATA_TYPE_LINE_DATA_SERIES: + case ChartOneDataTypeEnum::CHART_DATA_TYPE_LINE_DATA_SERIES: break; - case ChartDataTypeEnum::CHART_DATA_TYPE_LINE_FREQUENCY_SERIES: + case ChartOneDataTypeEnum::CHART_DATA_TYPE_LINE_FREQUENCY_SERIES: break; - case ChartDataTypeEnum::CHART_DATA_TYPE_LINE_TIME_SERIES: + case ChartOneDataTypeEnum::CHART_DATA_TYPE_LINE_TIME_SERIES: break; } diff --git a/src/GuiQt/ChartMatrixSeriesSelectionViewController.cxx b/src/GuiQt/ChartMatrixSeriesSelectionViewController.cxx index 8d036071e6332d71ac8815dedc0a7aea58bd607a..37e68c1d1ea299169f617368efc3d11d14595a6b 100644 --- a/src/GuiQt/ChartMatrixSeriesSelectionViewController.cxx +++ b/src/GuiQt/ChartMatrixSeriesSelectionViewController.cxx @@ -44,7 +44,6 @@ #include "CaretDataFileSelectionModel.h" #include "CaretLogger.h" #include "CaretMappableDataFile.h" -#include "CaretMappableDataFileAndMapSelectionModel.h" #include "CaretMappableDataFileAndMapSelectorObject.h" #include "ChartableMatrixInterface.h" #include "ChartMatrixDisplayProperties.h" @@ -242,13 +241,13 @@ ChartMatrixSeriesSelectionViewController::updateSelectionViewController() } const int32_t browserTabIndex = browserTabContent->getTabNumber(); - ChartDataTypeEnum::Enum chartDataType = ChartDataTypeEnum::CHART_DATA_TYPE_INVALID; + ChartOneDataTypeEnum::Enum chartDataType = ChartOneDataTypeEnum::CHART_DATA_TYPE_INVALID; ModelChart* modelChart = brain->getChartModel(); if (modelChart != NULL) { - chartDataType = modelChart->getSelectedChartDataType(browserTabIndex); + chartDataType = modelChart->getSelectedChartOneDataType(browserTabIndex); } - if (chartDataType == ChartDataTypeEnum::CHART_DATA_TYPE_MATRIX_SERIES) { + if (chartDataType == ChartOneDataTypeEnum::CHART_DATA_TYPE_MATRIX_SERIES) { CaretMappableDataFile* caretMappableDataFile = NULL; ChartableMatrixInterface* chartableMatrixInterface = NULL; ChartMatrixDisplayProperties* chartMatrixDisplayProperties = NULL; @@ -353,10 +352,10 @@ ChartMatrixSeriesSelectionViewController::getChartMatrixAndProperties(CaretMappa ModelChart* modelChart = brain->getChartModel(); if (modelChart != NULL) { - switch (modelChart->getSelectedChartDataType(browserTabIndexOut)) { - case ChartDataTypeEnum::CHART_DATA_TYPE_INVALID: + switch (modelChart->getSelectedChartOneDataType(browserTabIndexOut)) { + case ChartOneDataTypeEnum::CHART_DATA_TYPE_INVALID: break; - case ChartDataTypeEnum::CHART_DATA_TYPE_MATRIX_LAYER: + case ChartOneDataTypeEnum::CHART_DATA_TYPE_MATRIX_LAYER: { CaretDataFileSelectionModel* parcelFileSelectionModel = modelChart->getChartableMatrixParcelFileSelectionModel(browserTabIndexOut); //m_matrixParcelFileSelectionComboBox->updateComboBox(parcelFileSelectionModel); @@ -373,7 +372,7 @@ ChartMatrixSeriesSelectionViewController::getChartMatrixAndProperties(CaretMappa } } break; - case ChartDataTypeEnum::CHART_DATA_TYPE_MATRIX_SERIES: + case ChartOneDataTypeEnum::CHART_DATA_TYPE_MATRIX_SERIES: { CaretDataFileSelectionModel* seriesFileSelectionModel = modelChart->getChartableMatrixSeriesFileSelectionModel(browserTabIndexOut); CaretDataFile* caretSeriesFile = seriesFileSelectionModel->getSelectedFile(); @@ -390,11 +389,11 @@ ChartMatrixSeriesSelectionViewController::getChartMatrixAndProperties(CaretMappa } } break; - case ChartDataTypeEnum::CHART_DATA_TYPE_LINE_DATA_SERIES: + case ChartOneDataTypeEnum::CHART_DATA_TYPE_LINE_DATA_SERIES: break; - case ChartDataTypeEnum::CHART_DATA_TYPE_LINE_FREQUENCY_SERIES: + case ChartOneDataTypeEnum::CHART_DATA_TYPE_LINE_FREQUENCY_SERIES: break; - case ChartDataTypeEnum::CHART_DATA_TYPE_LINE_TIME_SERIES: + case ChartOneDataTypeEnum::CHART_DATA_TYPE_LINE_TIME_SERIES: break; } } diff --git a/src/GuiQt/ChartSelectionViewController.cxx b/src/GuiQt/ChartSelectionViewController.cxx index e59da9d7f449801c858a49049df2927fa37f70b2..c606229863c9ff3c208b6d06d2e011fe8db1eea4 100644 --- a/src/GuiQt/ChartSelectionViewController.cxx +++ b/src/GuiQt/ChartSelectionViewController.cxx @@ -108,28 +108,28 @@ ChartSelectionViewController::updateSelectionViewController() } const int32_t browserTabIndex = browserTabContent->getTabNumber(); - ChartDataTypeEnum::Enum chartDataType = ChartDataTypeEnum::CHART_DATA_TYPE_INVALID; + ChartOneDataTypeEnum::Enum chartDataType = ChartOneDataTypeEnum::CHART_DATA_TYPE_INVALID; ModelChart* modelChart = brain->getChartModel(); if (modelChart != NULL) { - chartDataType = modelChart->getSelectedChartDataType(browserTabIndex); + chartDataType = modelChart->getSelectedChartOneDataType(browserTabIndex); } switch (chartDataType) { - case ChartDataTypeEnum::CHART_DATA_TYPE_INVALID: + case ChartOneDataTypeEnum::CHART_DATA_TYPE_INVALID: break; - case ChartDataTypeEnum::CHART_DATA_TYPE_MATRIX_LAYER: + case ChartOneDataTypeEnum::CHART_DATA_TYPE_MATRIX_LAYER: m_mode = MODE_MATRIX_LAYER; break; - case ChartDataTypeEnum::CHART_DATA_TYPE_MATRIX_SERIES: + case ChartOneDataTypeEnum::CHART_DATA_TYPE_MATRIX_SERIES: m_mode = MODE_MATRIX_SERIES; break; - case ChartDataTypeEnum::CHART_DATA_TYPE_LINE_DATA_SERIES: + case ChartOneDataTypeEnum::CHART_DATA_TYPE_LINE_DATA_SERIES: m_mode = MODE_BRAINORDINATE; break; - case ChartDataTypeEnum::CHART_DATA_TYPE_LINE_FREQUENCY_SERIES: + case ChartOneDataTypeEnum::CHART_DATA_TYPE_LINE_FREQUENCY_SERIES: m_mode = MODE_BRAINORDINATE; break; - case ChartDataTypeEnum::CHART_DATA_TYPE_LINE_TIME_SERIES: + case ChartOneDataTypeEnum::CHART_DATA_TYPE_LINE_TIME_SERIES: m_mode = MODE_BRAINORDINATE; break; } diff --git a/src/GuiQt/ChartToolBoxViewController.cxx b/src/GuiQt/ChartToolBoxViewController.cxx index b5566c04d0663ee86fcdd5066bf494d49ee1e481..f345685bb6b021f92eb552733f8d9baead70f577 100644 --- a/src/GuiQt/ChartToolBoxViewController.cxx +++ b/src/GuiQt/ChartToolBoxViewController.cxx @@ -121,19 +121,19 @@ ChartToolBoxViewController::receiveEvent(Event* event) if (chartModel != NULL) { switch (chartModel->getChartDataType()) { - case ChartDataTypeEnum::CHART_DATA_TYPE_INVALID: + case ChartOneDataTypeEnum::CHART_DATA_TYPE_INVALID: break; - case ChartDataTypeEnum::CHART_DATA_TYPE_MATRIX_LAYER: + case ChartOneDataTypeEnum::CHART_DATA_TYPE_MATRIX_LAYER: break; - case ChartDataTypeEnum::CHART_DATA_TYPE_MATRIX_SERIES: + case ChartOneDataTypeEnum::CHART_DATA_TYPE_MATRIX_SERIES: break; - case ChartDataTypeEnum::CHART_DATA_TYPE_LINE_DATA_SERIES: + case ChartOneDataTypeEnum::CHART_DATA_TYPE_LINE_DATA_SERIES: historyWidgetValid = true; break; - case ChartDataTypeEnum::CHART_DATA_TYPE_LINE_FREQUENCY_SERIES: + case ChartOneDataTypeEnum::CHART_DATA_TYPE_LINE_FREQUENCY_SERIES: historyWidgetValid = true; break; - case ChartDataTypeEnum::CHART_DATA_TYPE_LINE_TIME_SERIES: + case ChartOneDataTypeEnum::CHART_DATA_TYPE_LINE_TIME_SERIES: historyWidgetValid = true; break; } @@ -170,20 +170,20 @@ ChartToolBoxViewController::getSelectedChartModel() ModelChart* modelChart = brain->getChartModel(); if (modelChart != NULL) { const int32_t browserTabIndex = browserTabContent->getTabNumber(); - switch (modelChart->getSelectedChartDataType(browserTabIndex)) { - case ChartDataTypeEnum::CHART_DATA_TYPE_INVALID: + switch (modelChart->getSelectedChartOneDataType(browserTabIndex)) { + case ChartOneDataTypeEnum::CHART_DATA_TYPE_INVALID: break; - case ChartDataTypeEnum::CHART_DATA_TYPE_MATRIX_LAYER: + case ChartOneDataTypeEnum::CHART_DATA_TYPE_MATRIX_LAYER: break; - case ChartDataTypeEnum::CHART_DATA_TYPE_MATRIX_SERIES: + case ChartOneDataTypeEnum::CHART_DATA_TYPE_MATRIX_SERIES: break; - case ChartDataTypeEnum::CHART_DATA_TYPE_LINE_DATA_SERIES: + case ChartOneDataTypeEnum::CHART_DATA_TYPE_LINE_DATA_SERIES: chartModel = modelChart->getSelectedDataSeriesChartModel(browserTabIndex); break; - case ChartDataTypeEnum::CHART_DATA_TYPE_LINE_FREQUENCY_SERIES: + case ChartOneDataTypeEnum::CHART_DATA_TYPE_LINE_FREQUENCY_SERIES: chartModel = modelChart->getSelectedFrequencySeriesChartModel(browserTabIndex); break; - case ChartDataTypeEnum::CHART_DATA_TYPE_LINE_TIME_SERIES: + case ChartOneDataTypeEnum::CHART_DATA_TYPE_LINE_TIME_SERIES: chartModel = modelChart->getSelectedTimeSeriesChartModel(browserTabIndex); break; } diff --git a/src/GuiQt/ChartTwoOverlaySetViewController.cxx b/src/GuiQt/ChartTwoOverlaySetViewController.cxx new file mode 100644 index 0000000000000000000000000000000000000000..687430db776be54731e742e7a53336e90d494c6f --- /dev/null +++ b/src/GuiQt/ChartTwoOverlaySetViewController.cxx @@ -0,0 +1,429 @@ + +/*LICENSE_START*/ +/* + * Copyright (C) 2016 Washington University School of Medicine + * + * 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 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ +/*LICENSE_END*/ + +#define __CHART_OVERLAY_SET_VIEW_CONTROLLER_DECLARE__ +#include "ChartTwoOverlaySetViewController.h" +#undef __CHART_OVERLAY_SET_VIEW_CONTROLLER_DECLARE__ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "BrainConstants.h" +#include "BrowserTabContent.h" +#include "CaretAssert.h" +#include "ChartTwoOverlay.h" +#include "ChartTwoOverlaySet.h" +#include "ChartTwoOverlayViewController.h" +#include "EventGraphicsUpdateOneWindow.h" +#include "EventManager.h" +#include "EventUserInterfaceUpdate.h" +#include "GuiManager.h" +#include "MapYokingGroupComboBox.h" +#include "WuQGridLayoutGroup.h" +#include "WuQtUtilities.h" + +using namespace caret; + + + +/** + * \class caret::ChartTwoOverlaySetViewController + * \brief View controller for a chart overlay set + * \ingroup GuiQt + */ + +/** + * Constructor. + * @param orientation + * Orientation for layout + * @param browserWindowIndex + * Index of browser window that contains this view controller. + * @param parent + * Parent widget. + */ +ChartTwoOverlaySetViewController::ChartTwoOverlaySetViewController(const Qt::Orientation orientation, + const int32_t browserWindowIndex, + QWidget* parent) +: QWidget(parent), +m_browserWindowIndex(browserWindowIndex) +{ + QWidget* gridWidget = new QWidget(); + QGridLayout* gridLayout = new QGridLayout(gridWidget); + WuQtUtilities::setLayoutSpacingAndMargins(gridLayout, 4, 2); + + for (int32_t i = 0; i < BrainConstants::MAXIMUM_NUMBER_OF_OVERLAYS; i++) { + ChartTwoOverlayViewController* ovc = new ChartTwoOverlayViewController(orientation, + m_browserWindowIndex, + i, + this); + m_chartOverlayViewControllers.push_back(ovc); + m_chartOverlayGridLayoutGroups.push_back(new WuQGridLayoutGroup(gridLayout, + this)); + + QObject::connect(ovc, SIGNAL(requestAddOverlayAbove(const int32_t)), + this, SLOT(processAddOverlayAbove(const int32_t))); + QObject::connect(ovc, SIGNAL(requestAddOverlayBelow(const int32_t)), + this, SLOT(processAddOverlayBelow(const int32_t))); + QObject::connect(ovc, SIGNAL(requestRemoveOverlay(const int32_t)), + this, SLOT(processRemoveOverlay(const int32_t))); + QObject::connect(ovc, SIGNAL(requestMoveOverlayUp(const int32_t)), + this, SLOT(processMoveOverlayUp(const int32_t))); + QObject::connect(ovc, SIGNAL(requestMoveOverlayDown(const int32_t)), + this, SLOT(processMoveOverlayDown(const int32_t))); + } + + if (orientation == Qt::Horizontal) { + static const int COLUMN_ON = 0; + static const int COLUMN_LOAD = 1; + static const int COLUMN_SETTINGS_EDIT = 2; + static const int COLUMN_SETTINGS_COLOR_BAR = 3; + static const int COLUMN_SETTINGS_CONSTRUCTION = 4; + static const int COLUMN_MATRIX_VIEW = 5; + static const int COLUMN_AXIS = 6; + static const int COLUMN_FILE = 7; + static const int COLUMN_YOKE = 8; + static const int COLUMN_MAP_INDEX = 9; + static const int COLUMN_ALL_MAPS = 10; + static const int COLUMN_MAP_NAME = 11; + + gridLayout->setColumnStretch(COLUMN_ON, 0); + gridLayout->setColumnStretch(COLUMN_LOAD, 0); + gridLayout->setColumnStretch(COLUMN_SETTINGS_EDIT, 0); + gridLayout->setColumnStretch(COLUMN_SETTINGS_COLOR_BAR, 0); + gridLayout->setColumnStretch(COLUMN_SETTINGS_CONSTRUCTION, 0); + gridLayout->setColumnStretch(COLUMN_MATRIX_VIEW, 0); + gridLayout->setColumnStretch(COLUMN_AXIS, 0); + gridLayout->setColumnStretch(COLUMN_FILE, 100); + gridLayout->setColumnStretch(COLUMN_YOKE, 0); + gridLayout->setColumnStretch(COLUMN_ALL_MAPS, 0); + gridLayout->setColumnStretch(COLUMN_MAP_INDEX, 0); + gridLayout->setColumnStretch(COLUMN_MAP_NAME, 100); + + QLabel* onLabel = new QLabel("On"); + QLabel* loadLabel = new QLabel("Load"); + QLabel* settingsLabel = new QLabel("Settings"); + QLabel* fileLabel = new QLabel("File"); + QLabel* yokeLabel = new QLabel("Yoke"); + QLabel* allMapsLabel = new QLabel("All"); + m_mapRowOrColumnIndexLabel = new QLabel("Index"); + m_mapRowOrColumnNameLabel = new QLabel("Name"); + + int row = gridLayout->rowCount(); + gridLayout->addWidget(onLabel, row, COLUMN_ON, Qt::AlignHCenter); + gridLayout->addWidget(loadLabel, row, COLUMN_LOAD, Qt::AlignHCenter); + gridLayout->addWidget(settingsLabel, row, COLUMN_SETTINGS_EDIT, 1, 5, Qt::AlignHCenter); + gridLayout->addWidget(fileLabel, row, COLUMN_FILE, Qt::AlignHCenter); + gridLayout->addWidget(yokeLabel, row, COLUMN_YOKE, Qt::AlignHCenter); + gridLayout->addWidget(allMapsLabel, row, COLUMN_ALL_MAPS, Qt::AlignHCenter); + gridLayout->addWidget(m_mapRowOrColumnIndexLabel, row, COLUMN_MAP_INDEX, Qt::AlignHCenter); + gridLayout->addWidget(m_mapRowOrColumnNameLabel, row, COLUMN_MAP_NAME, Qt::AlignHCenter); + + for (int32_t i = 0; i < BrainConstants::MAXIMUM_NUMBER_OF_OVERLAYS; i++) { + row = gridLayout->rowCount(); + ChartTwoOverlayViewController* covc = m_chartOverlayViewControllers[i]; + WuQGridLayoutGroup* glg = m_chartOverlayGridLayoutGroups[i]; + + glg->addWidget(covc->m_enabledCheckBox, row, COLUMN_ON, Qt::AlignHCenter); + glg->addWidget(covc->m_lineSeriesLoadingEnabledCheckBox, row, COLUMN_LOAD, Qt::AlignHCenter); + glg->addWidget(covc->m_settingsToolButton, row, COLUMN_SETTINGS_EDIT, Qt::AlignHCenter); + glg->addWidget(covc->m_colorBarToolButton, row, COLUMN_SETTINGS_COLOR_BAR, Qt::AlignHCenter); + glg->addWidget(covc->m_constructionToolButton, row, COLUMN_SETTINGS_CONSTRUCTION, Qt::AlignHCenter); + glg->addWidget(covc->m_matrixTriangularViewModeToolButton, row, COLUMN_MATRIX_VIEW, Qt::AlignHCenter); + glg->addWidget(covc->m_axisLocationToolButton, row, COLUMN_AXIS, Qt::AlignHCenter); + glg->addWidget(covc->m_mapFileComboBox, row, COLUMN_FILE); + glg->addWidget(covc->m_mapRowOrColumnYokingGroupComboBox->getWidget(), row, COLUMN_YOKE, Qt::AlignHCenter); + glg->addWidget(covc->m_allMapsCheckBox, row, COLUMN_ALL_MAPS, Qt::AlignHCenter); + glg->addWidget(covc->m_mapRowOrColumnIndexSpinBox, row, COLUMN_MAP_INDEX, Qt::AlignHCenter); + glg->addWidget(covc->m_mapRowOrColumnNameComboBox, row, COLUMN_MAP_NAME); + } + } + else { + static const int ROW_ONE_COLUMN_ON = 0; + static const int ROW_ONE_COLUMN_SETTINGS_EDIT = 1; + static const int ROW_ONE_COLUMN_SETTINGS_COLOR_BAR = 2; + static const int ROW_ONE_COLUMN_SETTINGS_CONSTRUCTION = 3; + static const int ROW_ONE_COLUMN_MATRIX_VIEW = 4; + static const int ROW_ONE_COLUMN_AXIS = 5; + static const int ROW_ONE_COLUMN_FILE_LABEL = 6; + static const int ROW_ONE_COLUMN_FILE_COMBO_BOX = 7; + + static const int ROW_TWO_COLUMN_LOAD = 0; + static const int ROW_TWO_COLUMN_YOKE = 2; + static const int ROW_TWO_COLUMN_ALL_MAPS = 4; + static const int ROW_TWO_COLUMN_MAP_INDEX = 6; + static const int ROW_TWO_COLUMN_MAP_NAME = 7; + + gridLayout->setColumnStretch(0, 0); + gridLayout->setColumnStretch(1, 0); + gridLayout->setColumnStretch(2, 0); + gridLayout->setColumnStretch(3, 0); + gridLayout->setColumnStretch(4, 0); + gridLayout->setColumnStretch(5, 0); + gridLayout->setColumnStretch(6, 0); + gridLayout->setColumnStretch(7, 100); + + for (int32_t i = 0; i < BrainConstants::MAXIMUM_NUMBER_OF_OVERLAYS; i++) { + WuQGridLayoutGroup* glg = m_chartOverlayGridLayoutGroups[i]; + + int row = gridLayout->rowCount(); + if (i > 0) { + QFrame* bottomHorizontalLineWidget = new QFrame(); + bottomHorizontalLineWidget->setLineWidth(0); + bottomHorizontalLineWidget->setMidLineWidth(1); + bottomHorizontalLineWidget->setFrameStyle(QFrame::HLine | QFrame::Raised); + glg->addWidget(bottomHorizontalLineWidget, + row, 0, 1, -1); + row++; + } + + ChartTwoOverlayViewController* covc = m_chartOverlayViewControllers[i]; + + QLabel* fileLabel = new QLabel("File"); + glg->addWidget(covc->m_enabledCheckBox, row, ROW_ONE_COLUMN_ON, Qt::AlignLeft); + glg->addWidget(covc->m_settingsToolButton, row, ROW_ONE_COLUMN_SETTINGS_EDIT, Qt::AlignHCenter); + glg->addWidget(covc->m_colorBarToolButton, row, ROW_ONE_COLUMN_SETTINGS_COLOR_BAR, Qt::AlignHCenter); + glg->addWidget(covc->m_constructionToolButton, row, ROW_ONE_COLUMN_SETTINGS_CONSTRUCTION, Qt::AlignHCenter); + glg->addWidget(covc->m_matrixTriangularViewModeToolButton, row, ROW_ONE_COLUMN_MATRIX_VIEW, Qt::AlignHCenter); + glg->addWidget(covc->m_axisLocationToolButton, row, ROW_ONE_COLUMN_AXIS, Qt::AlignHCenter); + glg->addWidget(fileLabel, row, ROW_ONE_COLUMN_FILE_LABEL, Qt::AlignHCenter); + glg->addWidget(covc->m_mapFileComboBox, row, ROW_ONE_COLUMN_FILE_COMBO_BOX, 1, 2); + row++; + + glg->addWidget(covc->m_lineSeriesLoadingEnabledCheckBox, row, ROW_TWO_COLUMN_LOAD, Qt::AlignLeft); + glg->addWidget(covc->m_mapRowOrColumnYokingGroupComboBox->getWidget(), row, ROW_TWO_COLUMN_YOKE, 1, 2, Qt::AlignHCenter); + glg->addWidget(covc->m_allMapsCheckBox, row, ROW_TWO_COLUMN_ALL_MAPS, 1, 2, Qt::AlignHCenter); + glg->addWidget(covc->m_mapRowOrColumnIndexSpinBox, row, ROW_TWO_COLUMN_MAP_INDEX, Qt::AlignHCenter); + glg->addWidget(covc->m_mapRowOrColumnNameComboBox, row, ROW_TWO_COLUMN_MAP_NAME); + } + } + + if (orientation == Qt::Horizontal) { + QVBoxLayout* verticalLayout = new QVBoxLayout(this); + WuQtUtilities::setLayoutSpacingAndMargins(verticalLayout, 2, 2); + verticalLayout->addWidget(gridWidget); + verticalLayout->addStretch(); + } + else { + /* + * Resolve WB-649 + */ + QVBoxLayout* verticalLayout = new QVBoxLayout(this); + WuQtUtilities::setLayoutSpacingAndMargins(verticalLayout, 1, 1); + verticalLayout->addWidget(gridWidget); + verticalLayout->addStretch(); + } + + EventManager::get()->addEventListener(this, EventTypeEnum::EVENT_USER_INTERFACE_UPDATE); +} + +/** + * Destructor. + */ +ChartTwoOverlaySetViewController::~ChartTwoOverlaySetViewController() +{ + EventManager::get()->removeAllEventsFromListener(this); +} + +/** + * Receive an event. + * + * @param event + * An event for which this instance is listening. + */ +void +ChartTwoOverlaySetViewController::receiveEvent(Event* event) +{ + if (event->getEventType() == EventTypeEnum::EVENT_USER_INTERFACE_UPDATE) { + EventUserInterfaceUpdate* uiEvent = + dynamic_cast(event); + CaretAssert(uiEvent); + + if (uiEvent->isUpdateForWindow(m_browserWindowIndex)) { + if (uiEvent->isToolBoxUpdate()) { + this->updateViewController(); + uiEvent->setEventProcessed(); + } + } + } +} + +/** + * Update this overlay set view controller using the given overlay set. + */ +void +ChartTwoOverlaySetViewController::updateViewController() +{ + ChartTwoOverlaySet* chartOverlaySet = getChartTwoOverlaySet(); + if (chartOverlaySet == NULL) { + return; + } + + const int32_t numberOfOverlays = static_cast(m_chartOverlayViewControllers.size()); + const int32_t numberOfDisplayedOverlays = chartOverlaySet->getNumberOfDisplayedOverlays(); + + for (int32_t i = 0; i < numberOfOverlays; i++) { + ChartTwoOverlay* chartOverlay = NULL; + if (chartOverlaySet != NULL) { + chartOverlay = chartOverlaySet->getOverlay(i); + } + m_chartOverlayViewControllers[i]->updateViewController(chartOverlay); + + bool displayOverlay = (chartOverlay != NULL); + if (i >= numberOfDisplayedOverlays) { + displayOverlay = false; + } + CaretAssertVectorIndex(m_chartOverlayGridLayoutGroups, i); + m_chartOverlayGridLayoutGroups[i]->setVisible(displayOverlay); + } + +// const ChartTwoOverlay* primaryOverlay = chartOverlaySet->getPrimaryOverlay(); +// if (primaryOverlay != NULL) { +// AString mapRowOrColumnName = "Map"; +// switch (primaryOverlay->getChartTwoDataType()) { +// case ChartTwoDataTypeEnum::CHART_DATA_TYPE_INVALID: +// break; +// case ChartTwoDataTypeEnum::CHART_DATA_TYPE_HISTOGRAM: +// break; +// case ChartTwoDataTypeEnum::CHART_DATA_TYPE_LINE_SERIES: +// break; +// case ChartTwoDataTypeEnum::CHART_DATA_TYPE_MATRIX: +// mapRowOrColumnName = "Row"; +// break; +// } +// +// if (m_mapRowOrColumnIndexLabel != NULL) { +// m_mapRowOrColumnIndexLabel->setText(mapRowOrColumnName+ " Index"); +// } +// if (m_mapRowOrColumnNameLabel != NULL) { +// m_mapRowOrColumnNameLabel->setText(mapRowOrColumnName + " Name"); +// } +// } +} + +/** + * @return The overlay set in this view controller. + */ +ChartTwoOverlaySet* +ChartTwoOverlaySetViewController::getChartTwoOverlaySet() +{ + ChartTwoOverlaySet* chartOverlaySet = NULL; + BrowserTabContent* browserTabContent = + GuiManager::get()->getBrowserTabContentForBrowserWindow(m_browserWindowIndex, true); + if (browserTabContent != NULL) { + chartOverlaySet = browserTabContent->getChartTwoOverlaySet(); + } + + return chartOverlaySet; +} + +/** + * Add an overlay above the overlay with the given index. + * @param overlayIndex + * Index of overlay that will have an overlay added above it. + */ +void +ChartTwoOverlaySetViewController::processAddOverlayAbove(const int32_t overlayIndex) +{ + ChartTwoOverlaySet* chartOverlaySet = getChartTwoOverlaySet(); + if (chartOverlaySet != NULL) { + chartOverlaySet->insertOverlayAbove(overlayIndex); + this->updateColoringAndGraphics(); + } +} + +/** + * Add an overlay below the overlay with the given index. + * @param overlayIndex + * Index of overlay that will have an overlay added below it. + */ +void +ChartTwoOverlaySetViewController::processAddOverlayBelow(const int32_t overlayIndex) +{ + ChartTwoOverlaySet* chartOverlaySet = getChartTwoOverlaySet(); + if (chartOverlaySet != NULL) { + chartOverlaySet->insertOverlayBelow(overlayIndex); + this->updateColoringAndGraphics(); + } +} + +/** + * Remove an overlay above the overlay with the given index. + * @param overlayIndex + * Index of overlay that will be removed + */ +void +ChartTwoOverlaySetViewController::processRemoveOverlay(const int32_t overlayIndex) +{ + ChartTwoOverlaySet* chartOverlaySet = getChartTwoOverlaySet(); + if (chartOverlaySet != NULL) { + chartOverlaySet->removeDisplayedOverlay(overlayIndex); + this->updateColoringAndGraphics(); + } +} + +/** + * Remove an overlay above the overlay with the given index. + * @param overlayIndex + * Index of overlay that will be removed + */ +void +ChartTwoOverlaySetViewController::processMoveOverlayDown(const int32_t overlayIndex) +{ + ChartTwoOverlaySet* chartOverlaySet = getChartTwoOverlaySet(); + if (chartOverlaySet != NULL) { + chartOverlaySet->moveDisplayedOverlayDown(overlayIndex); + this->updateColoringAndGraphics(); + } +} + +/** + * Remove an overlay above the overlay with the given index. + * @param overlayIndex + * Index of overlay that will be removed + */ +void +ChartTwoOverlaySetViewController::processMoveOverlayUp(const int32_t overlayIndex) +{ + ChartTwoOverlaySet* chartOverlaySet = getChartTwoOverlaySet(); + if (chartOverlaySet != NULL) { + chartOverlaySet->moveDisplayedOverlayUp(overlayIndex); + this->updateColoringAndGraphics(); + } +} + +/** + * Update surface coloring and graphics after overlay changes. + */ +void +ChartTwoOverlaySetViewController::updateColoringAndGraphics() +{ + this->updateViewController(); + + EventGraphicsUpdateOneWindow graphicsUpdate(m_browserWindowIndex); + EventManager::get()->sendEvent(graphicsUpdate.getPointer()); +} diff --git a/src/GuiQt/ChartTwoOverlaySetViewController.h b/src/GuiQt/ChartTwoOverlaySetViewController.h new file mode 100644 index 0000000000000000000000000000000000000000..f637eae69838b5c779943675016e76ce340de6aa --- /dev/null +++ b/src/GuiQt/ChartTwoOverlaySetViewController.h @@ -0,0 +1,96 @@ +#ifndef __CHART_TWO_OVERLAY_SET_VIEW_CONTROLLER_H__ +#define __CHART_TWO_OVERLAY_SET_VIEW_CONTROLLER_H__ + +/*LICENSE_START*/ +/* + * Copyright (C) 2016 Washington University School of Medicine + * + * 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 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ +/*LICENSE_END*/ + + +#include + +#include "EventListenerInterface.h" + +class QLabel; +class QScrollArea; + +namespace caret { + class ChartTwoOverlaySet; + class ChartTwoOverlayViewController; + class WuQGridLayoutGroup; + + class ChartTwoOverlaySetViewController : public QWidget, public EventListenerInterface { + + Q_OBJECT + + public: + ChartTwoOverlaySetViewController(const Qt::Orientation orientation, + const int32_t browserWindowIndex, + QWidget* parent = 0); + + virtual ~ChartTwoOverlaySetViewController(); + + + // ADD_NEW_METHODS_HERE + + virtual void receiveEvent(Event* event); + + private slots: + void processAddOverlayAbove(const int32_t overlayIndex); + + void processAddOverlayBelow(const int32_t overlayIndex); + + void processRemoveOverlay(const int32_t overlayIndex); + + void processMoveOverlayDown(const int32_t overlayIndex); + + void processMoveOverlayUp(const int32_t overlayIndex); + + private: + ChartTwoOverlaySetViewController(const ChartTwoOverlaySetViewController&); + + ChartTwoOverlaySetViewController& operator=(const ChartTwoOverlaySetViewController&); + + ChartTwoOverlaySet* getChartTwoOverlaySet(); + + void updateViewController(); + + void updateColoringAndGraphics(); + + const int32_t m_browserWindowIndex; + + std::vector m_chartOverlayViewControllers; + + std::vector m_chartOverlayGridLayoutGroups; + + QScrollArea* m_scrollArea; + + QLabel* m_mapRowOrColumnIndexLabel = NULL; + + QLabel* m_mapRowOrColumnNameLabel = NULL; + + // ADD_NEW_MEMBERS_HERE + + }; + +#ifdef __CHART_TWO_OVERLAY_SET_VIEW_CONTROLLER_DECLARE__ + // +#endif // __CHART_OVERLAY_SET_VIEW_CONTROLLER_DECLARE__ + +} // namespace +#endif //__CHART_TWO_OVERLAY_SET_VIEW_CONTROLLER_H__ diff --git a/src/GuiQt/ChartTwoOverlayViewController.cxx b/src/GuiQt/ChartTwoOverlayViewController.cxx new file mode 100644 index 0000000000000000000000000000000000000000..7df8ef9ed569ab6a5c785a504091215add04d62e --- /dev/null +++ b/src/GuiQt/ChartTwoOverlayViewController.cxx @@ -0,0 +1,1401 @@ + +/*LICENSE_START*/ +/* + * Copyright (C) 2016 Washington University School of Medicine + * + * 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 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ +/*LICENSE_END*/ + +#define __CHART_TWO_OVERLAY_VIEW_CONTROLLER_DECLARE__ +#include "ChartTwoOverlayViewController.h" +#undef __CHART_TWO_OVERLAY_VIEW_CONTROLLER_DECLARE__ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "CaretAssert.h" +using namespace caret; + +#include "AnnotationColorBar.h" +#include "Brain.h" +#include "CaretMappableDataFile.h" +#include "ChartTwoOverlay.h" +#include "ChartableTwoFileDelegate.h" +#include "ChartableTwoFileHistogramChart.h" +#include "ChartableTwoFileLineSeriesChart.h" +#include "ChartableTwoFileMatrixChart.h" +#include "EventDataFileReload.h" +#include "EventGraphicsUpdateAllWindows.h" +#include "EventGraphicsUpdateOneWindow.h" +#include "EventManager.h" +#include "EventMapYokingSelectMap.h" +#include "EventOverlaySettingsEditorDialogRequest.h" +#include "EventSurfaceColoringInvalidate.h" +#include "EventUserInterfaceUpdate.h" +#include "FileInformation.h" +#include "FilePathNamePrefixCompactor.h" +#include "GuiManager.h" +#include "MapYokingGroupComboBox.h" +#include "UsernamePasswordWidget.h" +#include "WuQFactory.h" +#include "WuQMessageBox.h" +#include "WuQtUtilities.h" + +/** + * \class caret::ChartTwoOverlayViewController + * \brief View controller for a chart overlay + * \ingroup GuiQt + */ + +/** + * Constructor. + * + * @param orientation + * Orientation of view controller. + * @param browserWindowIndex + * Index of browser window in which this view controller resides. + * @param chartOverlayIndex + * Index of this overlay view controller. + * @param parent + * The parent widget. + */ +ChartTwoOverlayViewController::ChartTwoOverlayViewController(const Qt::Orientation orientation, + const int32_t browserWindowIndex, + const int32_t chartOverlayIndex, + QObject* parent) +: QObject(parent), +m_browserWindowIndex(browserWindowIndex), +m_chartOverlayIndex(chartOverlayIndex), +m_chartOverlay(NULL) +{ + int minComboBoxWidth = 200; + int maxComboBoxWidth = 100000; //400; + if (orientation == Qt::Horizontal) { + minComboBoxWidth = 50; + maxComboBoxWidth = 100000; + } + const QComboBox::SizeAdjustPolicy comboSizePolicy = QComboBox::AdjustToContentsOnFirstShow; //QComboBox::AdjustToContents; + + /* + * Enabled Check Box + */ + const QString enabledCheckboxText = ((orientation == Qt::Horizontal) ? " " : "On"); + m_enabledCheckBox = new QCheckBox(enabledCheckboxText); + QObject::connect(m_enabledCheckBox, SIGNAL(clicked(bool)), + this, SLOT(enabledCheckBoxClicked(bool))); + m_enabledCheckBox->setToolTip("Display line charts from the selected file"); + + /* + * Line Series Enabled Check Box + */ + const QString loadingCheckboxText = ((orientation == Qt::Horizontal) ? " " : "Load"); + m_lineSeriesLoadingEnabledCheckBox = new QCheckBox(loadingCheckboxText); + QObject::connect(m_lineSeriesLoadingEnabledCheckBox, &QCheckBox::clicked, + this, &ChartTwoOverlayViewController::lineSeriesLoadingEnabledCheckBoxClicked); + m_lineSeriesLoadingEnabledCheckBox->setToolTip("Enable loading of line charts for the selected file"); + + /* + * Settings Tool Button + */ + QIcon settingsIcon; + const bool settingsIconValid = WuQtUtilities::loadIcon(":/LayersPanel/wrench.png", + settingsIcon); + + m_settingsAction = WuQtUtilities::createAction("S", + "Edit settings for this chart", + this, + this, + SLOT(settingsActionTriggered())); + if (settingsIconValid) { + m_settingsAction->setIcon(settingsIcon); + } + m_settingsToolButton = new QToolButton(); + m_settingsToolButton->setDefaultAction(m_settingsAction); + + /* + * ColorBar Tool Button + */ + QIcon colorBarIcon; + const bool colorBarIconValid = WuQtUtilities::loadIcon(":/LayersPanel/colorbar.png", + colorBarIcon); + m_colorBarAction = WuQtUtilities::createAction("CB", + "Display color bar for this overlay", + this, + this, + SLOT(colorBarActionTriggered(bool))); + m_colorBarAction->setCheckable(true); + if (colorBarIconValid) { + m_colorBarAction->setIcon(colorBarIcon); + } + m_colorBarToolButton = new QToolButton(); + m_colorBarToolButton->setDefaultAction(m_colorBarAction); + + /* + * Construction Tool Button + */ + QIcon constructionIcon; + const bool constructionIconValid = WuQtUtilities::loadIcon(":/LayersPanel/construction.png", + constructionIcon); + m_constructionAction = WuQtUtilities::createAction("C", + "Add/Move/Remove Layers", + this); + if (constructionIconValid) { + m_constructionAction->setIcon(constructionIcon); + } + m_constructionToolButton = new QToolButton(); + QMenu* constructionMenu = createConstructionMenu(m_constructionToolButton); + m_constructionAction->setMenu(constructionMenu); + m_constructionToolButton->setDefaultAction(m_constructionAction); + m_constructionToolButton->setPopupMode(QToolButton::InstantPopup); + + /* + * Matrix triangular view mode button + */ + m_matrixTriangularViewModeAction = WuQtUtilities::createAction("M", + "Select a triangular view of the matrix", + this); + m_matrixTriangularViewModeToolButton = new QToolButton(); + QMenu* matrixTriangularViewModeMenu = createMatrixTriangularViewModeMenu(m_matrixTriangularViewModeToolButton); + m_matrixTriangularViewModeAction->setMenu(matrixTriangularViewModeMenu); + m_matrixTriangularViewModeToolButton->setDefaultAction(m_matrixTriangularViewModeAction); + m_matrixTriangularViewModeToolButton->setPopupMode(QToolButton::InstantPopup); + + + /* + * Axis location button + */ + m_axisLocationAction = WuQtUtilities::createAction("A", + "Select location of vertical axis for the selected file", + this); + m_axisLocationToolButton = new QToolButton(); + QMenu* axisLocationMenu = createAxisLocationMenu(m_axisLocationToolButton); + m_axisLocationAction->setMenu(axisLocationMenu); + m_axisLocationToolButton->setDefaultAction(m_axisLocationAction); + m_axisLocationToolButton->setPopupMode(QToolButton::InstantPopup); + + /* + * Map file Selection Check Box + */ + m_mapFileComboBox = WuQFactory::newComboBox(); + m_mapFileComboBox->setMinimumWidth(minComboBoxWidth); + m_mapFileComboBox->setMaximumWidth(maxComboBoxWidth); + QObject::connect(m_mapFileComboBox, SIGNAL(activated(int)), + this, SLOT(fileComboBoxSelected(int))); + m_mapFileComboBox->setToolTip("Selects file for this overlay"); + m_mapFileComboBox->setSizeAdjustPolicy(comboSizePolicy); + + /* + * Yoking Group + */ + const AString yokeToolTip = ("Select a yoking group.\n" + "\n" + "When files with more than one map are yoked,\n" + "the seleted maps are synchronized by map index.\n" + "\n" + "If the SAME FILE is in yoked in multiple overlays,\n" + "the overlay enabled statuses are synchronized.\n"); + m_mapRowOrColumnYokingGroupComboBox = new MapYokingGroupComboBox(this); + m_mapRowOrColumnYokingGroupComboBox->getWidget()->setStatusTip("Synchronize enabled status and map indices)"); + m_mapRowOrColumnYokingGroupComboBox->getWidget()->setToolTip("Yoke to Overlay Mapped Files"); +#ifdef CARET_OS_MACOSX + m_mapRowOrColumnYokingGroupComboBox->getWidget()->setFixedWidth(m_mapRowOrColumnYokingGroupComboBox->getWidget()->sizeHint().width() - 20); +#endif // CARET_OS_MACOSX + QObject::connect(m_mapRowOrColumnYokingGroupComboBox, SIGNAL(itemActivated()), + this, SLOT(yokingGroupActivated())); + + /* + * All maps check box + */ + m_allMapsCheckBox = new QCheckBox("All Maps"); + m_allMapsCheckBox->setToolTip("Show histogram of all maps"); + if (orientation == Qt::Horizontal) { + m_allMapsCheckBox->setText(" "); + } + QObject::connect(m_allMapsCheckBox, SIGNAL(clicked(bool)), + this, SLOT(allMapsCheckBoxClicked(bool))); + + /* + * Map/Row/Column Index Spin Box + */ + m_mapRowOrColumnIndexSpinBox = WuQFactory::newSpinBox(); + QObject::connect(m_mapRowOrColumnIndexSpinBox, SIGNAL(valueChanged(int)), + this, SLOT(mapRowOrColumnIndexSpinBoxValueChanged(int))); + m_mapRowOrColumnIndexSpinBox->setToolTip("Select map/row/column by its index"); + m_mapRowOrColumnIndexSpinBox->setRange(1, 9999); // fix size for 4 digits + m_mapRowOrColumnIndexSpinBox->setFixedSize(m_mapRowOrColumnIndexSpinBox->sizeHint()); + m_mapRowOrColumnIndexSpinBox->setRange(1, 1); + m_mapRowOrColumnIndexSpinBox->setValue(1); + + /* + * Map/Row/Column Name Combo Box + */ + m_mapRowOrColumnNameComboBox = WuQFactory::newComboBox(); + m_mapRowOrColumnNameComboBox->setMinimumWidth(minComboBoxWidth); + m_mapRowOrColumnNameComboBox->setMaximumWidth(maxComboBoxWidth); + QObject::connect(m_mapRowOrColumnNameComboBox, SIGNAL(activated(int)), + this, SLOT(mapRowOrColumnNameComboBoxSelected(int))); + m_mapRowOrColumnNameComboBox->setToolTip("Select map/row/column by its name"); + m_mapRowOrColumnNameComboBox->setSizeAdjustPolicy(comboSizePolicy); +} + +/** + * Destructor. + */ +ChartTwoOverlayViewController::~ChartTwoOverlayViewController() +{ +} + +/* + * If this overlay ins an overlay settings editor, update its content + */ +void +ChartTwoOverlayViewController::updateOverlaySettingsEditor() +{ + if (m_chartOverlay == NULL) { + return; + } + + CaretMappableDataFile* mapFile = NULL; + ChartTwoOverlay::SelectedIndexType selectedIndexType = ChartTwoOverlay::SelectedIndexType::INVALID; + int32_t selectedIndex = -1; + m_chartOverlay->getSelectionData(mapFile, + selectedIndexType, + selectedIndex); + + if ((mapFile != NULL) + && (selectedIndex >= 0)) { + EventOverlaySettingsEditorDialogRequest pcme(EventOverlaySettingsEditorDialogRequest::MODE_OVERLAY_MAP_CHANGED, + m_browserWindowIndex, + m_chartOverlay, + mapFile, + selectedIndexType, + selectedIndex); + EventManager::get()->sendEvent(pcme.getPointer()); + } +} + +/** + * Called when a selection is made from the file combo box. + * @parm indx + * Index of selection. + */ +void +ChartTwoOverlayViewController::fileComboBoxSelected(int indx) +{ + if (m_chartOverlay == NULL) { + return; + } + + void* pointer = m_mapFileComboBox->itemData(indx).value(); + CaretMappableDataFile* file = (CaretMappableDataFile*)pointer; + m_chartOverlay->setSelectionData(file, -1); + + updateViewController(m_chartOverlay); + m_mapRowOrColumnYokingGroupComboBox->validateYokingChange(m_chartOverlay); + updateUserInterfaceAndGraphicsWindow(); + updateOverlaySettingsEditor(); +} + +/** + * Called when a selection is made from the map index spin box. + * @parm indxIn + * Index of selection. + */ +void +ChartTwoOverlayViewController::mapRowOrColumnIndexSpinBoxValueChanged(int indxIn) +{ + if (m_chartOverlay == NULL) + { + //TSC: not sure how to put the displayed integer back to 0 where it starts when opening without data files + return; + } + + const bool focusFlag = m_mapRowOrColumnIndexSpinBox->hasFocus(); + + /* + * Get the file that is selected from the file combo box + */ + const int32_t fileIndex = m_mapFileComboBox->currentIndex(); + void* pointer = m_mapFileComboBox->itemData(fileIndex).value(); + CaretMappableDataFile* file = (CaretMappableDataFile*)pointer; + + /* + * Spin box may range [0, N-1] or [1, N] but in source code + * indices are always [0, N-1] + */ + const int32_t indx = indxIn - m_mapRowOrColumnIndexSpinBox->minimum(); + m_chartOverlay->setSelectionData(file, indx); + + const MapYokingGroupEnum::Enum mapYoking = m_chartOverlay->getMapYokingGroup(); + if (mapYoking != MapYokingGroupEnum::MAP_YOKING_GROUP_OFF) { + EventMapYokingSelectMap selectMapEvent(mapYoking, + file, + indx, + m_chartOverlay->isEnabled()); + EventManager::get()->sendEvent(selectMapEvent.getPointer()); + } + + /* + * Need to update map name combo box. + */ + m_mapRowOrColumnNameComboBox->blockSignals(true); + if ((indx >= 0) + && (indx < m_mapRowOrColumnNameComboBox->count())) { + m_mapRowOrColumnNameComboBox->setCurrentIndex(indx); + } + m_mapRowOrColumnNameComboBox->blockSignals(false); + + this->updateUserInterfaceAndGraphicsWindow(); + + updateOverlaySettingsEditor(); + + /* + * User interface update may cause loss of focus so restore it + */ + if (focusFlag) { + m_mapRowOrColumnIndexSpinBox->setFocus(); + } +} + +/** + * Called when a selection is made from the map name combo box. + * @parm indx + * Index of selection. + */ +void +ChartTwoOverlayViewController::mapRowOrColumnNameComboBoxSelected(int indx) +{ + if (m_chartOverlay == NULL) { + return; + } + + /* + * Get the file that is selected from the file combo box + */ + const int32_t fileIndex = m_mapFileComboBox->currentIndex(); + void* pointer = m_mapFileComboBox->itemData(fileIndex).value(); + CaretMappableDataFile* file = (CaretMappableDataFile*)pointer; + + m_chartOverlay->setSelectionData(file, indx); + + const MapYokingGroupEnum::Enum mapYoking = m_chartOverlay->getMapYokingGroup(); + if (mapYoking != MapYokingGroupEnum::MAP_YOKING_GROUP_OFF) { + EventMapYokingSelectMap selectMapEvent(mapYoking, + file, + indx, + m_chartOverlay->isEnabled()); + EventManager::get()->sendEvent(selectMapEvent.getPointer()); + } + + /* + * Need to update map index spin box. + * Spin box may range [0, N-1] or [1, N] but in source code + * indices are always [0, N-1] + */ + const int spinBoxIndex = indx + m_mapRowOrColumnIndexSpinBox->minimum(); + m_mapRowOrColumnIndexSpinBox->blockSignals(true); + m_mapRowOrColumnIndexSpinBox->setValue(spinBoxIndex); + m_mapRowOrColumnIndexSpinBox->blockSignals(false); + + this->updateUserInterfaceAndGraphicsWindow(); + + updateOverlaySettingsEditor(); + + /* + * User interface update may cause loss of focus so restore it + */ + m_mapRowOrColumnNameComboBox->setFocus(); +} + +/** + * Called when enabled checkbox state is changed + * @parm checked + * Checked status + */ +void +ChartTwoOverlayViewController::enabledCheckBoxClicked(bool checked) +{ + if (m_chartOverlay == NULL) { + return; + } + m_chartOverlay->setEnabled(checked); + + const MapYokingGroupEnum::Enum mapYoking = m_chartOverlay->getMapYokingGroup(); + if (mapYoking != MapYokingGroupEnum::MAP_YOKING_GROUP_OFF) { + CaretMappableDataFile* mapFile = NULL; + ChartTwoOverlay::SelectedIndexType selectedIndexType = ChartTwoOverlay::SelectedIndexType::INVALID; + int32_t selectedIndex = -1; + m_chartOverlay->getSelectionData(mapFile, + selectedIndexType, + selectedIndex); + + if (selectedIndexType == ChartTwoOverlay::SelectedIndexType::MAP) { + EventMapYokingSelectMap selectMapEvent(mapYoking, + mapFile, + selectedIndex, + m_chartOverlay->isEnabled()); + EventManager::get()->sendEvent(selectMapEvent.getPointer()); + } + } + + this->updateUserInterfaceAndGraphicsWindow(); +} + +/** + * Called when line-series loading enabled checkbox status is changed + * @parm checked + * Checked status + */ +void +ChartTwoOverlayViewController::lineSeriesLoadingEnabledCheckBoxClicked(bool checked) +{ + if (m_chartOverlay == NULL) { + return; + } + m_chartOverlay->setLineSeriesLoadingEnabled(checked); + + this->updateUserInterfaceAndGraphicsWindow(); +} + +/** + * Called when colorbar toolbutton is toggled. + * @param status + * New status. + */ +void +ChartTwoOverlayViewController::colorBarActionTriggered(bool status) +{ + if (m_chartOverlay == NULL) { + return; + } + + m_chartOverlay->getColorBar()->setDisplayed(status); + + this->updateGraphicsWindow(); +} + +/** + * Called when colorbar toolbutton is toggled. + * @param status + * New status. + */ +void +ChartTwoOverlayViewController::allMapsCheckBoxClicked(bool status) +{ + if (m_chartOverlay == NULL) { + return; + } + + CaretMappableDataFile* mapFile = NULL; + ChartTwoOverlay::SelectedIndexType selectedIndexType = ChartTwoOverlay::SelectedIndexType::INVALID; + int32_t selectedIndex = -1; + m_chartOverlay->getSelectionData(mapFile, + selectedIndexType, + selectedIndex); + if (mapFile != NULL) { + mapFile->invalidateHistogramChartColoring(); + } + + m_chartOverlay->setAllMapsSelected(status); + + this->updateGraphicsWindow(); +} + +/** + * Validate yoking when there are changes made to the overlay. + */ +void +ChartTwoOverlayViewController::validateYokingSelection() +{ + m_mapRowOrColumnYokingGroupComboBox->validateYokingChange(m_chartOverlay); + updateViewController(m_chartOverlay); + updateUserInterfaceAndGraphicsWindow(); +} + +/** + * Called when the yoking group is changed. + */ +void +ChartTwoOverlayViewController::yokingGroupActivated() +{ + MapYokingGroupEnum::Enum yokingGroup = m_mapRowOrColumnYokingGroupComboBox->getMapYokingGroup(); + + /* + * Has yoking group changed? + * TSC: overlay can be null when opened without loaded files + */ + if (m_chartOverlay != NULL && yokingGroup != m_chartOverlay->getMapYokingGroup()) { + validateYokingSelection(); + } +} + + +/** + * Called when the settings action is selected. + */ +void +ChartTwoOverlayViewController::settingsActionTriggered() +{ + if (m_chartOverlay == NULL) { + return; + } + + CaretMappableDataFile* mapFile = NULL; + ChartTwoOverlay::SelectedIndexType selectedIndexType = ChartTwoOverlay::SelectedIndexType::INVALID; + int32_t selectedIndex = -1; + m_chartOverlay->getSelectionData(mapFile, + selectedIndexType, + selectedIndex); + if (mapFile != NULL) { + EventOverlaySettingsEditorDialogRequest pcme(EventOverlaySettingsEditorDialogRequest::MODE_SHOW_EDITOR, + m_browserWindowIndex, + m_chartOverlay, + mapFile, + selectedIndexType, + selectedIndex); + EventManager::get()->sendEvent(pcme.getPointer()); + } +} + +/** + * Update this view controller using the given overlay. + * @param overlay + * Overlay that is used in this view controller. + */ +void +ChartTwoOverlayViewController::updateViewController(ChartTwoOverlay* chartOverlay) +{ + m_chartOverlay = chartOverlay; + + + /* + * Get the selection information for the overlay. + */ + std::vector dataFiles; + CaretMappableDataFile* selectedFile = NULL; + std::vector selectedFileMapNames; + ChartTwoOverlay::SelectedIndexType selectedIndexType = ChartTwoOverlay::SelectedIndexType::INVALID; + int32_t selectedIndex = -1; + if (m_chartOverlay != NULL) { + m_chartOverlay->getSelectionData(dataFiles, + selectedFile, + selectedFileMapNames, + selectedIndexType, + selectedIndex); + } + + /* + * Setup names of file for display in combo box + */ + std::vector displayNames; + FilePathNamePrefixCompactor::removeMatchingPathPrefixFromCaretDataFiles(dataFiles, + displayNames); + CaretAssert(dataFiles.size() == displayNames.size()); + + /* + * Update tooltips with full path to file and name of map + * as names may be too long to fit into combo boxes + */ + AString fileComboBoxToolTip("Select file for this overlay"); + AString nameComboBoxToolTip("Select map by its name"); + if (selectedFile != NULL) { + FileInformation fileInfo(selectedFile->getFileName()); + fileComboBoxToolTip.append(":\n" + + fileInfo.getFileName() + + "\n" + + fileInfo.getPathName() + + "\n\n" + + "Copy File Name/Path to Clipboard with Construction Menu"); + + nameComboBoxToolTip.append(":\n" + + m_mapRowOrColumnNameComboBox->currentText()); + } + m_mapFileComboBox->setToolTip(fileComboBoxToolTip); + m_mapRowOrColumnNameComboBox->setToolTip(nameComboBoxToolTip); + + /* + * Load the file selection combo box. + */ + m_mapFileComboBox->clear(); + int32_t selectedFileIndex = -1; + const int32_t numFiles = static_cast(dataFiles.size()); + for (int32_t i = 0; i < numFiles; i++) { + CaretMappableDataFile* dataFile = dataFiles[i]; + + AString dataTypeName = DataFileTypeEnum::toOverlayTypeName(dataFile->getDataFileType()); + CaretAssertVectorIndex(displayNames, i); + m_mapFileComboBox->addItem(displayNames[i], + qVariantFromValue((void*)dataFile)); + if (dataFile == selectedFile) { + selectedFileIndex = i; + } + } + if (selectedFileIndex >= 0) { + m_mapFileComboBox->setCurrentIndex(selectedFileIndex); + } + + /* + * Load the map name selection combo box and map index spin box + */ + m_mapRowOrColumnNameComboBox->setEnabled(false); + m_mapRowOrColumnNameComboBox->blockSignals(true); + m_mapRowOrColumnNameComboBox->clear(); + m_mapRowOrColumnIndexSpinBox->setEnabled(false); + m_mapRowOrColumnIndexSpinBox->blockSignals(true); + m_mapRowOrColumnIndexSpinBox->setRange(1, 1); + m_mapRowOrColumnIndexSpinBox->setValue(1); + const int32_t numberOfMaps = static_cast(selectedFileMapNames.size()); + if (numberOfMaps > 0) { + m_mapRowOrColumnNameComboBox->setEnabled(true); + m_mapRowOrColumnIndexSpinBox->setEnabled(true); + + for (int32_t i = 0; i < numberOfMaps; i++) { + CaretAssertVectorIndex(selectedFileMapNames, i); + m_mapRowOrColumnNameComboBox->addItem(selectedFileMapNames[i]); + } + m_mapRowOrColumnNameComboBox->setCurrentIndex(selectedIndex); + + /* + * Spin box ranges [0, N-1] or [1, N] depending upon data + */ + int32_t mapIndexMinimumValue = 1; + int32_t mapIndexMaximumValue = numberOfMaps; + switch (m_chartOverlay->getChartTwoDataType()) { + case ChartTwoDataTypeEnum::CHART_DATA_TYPE_INVALID: + break; + case ChartTwoDataTypeEnum::CHART_DATA_TYPE_HISTOGRAM: + break; + case ChartTwoDataTypeEnum::CHART_DATA_TYPE_LINE_SERIES: + break; + case ChartTwoDataTypeEnum::CHART_DATA_TYPE_MATRIX: + break; + } + + m_mapRowOrColumnIndexSpinBox->setRange(mapIndexMinimumValue, mapIndexMaximumValue); + const int spinBoxIndex = selectedIndex + m_mapRowOrColumnIndexSpinBox->minimum(); + m_mapRowOrColumnIndexSpinBox->setValue(spinBoxIndex); + } + m_mapRowOrColumnNameComboBox->blockSignals(false); + m_mapRowOrColumnIndexSpinBox->blockSignals(false); + + const bool validOverlayAndFileFlag = ((m_chartOverlay != NULL) + && (selectedFile != NULL)); + + /* + * Update enabled checkbox + */ + m_enabledCheckBox->setEnabled(false); + m_enabledCheckBox->setChecked(false); + if (validOverlayAndFileFlag) { + m_enabledCheckBox->setEnabled(true); + m_enabledCheckBox->setChecked(m_chartOverlay->isEnabled()); + } + + /* + * Update lines series loading checkbox + */ + m_lineSeriesLoadingEnabledCheckBox->setEnabled(false); + m_lineSeriesLoadingEnabledCheckBox->setChecked(false); + if ((validOverlayAndFileFlag) + && (m_chartOverlay->getChartTwoDataType() == ChartTwoDataTypeEnum::CHART_DATA_TYPE_LINE_SERIES)) { + m_lineSeriesLoadingEnabledCheckBox->setEnabled(true); + m_lineSeriesLoadingEnabledCheckBox->setChecked(m_chartOverlay->isLineSeriesLoadingEnabled()); + } + + /* + * Update yoking + */ + m_mapRowOrColumnYokingGroupComboBox->getWidget()->setEnabled(false); + m_mapRowOrColumnYokingGroupComboBox->setMapYokingGroup(MapYokingGroupEnum::MAP_YOKING_GROUP_OFF); + if (validOverlayAndFileFlag) { + if (m_chartOverlay->isMapYokingSupported()) { + m_mapRowOrColumnYokingGroupComboBox->getWidget()->setEnabled(true); + m_mapRowOrColumnYokingGroupComboBox->setMapYokingGroup(m_chartOverlay->getMapYokingGroup()); + } + } + + /* + * Update all maps + */ + m_allMapsCheckBox->setEnabled(false); + m_allMapsCheckBox->setChecked(false); + if (validOverlayAndFileFlag) { + m_allMapsCheckBox->setEnabled(m_chartOverlay->isAllMapsSupported()); + if (m_chartOverlay->isAllMapsSupported()) { + m_allMapsCheckBox->setChecked(m_chartOverlay->isAllMapsSelected()); + } + } + + /* + * Update settings (wrench) button + */ + bool enableSettingsActionFlag = validOverlayAndFileFlag; + switch (m_chartOverlay->getChartTwoDataType()) { + case ChartTwoDataTypeEnum::CHART_DATA_TYPE_INVALID: + enableSettingsActionFlag = false; + break; + case ChartTwoDataTypeEnum::CHART_DATA_TYPE_HISTOGRAM: + break; + case ChartTwoDataTypeEnum::CHART_DATA_TYPE_LINE_SERIES: + break; + case ChartTwoDataTypeEnum::CHART_DATA_TYPE_MATRIX: + break; + } + + m_settingsAction->setEnabled(enableSettingsActionFlag); + + /* + * Update color bar button + */ + m_colorBarAction->blockSignals(true); + m_colorBarAction->setEnabled(false); + m_colorBarAction->setChecked(false); + if (validOverlayAndFileFlag) { + if (selectedFile->isMappedWithPalette()) { + m_colorBarAction->setEnabled(true); + m_colorBarAction->setChecked(m_chartOverlay->getColorBar()->isDisplayed()); + } + } + m_colorBarAction->blockSignals(false); + + /* + * Update construction button + */ + m_constructionAction->setEnabled(true); + + /* + * Update matrix triangular view mode + */ + m_matrixTriangularViewModeAction->setEnabled(false); + if (validOverlayAndFileFlag) { + const ChartTwoMatrixTriangularViewingModeEnum::Enum viewMode = m_chartOverlay->getMatrixTriangularViewingMode(); + + for (auto& mvmd : m_matrixViewMenuData) { + if (std::get<0>(mvmd) == viewMode) { + std::get<1>(mvmd)->setChecked(true); + updateMatrixTriangularViewModeAction(viewMode); + break; + } + } + + if (m_chartOverlay->isMatrixTriangularViewingModeSupported()) { + m_matrixTriangularViewModeAction->setEnabled(true); + } + } + + /* + * Update vertical axis location + */ + m_axisLocationAction->setEnabled(false); + if (validOverlayAndFileFlag) { + if (m_chartOverlay->isCartesianVerticalAxisLocationSupported()) { + m_axisLocationAction->setEnabled(true); + const ChartAxisLocationEnum::Enum axisLocation = m_chartOverlay->getCartesianVerticalAxisLocation(); + for (auto& almd : m_axisLocationMenuData) { + if (std::get<0>(almd) == axisLocation) { + updateAxisLocationAction(axisLocation); + break; + } + } + } + } +} + +/** + * Update the matrix triangular view mode button. + * + * @param matrixViewMode + * Matrix triangular view mode. + */ +void +ChartTwoOverlayViewController::updateMatrixTriangularViewModeAction(const ChartTwoMatrixTriangularViewingModeEnum::Enum matrixViewMode) +{ + CaretAssert(m_matrixTriangularViewModeAction); + m_matrixTriangularViewModeAction->blockSignals(true); + for (auto& mvmd : m_matrixViewMenuData) { + if (std::get<0>(mvmd) == matrixViewMode) { + QPixmap pixmap = std::get<2>(mvmd); + if ( ! pixmap.isNull()) { + m_matrixTriangularViewModeAction->setIcon(pixmap); + m_matrixTriangularViewModeAction->setText(""); + } + else { + m_matrixTriangularViewModeAction->setText("M"); + } + break; + } + } + m_matrixTriangularViewModeAction->blockSignals(false); +} + +/** + * Update the axis location button. + * + * @param axisLocation + * Axis location. + */ +void +ChartTwoOverlayViewController::updateAxisLocationAction(const ChartAxisLocationEnum::Enum axisLocation) +{ + CaretAssert(m_axisLocationAction); + m_axisLocationAction->blockSignals(true); + for (auto& almd : m_axisLocationMenuData) { + if (std::get<0>(almd) == axisLocation) { + QPixmap pixmap = std::get<2>(almd); + if ( ! pixmap.isNull()) { + m_axisLocationAction->setIcon(pixmap); + m_axisLocationAction->setText(""); + } + else { + m_axisLocationAction->setText("A"); + } + break; + } + } + m_axisLocationAction->blockSignals(false); +} + + +/** + * Update graphics and GUI after selections made + */ +void +ChartTwoOverlayViewController::updateUserInterfaceAndGraphicsWindow() +{ + updateUserInterface(); + updateGraphicsWindow(); +} + +/** + * Update graphics and GUI after selections made + */ +void +ChartTwoOverlayViewController::updateUserInterface() +{ + EventManager::get()->sendEvent(EventUserInterfaceUpdate().getPointer()); +} + +/** + * Update graphics after selections made + */ +void +ChartTwoOverlayViewController::updateGraphicsWindow() +{ + if (m_chartOverlay->getMapYokingGroup() != MapYokingGroupEnum::MAP_YOKING_GROUP_OFF) { + EventManager::get()->sendEvent(EventSurfaceColoringInvalidate().getPointer()); + EventManager::get()->sendEvent(EventGraphicsUpdateAllWindows().getPointer()); + } + else { + EventManager::get()->sendEvent(EventGraphicsUpdateOneWindow(m_browserWindowIndex).getPointer()); + } +} + +/** + * Create the matrix triangular view mode menu. + * @param parent + * Parent widget. + */ +QMenu* +ChartTwoOverlayViewController::createMatrixTriangularViewModeMenu(QWidget* parent) +{ + std::vector allViewModes; + ChartTwoMatrixTriangularViewingModeEnum::getAllEnums(allViewModes); + + QMenu* menu = new QMenu(parent); + QObject::connect(menu, &QMenu::triggered, + this, &ChartTwoOverlayViewController::menuMatrixTriangularViewModeTriggered); + + QActionGroup* actionGroup = new QActionGroup(this); + actionGroup->setExclusive(true); + + for (auto viewMode: allViewModes) { + QAction* action = menu->addAction(ChartTwoMatrixTriangularViewingModeEnum::toGuiName(viewMode)); + action->setCheckable(true); + action->setData((int)ChartTwoMatrixTriangularViewingModeEnum::toIntegerCode(viewMode)); + QPixmap pixmap = createMatrixTriangularViewModePixmap(menu, viewMode); + action->setIcon(pixmap); + actionGroup->addAction(action); + + m_matrixViewMenuData.push_back(std::make_tuple(viewMode, action, pixmap)); + } + + return menu; +} + +/** + * Called when an item is selected on matrix triangular view mode menu. + * + * @action + * Action of menu item selected. + */ +void +ChartTwoOverlayViewController::menuMatrixTriangularViewModeTriggered(QAction* action) +{ + const QVariant itemData = action->data(); + CaretAssert(itemData.isValid()); + bool valid = false; + ChartTwoMatrixTriangularViewingModeEnum::Enum viewMode = ChartTwoMatrixTriangularViewingModeEnum::fromIntegerCode(itemData.toInt(), &valid); + + if (valid) { + m_chartOverlay->setMatrixTriangularViewingMode(viewMode); + updateMatrixTriangularViewModeAction(viewMode); + CaretMappableDataFile* mapFile = NULL; + ChartTwoOverlay::SelectedIndexType selectedIndexType = ChartTwoOverlay::SelectedIndexType::INVALID; + int32_t selectedIndex = -1; + m_chartOverlay->getSelectionData(mapFile, + selectedIndexType, + selectedIndex); + if (mapFile != NULL) { + mapFile->updateScalarColoringForAllMaps(); + } + this->updateGraphicsWindow(); + } +} + +/** + * Create the axis location menu. + * @param parent + * Parent widget. + */ +QMenu* +ChartTwoOverlayViewController::createAxisLocationMenu(QWidget* widget) +{ + std::vector axisLocations; + axisLocations.push_back(ChartAxisLocationEnum::CHART_AXIS_LOCATION_LEFT); + axisLocations.push_back(ChartAxisLocationEnum::CHART_AXIS_LOCATION_RIGHT); + + QMenu* menu = new QMenu(widget); + QObject::connect(menu, &QMenu::triggered, + this, &ChartTwoOverlayViewController::menuAxisLocationTriggered); + + QActionGroup* actionGroup = new QActionGroup(this); + actionGroup->setExclusive(true); + + for (auto axis: axisLocations) { + QAction* action = menu->addAction(ChartAxisLocationEnum::toGuiName(axis)); + action->setCheckable(true); + action->setData((int)ChartAxisLocationEnum::toIntegerCode(axis)); + QPixmap pixmap = createAxisLocationPixmap(menu, axis); + action->setIcon(pixmap); + actionGroup->addAction(action); + + m_axisLocationMenuData.push_back(std::make_tuple(axis, action, pixmap)); + } + + return menu; +} + +/** + * Called when an item is selected on axis location menu. + * + * @action + * Action of menu item selected. + */ +void +ChartTwoOverlayViewController::menuAxisLocationTriggered(QAction* action) +{ + const QVariant itemData = action->data(); + CaretAssert(itemData.isValid()); + bool valid = false; + ChartAxisLocationEnum::Enum axisLocation = ChartAxisLocationEnum::fromIntegerCode(itemData.toInt(), &valid); + + if (valid) { + m_chartOverlay->setCartesianVerticalAxisLocation(axisLocation); + updateAxisLocationAction(axisLocation); + this->updateGraphicsWindow(); + } +} + +/** + * Create the construction menu. + * @param parent + * Parent widget. + */ +QMenu* +ChartTwoOverlayViewController::createConstructionMenu(QWidget* parent) +{ + QMenu* menu = new QMenu(parent); + QObject::connect(menu, SIGNAL(aboutToShow()), + this, SLOT(menuConstructionAboutToShow())); + + menu->addAction("Add Overlay Above", + this, + SLOT(menuAddOverlayAboveTriggered())); + + menu->addAction("Add Overlay Below", + this, + SLOT(menuAddOverlayBelowTriggered())); + + menu->addSeparator(); + + menu->addAction("Move Overlay Up", + this, + SLOT(menuMoveOverlayUpTriggered())); + + menu->addAction("Move Overlay Down", + this, + SLOT(menuMoveOverlayDownTriggered())); + + menu->addSeparator(); + + menu->addAction("Remove This Overlay", + this, + SLOT(menuRemoveOverlayTriggered())); + + menu->addSeparator(); + + m_constructionReloadFileAction = menu->addAction("Reload Selected File", + this, + SLOT(menuReloadFileTriggered())); + + menu->addSeparator(); + + menu->addAction("Copy Path and File Name to Clipboard", + this, + SLOT(menuCopyFileNameToClipBoard())); + + menu->addAction("Copy Map Name to Clipboard", + this, + SLOT(menuCopyMapNameToClipBoard())); + + return menu; + +} + +/** + * Called when construction menu is about to be displayed. + */ +void +ChartTwoOverlayViewController::menuConstructionAboutToShow() +{ + if (m_chartOverlay != NULL) { + CaretMappableDataFile* mapFile = NULL; + ChartTwoOverlay::SelectedIndexType selectedIndexType = ChartTwoOverlay::SelectedIndexType::INVALID; + int32_t selectedIndex = -1; + m_chartOverlay->getSelectionData(mapFile, + selectedIndexType, + selectedIndex); + + QString menuText = "Reload Selected File"; + if (mapFile != NULL) { + if (mapFile->isModified()) { + QString suffix = " (MODIFIED)"; + if (mapFile->isModifiedPaletteColorMapping()) { + if ( ! mapFile->isModifiedExcludingPaletteColorMapping()) { + suffix = " (MODIFIED PALETTE)"; + } + } + menuText += suffix; + } + } + m_constructionReloadFileAction->setText(menuText); + } +} + +/** + * Add an overlay above this overlay. + */ +void +ChartTwoOverlayViewController::menuAddOverlayAboveTriggered() +{ + emit requestAddOverlayAbove(m_chartOverlayIndex); +} + +/** + * Add an overlay below this overlay. + */ +void +ChartTwoOverlayViewController::menuAddOverlayBelowTriggered() +{ + emit requestAddOverlayBelow(m_chartOverlayIndex); +} + +/** + * Remove this overlay. + */ +void +ChartTwoOverlayViewController::menuRemoveOverlayTriggered() +{ + emit requestRemoveOverlay(m_chartOverlayIndex); +} + +/** + * Move this overlay down. + */ +void +ChartTwoOverlayViewController::menuMoveOverlayDownTriggered() +{ + emit requestMoveOverlayDown(m_chartOverlayIndex); +} + +/** + * Move this overlay down. + */ +void +ChartTwoOverlayViewController::menuMoveOverlayUpTriggered() +{ + emit requestMoveOverlayUp(m_chartOverlayIndex); +} + +/** + * Copy the file name to the clip board. + */ +void +ChartTwoOverlayViewController::menuCopyFileNameToClipBoard() +{ + if (m_chartOverlay != NULL) { + CaretMappableDataFile* mapFile = NULL; + ChartTwoOverlay::SelectedIndexType selectedIndexType = ChartTwoOverlay::SelectedIndexType::INVALID; + int32_t selectedIndex = -1; + m_chartOverlay->getSelectionData(mapFile, + selectedIndexType, + selectedIndex); + + if (mapFile != NULL) { + QApplication::clipboard()->setText(mapFile->getFileName().trimmed(), + QClipboard::Clipboard); + } + } +} + +/** + * Copy the map name to the clip board. + */ +void +ChartTwoOverlayViewController::menuCopyMapNameToClipBoard() +{ + if (m_chartOverlay != NULL) { + std::vector allMapFiles; + std::vector indexNames; + CaretMappableDataFile* mapFile = NULL; + ChartTwoOverlay::SelectedIndexType selectedIndexType = ChartTwoOverlay::SelectedIndexType::INVALID; + int32_t selectedIndex = -1; + m_chartOverlay->getSelectionData(allMapFiles, + mapFile, + indexNames, + selectedIndexType, + selectedIndex); + + if (mapFile != NULL) { + if ((selectedIndex >= 0) + && (selectedIndex < static_cast(indexNames.size()))) { + QApplication::clipboard()->setText(indexNames[selectedIndex], + QClipboard::Clipboard); + } + } + } +} + +/** + * Reload the file in the overlay. + */ +void +ChartTwoOverlayViewController::menuReloadFileTriggered() +{ + if (m_chartOverlay != NULL) { + CaretMappableDataFile* mapFile = NULL; + ChartTwoOverlay::SelectedIndexType selectedIndexType = ChartTwoOverlay::SelectedIndexType::INVALID; + int32_t selectedIndex = -1; + m_chartOverlay->getSelectionData(mapFile, + selectedIndexType, + selectedIndex); + + if (mapFile != NULL) { + AString username; + AString password; + + if (DataFile::isFileOnNetwork(mapFile->getFileName())) { + const QString msg("This file is on the network. " + "If accessing the file requires a username and " + "password, enter it here. Otherwise, remove any " + "text from the username and password fields."); + + + if (UsernamePasswordWidget::getUserNameAndPasswordInDialog(m_constructionToolButton, + "Username and Password", + msg, + username, + password)) { + /* nothing */ + } + else { + return; + } + } + + EventDataFileReload reloadEvent(GuiManager::get()->getBrain(), + mapFile); + reloadEvent.setUsernameAndPassword(username, + password); + EventManager::get()->sendEvent(reloadEvent.getPointer()); + + if (reloadEvent.isError()) { + WuQMessageBox::errorOk(m_constructionToolButton, + reloadEvent.getErrorMessage()); + } + + updateOverlaySettingsEditor(); + + updateUserInterfaceAndGraphicsWindow(); + } + } +} + +/** + * Create a axis location mode pixmap. + * + * @param widget + * To color the pixmap with backround and foreground, + * the palette from the given widget is used. + * @param axisLocation + * Axis location represented by the icon. + * @return + * Pixmap for matrix view mode. + */ +QPixmap +ChartTwoOverlayViewController::createAxisLocationPixmap(QWidget* widget, + const ChartAxisLocationEnum::Enum axisLocation) +{ + CaretAssert(widget); + + /* + * Create a small, square pixmap that will contain + * the foreground color around the pixmap's perimeter. + */ + const qreal iconSize = 24.0; + const qreal minValue = 2.0; + const qreal maxValue = iconSize - minValue; + + QPixmap pixmap(static_cast(iconSize), + static_cast(iconSize)); + QSharedPointer painter = WuQtUtilities::createPixmapWidgetPainterOriginBottomLeft(widget, + pixmap); + + QPen pen = painter->pen(); + pen.setWidthF(2.0); + painter->setPen(pen); + + const int offsetFromEdge = 3; + switch (axisLocation) { + case ChartAxisLocationEnum::CHART_AXIS_LOCATION_BOTTOM: + painter->drawLine(QPointF(minValue, minValue + offsetFromEdge), + QPointF(maxValue, minValue + offsetFromEdge)); + break; + case ChartAxisLocationEnum::CHART_AXIS_LOCATION_LEFT: + painter->drawLine(QPointF(minValue + offsetFromEdge, minValue), + QPointF(minValue + offsetFromEdge, maxValue)); + break; + case ChartAxisLocationEnum::CHART_AXIS_LOCATION_RIGHT: + painter->drawLine(QPointF(maxValue - offsetFromEdge, minValue), + QPointF(maxValue - offsetFromEdge, maxValue)); + break; + case ChartAxisLocationEnum::CHART_AXIS_LOCATION_TOP: + painter->drawLine(QPointF(minValue, maxValue - offsetFromEdge), + QPointF(maxValue, maxValue - offsetFromEdge)); + break; + } + + return pixmap; +} + +/** + * Create a matrix view mode pixmap. + * + * @param widget + * To color the pixmap with backround and foreground, + * the palette from the given widget is used. + * @param matrixViewMode + * Matrix view mode represented by the icon. + * @return + * Pixmap for matrix view mode. + */ +QPixmap +ChartTwoOverlayViewController::createMatrixTriangularViewModePixmap(QWidget* widget, + const ChartTwoMatrixTriangularViewingModeEnum::Enum matrixViewMode) +{ + CaretAssert(widget); + + /* + * Create a small, square pixmap that will contain + * the foreground color around the pixmap's perimeter. + */ + const qreal iconSize = 24.0; + const qreal minValue = 2.0; + const qreal maxValue = iconSize - minValue; + const QPointF bottomLeft(minValue, minValue); + const QPointF bottomRight(maxValue, minValue); + const QPointF topRight(maxValue, maxValue); + const QPointF topLeft(minValue, maxValue); + + QPixmap pixmap(static_cast(iconSize), + static_cast(iconSize)); + QSharedPointer painter = WuQtUtilities::createPixmapWidgetPainterOriginBottomLeft(widget, + pixmap); + + QPen pen = painter->pen(); + pen.setWidthF(2.0); + painter->setPen(pen); + + QPolygonF polygon; + switch (matrixViewMode) { + case ChartTwoMatrixTriangularViewingModeEnum::MATRIX_VIEW_FULL: + { + polygon.push_back(bottomLeft); + polygon.push_back(bottomRight); + polygon.push_back(topRight); + polygon.push_back(topLeft); + } + break; + case ChartTwoMatrixTriangularViewingModeEnum::MATRIX_VIEW_FULL_NO_DIAGONAL: + { + polygon.push_back(bottomLeft); + polygon.push_back(bottomRight); + polygon.push_back(topRight); + polygon.push_back(topLeft); + + painter->drawLine(topLeft, bottomRight); + } + break; + case ChartTwoMatrixTriangularViewingModeEnum::MATRIX_VIEW_LOWER_NO_DIAGONAL: + { + polygon.push_back(bottomLeft); + polygon.push_back(bottomRight); + polygon.push_back(topLeft); + } + break; + case ChartTwoMatrixTriangularViewingModeEnum::MATRIX_VIEW_UPPER_NO_DIAGONAL: + { + polygon.push_back(bottomRight); + polygon.push_back(topRight); + polygon.push_back(topLeft); + } + break; + } + + + painter->drawPolygon(polygon); + + return pixmap; +} + diff --git a/src/GuiQt/ChartTwoOverlayViewController.h b/src/GuiQt/ChartTwoOverlayViewController.h new file mode 100644 index 0000000000000000000000000000000000000000..0be90da0681e20c13824a653fd5a7ce5ab41c5f9 --- /dev/null +++ b/src/GuiQt/ChartTwoOverlayViewController.h @@ -0,0 +1,198 @@ +#ifndef __CHART_TWO_OVERLAY_VIEW_CONTROLLER_H__ +#define __CHART_TWO_OVERLAY_VIEW_CONTROLLER_H__ + +/*LICENSE_START*/ +/* + * Copyright (C) 2016 Washington University School of Medicine + * + * 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 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ +/*LICENSE_END*/ + + +#include +#include + +#include "ChartAxisLocationEnum.h" +#include "ChartTwoMatrixTriangularViewingModeEnum.h" + +class QAction; +class QCheckBox; +class QComboBox; +class QGridLayout; +class QMenu; +class QSpinBox; +class QToolButton; + +namespace caret { + class ChartTwoOverlay; + class MapYokingGroupComboBox; + class WuQGridLayoutGroup; + + class ChartTwoOverlayViewController : public QObject { + + Q_OBJECT + + public: + ChartTwoOverlayViewController(const Qt::Orientation orientation, + const int32_t browserWindowIndex, + const int32_t chartOverlayIndex, + QObject* parent); + + virtual ~ChartTwoOverlayViewController(); + + void updateViewController(ChartTwoOverlay* overlay); + + // ADD_NEW_METHODS_HERE + signals: + void requestAddOverlayAbove(const int32_t overlayIndex); + + void requestAddOverlayBelow(const int32_t overlayIndex); + + void requestRemoveOverlay(const int32_t overlayIndex); + + void requestMoveOverlayUp(const int32_t overlayIndex); + + void requestMoveOverlayDown(const int32_t overlayIndex); + + private slots: + void fileComboBoxSelected(int); + + void mapRowOrColumnNameComboBoxSelected(int); + + void mapRowOrColumnIndexSpinBoxValueChanged(int); + + void enabledCheckBoxClicked(bool); + + void lineSeriesLoadingEnabledCheckBoxClicked(bool); + + void colorBarActionTriggered(bool); + + void settingsActionTriggered(); + + void yokingGroupActivated(); + + void allMapsCheckBoxClicked(bool); + + void menuAddOverlayAboveTriggered(); + + void menuAddOverlayBelowTriggered(); + + void menuRemoveOverlayTriggered(); + + void menuMoveOverlayDownTriggered(); + + void menuMoveOverlayUpTriggered(); + + void menuReloadFileTriggered(); + + void menuCopyFileNameToClipBoard(); + + void menuCopyMapNameToClipBoard(); + + void menuConstructionAboutToShow(); + + void menuMatrixTriangularViewModeTriggered(QAction* action); + + void menuAxisLocationTriggered(QAction* action); + + private: + ChartTwoOverlayViewController(const ChartTwoOverlayViewController&); + + ChartTwoOverlayViewController& operator=(const ChartTwoOverlayViewController&); + + void updateUserInterfaceAndGraphicsWindow(); + + void updateUserInterface(); + + void updateGraphicsWindow(); + + QMenu* createConstructionMenu(QWidget* parent); + + QMenu* createMatrixTriangularViewModeMenu(QWidget* widget); + + QMenu* createAxisLocationMenu(QWidget* widget); + + void validateYokingSelection(); + + void updateOverlaySettingsEditor(); + + QPixmap createMatrixTriangularViewModePixmap(QWidget* widget, + const ChartTwoMatrixTriangularViewingModeEnum::Enum matrixViewMode); + + QPixmap createAxisLocationPixmap(QWidget* widget, + const ChartAxisLocationEnum::Enum axisLocation); + + void updateMatrixTriangularViewModeAction(const ChartTwoMatrixTriangularViewingModeEnum::Enum matrixViewMode); + + void updateAxisLocationAction(const ChartAxisLocationEnum::Enum axisLocation); + + const int32_t m_browserWindowIndex; + + const int32_t m_chartOverlayIndex; + + ChartTwoOverlay* m_chartOverlay; + + QCheckBox* m_enabledCheckBox; + + QCheckBox* m_lineSeriesLoadingEnabledCheckBox; + + QToolButton* m_settingsToolButton; + + QAction* m_settingsAction; + + QToolButton* m_colorBarToolButton; + + QAction* m_colorBarAction; + + QAction* m_constructionAction; + + QToolButton* m_matrixTriangularViewModeToolButton; + + QAction* m_matrixTriangularViewModeAction; + + QAction* m_axisLocationAction; + + QToolButton* m_axisLocationToolButton; + + std::vector> m_matrixViewMenuData; + + std::vector> m_axisLocationMenuData; + + QComboBox* m_mapFileComboBox; + + MapYokingGroupComboBox* m_mapRowOrColumnYokingGroupComboBox; + + QCheckBox* m_allMapsCheckBox; + + QSpinBox* m_mapRowOrColumnIndexSpinBox; + + QComboBox* m_mapRowOrColumnNameComboBox; + + QToolButton* m_constructionToolButton; + + QAction* m_constructionReloadFileAction; + + // ADD_NEW_MEMBERS_HERE + + friend class ChartTwoOverlaySetViewController; + }; + +#ifdef __CHART_TWO_OVERLAY_VIEW_CONTROLLER_DECLARE__ + // +#endif // __CHART_TWO_OVERLAY_VIEW_CONTROLLER_DECLARE__ + +} // namespace +#endif //__CHART_TWO_OVERLAY_VIEW_CONTROLLER_H__ diff --git a/src/GuiQt/CiftiConnectivityMatrixViewController.cxx b/src/GuiQt/CiftiConnectivityMatrixViewController.cxx index 0d69389828a11ce3d454a82b16bad703ba0fc604..1780d2ad4ca5e1c8779b137b39898a5f21b66fd6 100644 --- a/src/GuiQt/CiftiConnectivityMatrixViewController.cxx +++ b/src/GuiQt/CiftiConnectivityMatrixViewController.cxx @@ -177,9 +177,15 @@ CiftiConnectivityMatrixViewController::updateViewController() "an identification operation"); m_fileEnableCheckBoxes.push_back(checkBox); + const AString dynToolTip("This option is enabled only for .dynconn.nii (dynamic connectivity) files. " + "When checked, this dynamic connectivity file will appear in the Overlay Layers' File selection combo box. " + "Dynamic connectivity files do not explicitly exist but allow dynamic " + "computation of connectivity from a dense data series (.dtseries) file. " + "Dynamic connectivity allows one to view connectivity for a brainordinate without creation of an " + "extremely large dense connectivity (.dconn.nii) file. " + "In Preferences, one may set the default to show/hide .dynconn.nii files."); layerCheckBox = new QCheckBox(""); - layerCheckBox->setToolTip("When selected, allow selection of this\n" - "file as an overlay in the Layers tab"); + WuQtUtilities::setWordWrappedToolTip(layerCheckBox, dynToolTip); m_layerCheckBoxes.push_back(layerCheckBox); lineEdit = new QLineEdit(); diff --git a/src/GuiQt/ClippingPlanesDialog.cxx b/src/GuiQt/ClippingPlanesDialog.cxx index ccbe347d838f1eb0318f2c1c423ff3844d39c689..c45140daa76eb6df6606d318f55d49e48c99dca2 100644 --- a/src/GuiQt/ClippingPlanesDialog.cxx +++ b/src/GuiQt/ClippingPlanesDialog.cxx @@ -367,21 +367,21 @@ void ClippingPlanesDialog::clippingValueChanged() { const float panning[3] = { - m_xPanDoubleSpinBox->value(), - m_yPanDoubleSpinBox->value(), - m_zPanDoubleSpinBox->value() + (float)m_xPanDoubleSpinBox->value(), + (float)m_yPanDoubleSpinBox->value(), + (float)m_zPanDoubleSpinBox->value() }; const float rotation[3] = { - m_xRotateDoubleSpinBox->value(), - m_yRotateDoubleSpinBox->value(), - m_zRotateDoubleSpinBox->value() + (float)m_xRotateDoubleSpinBox->value(), + (float)m_yRotateDoubleSpinBox->value(), + (float)m_zRotateDoubleSpinBox->value() }; const float thickness[3] ={ - m_xThicknessDoubleSpinBox->value(), - m_yThicknessDoubleSpinBox->value(), - m_zThicknessDoubleSpinBox->value() + (float)m_xThicknessDoubleSpinBox->value(), + (float)m_yThicknessDoubleSpinBox->value(), + (float)m_zThicknessDoubleSpinBox->value() }; /* diff --git a/src/GuiQt/CopyPaletteColorMappingToFilesDialog.cxx b/src/GuiQt/CopyPaletteColorMappingToFilesDialog.cxx new file mode 100644 index 0000000000000000000000000000000000000000..627b16b6f3e344336e3a79708adf0d80ee3f9bc9 --- /dev/null +++ b/src/GuiQt/CopyPaletteColorMappingToFilesDialog.cxx @@ -0,0 +1,305 @@ + +/*LICENSE_START*/ +/* + * Copyright (C) 2018 Washington University School of Medicine + * + * 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 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ +/*LICENSE_END*/ + +#define __COPY_PALETTE_COLOR_MAPPING_TO_FILES_DIALOG_DECLARE__ +#include "CopyPaletteColorMappingToFilesDialog.h" +#undef __COPY_PALETTE_COLOR_MAPPING_TO_FILES_DIALOG_DECLARE__ + +#include +#include + +#include +#include +#include +#include +#include + +#include "Brain.h" +#include "CaretAssert.h" +#include "CaretMappableDataFile.h" +#include "CursorDisplayScoped.h" +#include "EventCaretMappableDataFilesGet.h" +#include "EventManager.h" +#include "GuiManager.h" +#include "WuQMessageBox.h" + +using namespace caret; + + + +/** + * \class caret::CopyPaletteColorMappingToFilesDialog + * \brief Dialog for copying palette color mapping to other files + * \ingroup GuiQt + */ + +/** + * Constructor. + * + * @param allPaletteMapFiles + * All palette mappable data files. + * @param selectedMapFile + * Map file that contains palette being copied + * @param selectedPaletteColorMapping + * Palette color mapping that is being copied + * @param parent + * Parent widget for this dialog. + * + */ +CopyPaletteColorMappingToFilesDialog::CopyPaletteColorMappingToFilesDialog(const std::vector& allPaletteMapFiles, + CaretMappableDataFile* selectedMapFile, + const PaletteColorMapping* selectedPaletteColorMapping, + QWidget* parent) +: WuQDialogModal("Copy Palette Color Mapping", + parent), +m_selectedMapFile(selectedMapFile), +m_selectedPaletteColorMapping(selectedPaletteColorMapping) +{ + CaretAssert(m_selectedMapFile); + CaretAssert(m_selectedPaletteColorMapping); + + for (const auto mapFile : allPaletteMapFiles) { + CaretAssert(mapFile); + QCheckBox* checkBox = new QCheckBox(mapFile->getFileNameNoPath()); + if (mapFile == selectedMapFile) { + checkBox->setStyleSheet("font : bold"); + } + m_fileCheckBoxes.push_back(std::make_pair(checkBox, mapFile)); + + if (s_previousCheckedFiles.find(mapFile) != s_previousCheckedFiles.end()) { + checkBox->setChecked(true); + } + else { + checkBox->setChecked(false); + } + } + + QToolButton* allOnToolButton = new QToolButton(); + allOnToolButton->setText("All On"); + QObject::connect(allOnToolButton, &QToolButton::clicked, + this, &CopyPaletteColorMappingToFilesDialog::allCheckBoxesOnClicked); + + QToolButton* allOffToolButton = new QToolButton(); + allOffToolButton->setText("All Off"); + QObject::connect(allOffToolButton, &QToolButton::clicked, + this, &CopyPaletteColorMappingToFilesDialog::allCheckBoxesOffClicked); + + QHBoxLayout* toolButtonLayout = new QHBoxLayout(); + toolButtonLayout->addWidget(allOnToolButton); + toolButtonLayout->addWidget(allOffToolButton); + toolButtonLayout->addStretch(); + + QGridLayout* checkBoxLayout = new QGridLayout(); + checkBoxLayout->setColumnMinimumWidth(0, 10); + AString lastFileTypeName; + int32_t row = checkBoxLayout->rowCount(); + for (auto checkFile : m_fileCheckBoxes) { + const AString fileTypeName = DataFileTypeEnum::toShortGuiName(checkFile.second->getDataFileType()); + if (fileTypeName != lastFileTypeName) { + checkBoxLayout->addWidget(new QLabel(fileTypeName), row, 0, 1, 2); + ++row; + } + lastFileTypeName = fileTypeName; + + checkBoxLayout->addWidget(checkFile.first, row, 1); + ++row; + } + + QWidget* widget = new QWidget; + QVBoxLayout* layout = new QVBoxLayout(widget); + layout->addLayout(toolButtonLayout); + layout->addLayout(checkBoxLayout); + + setCentralWidget(widget, SCROLL_AREA_AS_NEEDED); +} + +/** + * Destructor. + */ +CopyPaletteColorMappingToFilesDialog::~CopyPaletteColorMappingToFilesDialog() +{ +} + +/** + * Called when the OK button is clicked. + */ +void +CopyPaletteColorMappingToFilesDialog::okButtonClicked() +{ + { + /* + * Note: Cursor MUST go out of scope (be removed) + * before calling parent's okButtonClicked() + */ + CursorDisplayScoped cursor; + cursor.showWaitCursor(); + + s_previousCheckedFiles.clear(); + + int32_t checkedCount = 0; + bool sourceFileFlag = false; + + for (auto checkFile : m_fileCheckBoxes) { + QCheckBox* checkBox = checkFile.first; + CaretAssert(checkBox); + CaretMappableDataFile* mapFile = checkFile.second; + CaretAssert(mapFile); + + if (checkBox->isChecked()) { + if (mapFile != m_selectedMapFile) { + mapFile->setPaletteNormalizationMode(m_selectedMapFile->getPaletteNormalizationMode()); + + const int32_t numMaps = mapFile->getNumberOfMaps(); + for (int32_t iMap = 0; iMap < numMaps; iMap++) { + PaletteColorMapping* pcm = mapFile->getMapPaletteColorMapping(iMap); + pcm->copy(*m_selectedPaletteColorMapping, + false); + mapFile->updateScalarColoringForAllMaps(); + ++checkedCount; + } + } + else { + sourceFileFlag = true; + } + + s_previousCheckedFiles.insert(mapFile); + } + } + + if (checkedCount <= 0) { + cursor.restoreCursor(); + if (sourceFileFlag) { + WuQMessageBox::errorOk(this, + "The selected file is the 'copy from' file. Select files for 'copying to'"); + } + else { + WuQMessageBox::errorOk(this, + "No files are selected for palette copying."); + } + return; + } + } + + WuQDialogModal::okButtonClicked(); +} + +/** + * Called when 'All On' checkboxes button is clicked. + */ +void +CopyPaletteColorMappingToFilesDialog::allCheckBoxesOnClicked() +{ + for (auto checkFile : m_fileCheckBoxes) { + checkFile.first->setChecked(true); + } +} + +/** + * Called when 'All Off' checkboxes button is clicked. + */ +void +CopyPaletteColorMappingToFilesDialog::allCheckBoxesOffClicked() +{ + for (auto checkFile : m_fileCheckBoxes) { + checkFile.first->setChecked(false); + } +} + + +/** + * Run the dialog. + * + * @param selectedMapFile + * Map file that contains palette being copied + * @param selectedPaletteColorMapping + * Palette color mapping that is being copied + * @param parent + * Parent widget for this dialog. + * @param errorMessageOut + * If not empty, there was an error + * @return + * True if the user pressed OK and palettes were copied. + * False if user pressed Cancel or if there was an error (errorMessageOut + * will contain description of the error). + */ +bool +CopyPaletteColorMappingToFilesDialog::run(CaretMappableDataFile* selectedMapFile, + const PaletteColorMapping* selectedPaletteColorMapping, + QWidget* parent, + AString& errorMessageOut) +{ + errorMessageOut.clear(); + + EventCaretMappableDataFilesGet mapFilesGet; + EventManager::get()->sendEvent(mapFilesGet.getPointer()); + std::vector allMapFiles; + mapFilesGet.getAllFiles(allMapFiles); + + std::vector paletteMappedFiles; + for (auto mapFile: allMapFiles) { + if (mapFile->isMappedWithPalette()) { + paletteMappedFiles.push_back(mapFile); + } + } + allMapFiles.clear(); + + /* + * Sort by Type and Filename + */ + std::sort(paletteMappedFiles.begin(), + paletteMappedFiles.end(), + [](CaretMappableDataFile* m1, CaretMappableDataFile* m2) { + if (m1->getDataFileType() == m2->getDataFileType()) { + return (m1->getFileNameNoPath().toLower() + < m2->getFileNameNoPath().toLower()); + } + + return (DataFileTypeEnum::toShortGuiName(m1->getDataFileType()) + < DataFileTypeEnum::toShortGuiName(m2->getDataFileType())); + } ); + + bool noFilesFlag = false; + if (paletteMappedFiles.empty()) { + noFilesFlag = true; + } + else if (paletteMappedFiles.size() == 1) { + CaretAssert(paletteMappedFiles[0] == selectedMapFile); + noFilesFlag = true; + } + if (noFilesFlag) { + errorMessageOut = "There are no other files that accept copying of palette color mapping."; + return false; + } + + CopyPaletteColorMappingToFilesDialog dialog(paletteMappedFiles, + selectedMapFile, + selectedPaletteColorMapping, + parent); + + if (dialog.exec() == CopyPaletteColorMappingToFilesDialog::Accepted) { + return true; + } + + return false; +} + + + diff --git a/src/GuiQt/CopyPaletteColorMappingToFilesDialog.h b/src/GuiQt/CopyPaletteColorMappingToFilesDialog.h new file mode 100644 index 0000000000000000000000000000000000000000..55e118988c1d61bf90e3a8423bd194a69f74db2a --- /dev/null +++ b/src/GuiQt/CopyPaletteColorMappingToFilesDialog.h @@ -0,0 +1,87 @@ +#ifndef __COPY_PALETTE_COLOR_MAPPING_TO_FILES_DIALOG_H__ +#define __COPY_PALETTE_COLOR_MAPPING_TO_FILES_DIALOG_H__ + +/*LICENSE_START*/ +/* + * Copyright (C) 2018 Washington University School of Medicine + * + * 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 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ +/*LICENSE_END*/ + + + +#include +#include + +#include "WuQDialogModal.h" + +class QCheckBox; + +namespace caret { + + class CaretMappableDataFile; + class PaletteColorMapping; + + class CopyPaletteColorMappingToFilesDialog : public WuQDialogModal { + + Q_OBJECT + + public: + static bool run(CaretMappableDataFile* selectedMapFile, + const PaletteColorMapping* selectedPaletteColorMapping, + QWidget* parent, + AString& errorMessageOut); + + virtual ~CopyPaletteColorMappingToFilesDialog(); + + protected: + virtual void okButtonClicked() override; + + // ADD_NEW_METHODS_HERE + + private slots: + void allCheckBoxesOnClicked(); + + void allCheckBoxesOffClicked(); + + private: + CopyPaletteColorMappingToFilesDialog(const std::vector& allPaletteMapFiles, + CaretMappableDataFile* selectedMapFile, + const PaletteColorMapping* selectedPaletteColorMapping, + QWidget* parent); + + CopyPaletteColorMappingToFilesDialog(const CopyPaletteColorMappingToFilesDialog&); + + CopyPaletteColorMappingToFilesDialog& operator=(const CopyPaletteColorMappingToFilesDialog&); + + const CaretMappableDataFile* m_selectedMapFile; + + const PaletteColorMapping* m_selectedPaletteColorMapping; + + std::vector> m_fileCheckBoxes; + + static std::set s_previousCheckedFiles; + + // ADD_NEW_MEMBERS_HERE + + }; + +#ifdef __COPY_PALETTE_COLOR_MAPPING_TO_FILES_DIALOG_DECLARE__ + std::set CopyPaletteColorMappingToFilesDialog::s_previousCheckedFiles; +#endif // __COPY_PALETTE_COLOR_MAPPING_TO_FILES_DIALOG_DECLARE__ + +} // namespace +#endif //__COPY_PALETTE_COLOR_MAPPING_TO_FILES_DIALOG_H__ diff --git a/src/GuiQt/DataFileContentCopyMoveDialog.cxx b/src/GuiQt/DataFileContentCopyMoveDialog.cxx index 0fe33cbbd451a462f93b83a2f331b40713f8f40e..86aa983270878ae0dedd97cb757804ad0d26914a 100644 --- a/src/GuiQt/DataFileContentCopyMoveDialog.cxx +++ b/src/GuiQt/DataFileContentCopyMoveDialog.cxx @@ -135,7 +135,7 @@ DataFileContentCopyMoveDialog::createOptionsWidget() QObject::connect(m_closeSourceFileCheckBox, SIGNAL(toggled(bool)), this, SLOT(closeSourceFileCheckBoxToggled(bool))); - m_copySelectedAnnotationsOnlyCheckBox = new QCheckBox("Copy Only Selected Annotations"); + m_copySelectedAnnotationsOnlyCheckBox = new QCheckBox("Copy Only Annotations SELECTED FOR EDITING"); m_copySelectedAnnotationsOnlyCheckBox->setChecked(false); QGroupBox* groupBox = new QGroupBox("Options"); @@ -194,8 +194,8 @@ DataFileContentCopyMoveDialog::createDestinationWidget() newDestinationFileToolButton->setText("New File Name..."); m_newDestinationFileNameLabel = new QLabel(" "); m_newDestinatonFileButtonGroupIndex = m_destinationButtonGroup->buttons().size(); - QRadioButton* newFileRadioButton = new QRadioButton(""); - m_destinationButtonGroup->addButton(newFileRadioButton, + m_newDestinationFileRadioButton = new QRadioButton(""); + m_destinationButtonGroup->addButton(m_newDestinationFileRadioButton, m_newDestinatonFileButtonGroupIndex); @@ -210,7 +210,7 @@ DataFileContentCopyMoveDialog::createDestinationWidget() while (buttonIter.hasNext()) { const int row = layout->rowCount(); QAbstractButton* button = buttonIter.next(); - if (button == newFileRadioButton) { + if (button == m_newDestinationFileRadioButton) { layout->addWidget(button, row, 0); layout->addWidget(newDestinationFileToolButton, @@ -242,6 +242,7 @@ DataFileContentCopyMoveDialog::newDestinationFileToolButtonClicked() m_newDestinationFileName = fileName; FileInformation fileInfo(fileName); m_newDestinationFileNameLabel->setText(fileInfo.getFileName()); + m_newDestinationFileRadioButton->setChecked(true); } } @@ -261,8 +262,7 @@ DataFileContentCopyMoveDialog::okButtonClicked() if (m_newDestinationFileName.isEmpty()) { throw DataFileException("New file name is empty."); } - const CaretDataFile* caretDataFile = dynamic_cast(m_sourceDataFileInterface); - CaretAssert(caretDataFile); + CaretAssert(dynamic_cast(m_sourceDataFileInterface)); destinationFile = m_sourceDataFileInterface->newInstanceOfDataFile(); if (destinationFile == NULL) { throw DataFileException("Failed to created new file."); diff --git a/src/GuiQt/DataFileContentCopyMoveDialog.h b/src/GuiQt/DataFileContentCopyMoveDialog.h index fe23492cda0857e977491e1f563964a043705a17..a27fd3dd7a690cca09ec2e3b17377002f2154183 100644 --- a/src/GuiQt/DataFileContentCopyMoveDialog.h +++ b/src/GuiQt/DataFileContentCopyMoveDialog.h @@ -27,6 +27,7 @@ class QButtonGroup; class QCheckBox; class QLabel; +class QRadioButton; namespace caret { @@ -79,6 +80,8 @@ namespace caret { QString m_newDestinationFileName; + QRadioButton* m_newDestinationFileRadioButton; + QCheckBox* m_closeSourceFileCheckBox; QCheckBox* m_copySelectedAnnotationsOnlyCheckBox; diff --git a/src/GuiQt/EventBrowserWindowContentGet.cxx b/src/GuiQt/EventBrowserWindowDrawingContent.cxx similarity index 59% rename from src/GuiQt/EventBrowserWindowContentGet.cxx rename to src/GuiQt/EventBrowserWindowDrawingContent.cxx index 9f80f0e260a38d213f32912c92c64f3fed9dae3b..21fdad077eab1936d14c17b3f5c283cd5e5c6313 100644 --- a/src/GuiQt/EventBrowserWindowContentGet.cxx +++ b/src/GuiQt/EventBrowserWindowDrawingContent.cxx @@ -18,14 +18,14 @@ */ /*LICENSE_END*/ -#include "EventBrowserWindowContentGet.h" +#include "EventBrowserWindowDrawingContent.h" using namespace caret; #include "CaretAssert.h" /** - * \class caret::DisplayGroupEnumComboBox - * \brief Combo box for selection of a display group. + * \class caret::EventBrowserWindowDrawingContent + * \brief Event to get content for drawing browser window. * \ingroup GuiQt */ @@ -34,19 +34,20 @@ using namespace caret; * @param browserWindowIndex * Index of browser window. */ -EventBrowserWindowContentGet::EventBrowserWindowContentGet(const int32_t browserWindowIndex) -: Event(EventTypeEnum::EVENT_BROWSER_WINDOW_CONTENT_GET) +EventBrowserWindowDrawingContent::EventBrowserWindowDrawingContent(const int32_t browserWindowIndex) +: Event(EventTypeEnum::EVENT_BROWSER_WINDOW_DRAWING_CONTENT_GET) { m_browserWindowIndex = browserWindowIndex; m_tabIndexForTileTabsHighlighting = -1; m_tileTabsConfiguration = NULL; m_selectedBrowserTabContent = NULL; + m_tileTabsSelectedFlag = false; } /* * Destructor. */ -EventBrowserWindowContentGet::~EventBrowserWindowContentGet() +EventBrowserWindowDrawingContent::~EventBrowserWindowDrawingContent() { } @@ -55,39 +56,39 @@ EventBrowserWindowContentGet::~EventBrowserWindowContentGet() * @return Get the browser window index. */ int32_t -EventBrowserWindowContentGet::getBrowserWindowIndex() const +EventBrowserWindowDrawingContent::getBrowserWindowIndex() const { return m_browserWindowIndex; } /** - * @return The number of items to draw. + * @return The number of browser tabs in window */ int32_t -EventBrowserWindowContentGet::getNumberOfItemsToDraw() const +EventBrowserWindowDrawingContent::getNumberOfBrowserTabs() const { return this->browserTabContents.size(); } /** - * Add tab content for drawing in a window. + * Add browser tab */ void -EventBrowserWindowContentGet::addTabContentToDraw(BrowserTabContent* browserTabContent) +EventBrowserWindowDrawingContent::addBrowserTab(BrowserTabContent* browserTabContent) { this->browserTabContents.push_back(browserTabContent); } /** - * Get the tab content for drawing in a window. + * Get the browser tab at the given index * * @param itemIndex - * Index of the item to draw. + * Index of the item to browser tab. * @return * Pointer to tab contents for the item index. */ BrowserTabContent* -EventBrowserWindowContentGet::getTabContentToDraw(const int32_t itemIndex) +EventBrowserWindowDrawingContent::getBrowserTab(const int32_t itemIndex) { CaretAssertVectorIndex(this->browserTabContents, itemIndex); return this->browserTabContents[itemIndex]; @@ -100,7 +101,7 @@ EventBrowserWindowContentGet::getTabContentToDraw(const int32_t itemIndex) * Index of tab for highlighting. */ void -EventBrowserWindowContentGet::setTabIndexForTileTabsHighlighting(const int32_t tabIndex) +EventBrowserWindowDrawingContent::setTabIndexForTileTabsHighlighting(const int32_t tabIndex) { m_tabIndexForTileTabsHighlighting = tabIndex; } @@ -109,7 +110,7 @@ EventBrowserWindowContentGet::setTabIndexForTileTabsHighlighting(const int32_t t * @return Index of tab for highlighting in Tile Tabs mode. */ int32_t -EventBrowserWindowContentGet::getTabIndexForTileTabsHighlighting() const +EventBrowserWindowDrawingContent::getTabIndexForTileTabsHighlighting() const { return m_tabIndexForTileTabsHighlighting; } @@ -119,7 +120,7 @@ EventBrowserWindowContentGet::getTabIndexForTileTabsHighlighting() const * May be NULL. */ TileTabsConfiguration* -EventBrowserWindowContentGet::getTileTabsConfiguration() const +EventBrowserWindowDrawingContent::getTileTabsConfiguration() const { return m_tileTabsConfiguration; } @@ -131,16 +132,17 @@ EventBrowserWindowContentGet::getTileTabsConfiguration() const * New selected tile tabs configuration. */ void -EventBrowserWindowContentGet::setTileTabsConfiguration(TileTabsConfiguration* tileTabsConfiguration) +EventBrowserWindowDrawingContent::setTileTabsConfiguration(TileTabsConfiguration* tileTabsConfiguration) { m_tileTabsConfiguration = tileTabsConfiguration; } /** * @return The selected browser tab content. May be NULL. + * In single tab mode, this is the tab to draw. */ BrowserTabContent* -EventBrowserWindowContentGet::getSelectedBrowserTabContent() +EventBrowserWindowDrawingContent::getSelectedBrowserTabContent() { return m_selectedBrowserTabContent; } @@ -152,10 +154,31 @@ EventBrowserWindowContentGet::getSelectedBrowserTabContent() * The selected browser tab content. */ void -EventBrowserWindowContentGet::setSelectedBrowserTabContent(BrowserTabContent* browserTabContent) +EventBrowserWindowDrawingContent::setSelectedBrowserTabContent(BrowserTabContent* browserTabContent) { m_selectedBrowserTabContent = browserTabContent; } +/** + * @return Is tile tabs selected. + */ +bool +EventBrowserWindowDrawingContent::isTileTabsSelected() const +{ + return m_tileTabsSelectedFlag; +} + +/** + * Set tile tabs selected. + * + * @param tileTabsSelected + * New status of tile tabs. + */ +void +EventBrowserWindowDrawingContent::setTileTabsSelected(const bool tileTabsSelected) +{ + m_tileTabsSelectedFlag = tileTabsSelected; +} + diff --git a/src/GuiQt/EventBrowserWindowContentGet.h b/src/GuiQt/EventBrowserWindowDrawingContent.h similarity index 69% rename from src/GuiQt/EventBrowserWindowContentGet.h rename to src/GuiQt/EventBrowserWindowDrawingContent.h index ee483c4579f35acf9dae62e40329f571ba773c61..03c34ab6994ef769c886328bd2426aa7bb9b5160 100644 --- a/src/GuiQt/EventBrowserWindowContentGet.h +++ b/src/GuiQt/EventBrowserWindowDrawingContent.h @@ -1,5 +1,5 @@ -#ifndef __EVENT_BROWSER_WINDOW_CONTENT_GET_H__ -#define __EVENT_BROWSER_WINDOW_CONTENT_GET_H__ +#ifndef __EVENT_BROWSER_WINDOW_DRAWING_CONTENT_GET_H__ +#define __EVENT_BROWSER_WINDOW_DRAWING_CONTENT_GET_H__ /*LICENSE_START*/ /* @@ -31,20 +31,20 @@ namespace caret { class TileTabsConfiguration; /// Get the content of a browser window - class EventBrowserWindowContentGet : public Event { + class EventBrowserWindowDrawingContent : public Event { public: - EventBrowserWindowContentGet(const int32_t browserWindowIndex); + EventBrowserWindowDrawingContent(const int32_t browserWindowIndex); - virtual ~EventBrowserWindowContentGet(); + virtual ~EventBrowserWindowDrawingContent(); int32_t getBrowserWindowIndex() const; - int32_t getNumberOfItemsToDraw() const; + int32_t getNumberOfBrowserTabs() const; - void addTabContentToDraw(BrowserTabContent* browserTabContent); + void addBrowserTab(BrowserTabContent* browserTabContent); - BrowserTabContent* getTabContentToDraw(const int32_t itemIndex); + BrowserTabContent* getBrowserTab(const int32_t itemIndex); void setTabIndexForTileTabsHighlighting(const int32_t tabIndex); @@ -58,17 +58,21 @@ namespace caret { void setSelectedBrowserTabContent(BrowserTabContent* browserTabContent); + bool isTileTabsSelected() const; + + void setTileTabsSelected(const bool tileTabsSelected); + private: - EventBrowserWindowContentGet(const EventBrowserWindowContentGet&); + EventBrowserWindowDrawingContent(const EventBrowserWindowDrawingContent&); - EventBrowserWindowContentGet& operator=(const EventBrowserWindowContentGet&); + EventBrowserWindowDrawingContent& operator=(const EventBrowserWindowDrawingContent&); BrowserTabContent* m_selectedBrowserTabContent; /** index of browswer window */ int32_t m_browserWindowIndex; - /** Tab content that are to be drawn in the window */ + /** all browser tabs in the window */ std::vector browserTabContents; /** Index of tab that is highlighted in Tile Tabs mode */ @@ -76,8 +80,10 @@ namespace caret { /** Selected tile tabs configuration when more than one item to draw */ TileTabsConfiguration* m_tileTabsConfiguration; + + bool m_tileTabsSelectedFlag; }; } // namespace -#endif // __EVENT_BROWSER_WINDOW_CONTENT_GET_H__ +#endif // __EVENT_BROWSER_WINDOW_DRAWING_CONTENT_GET_H__ diff --git a/src/GuiQt/EventBrowserWindowTileTabOperation.cxx b/src/GuiQt/EventBrowserWindowTileTabOperation.cxx new file mode 100644 index 0000000000000000000000000000000000000000..52a52281a163968093b0f98d04fa6ff4ef4246e6 --- /dev/null +++ b/src/GuiQt/EventBrowserWindowTileTabOperation.cxx @@ -0,0 +1,131 @@ + +/*LICENSE_START*/ +/* + * Copyright (C) 2017 Washington University School of Medicine + * + * 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 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ +/*LICENSE_END*/ + +#define __EVENT_BROWSER_WINDOW_TILE_TAB_OPERATION_DECLARE__ +#include "EventBrowserWindowTileTabOperation.h" +#undef __EVENT_BROWSER_WINDOW_TILE_TAB_OPERATION_DECLARE__ + +#include + +#include "CaretAssert.h" +#include "EventManager.h" +#include "EventTypeEnum.h" +#include "WuQMessageBox.h" +using namespace caret; + + + +/** + * \class caret::EventBrowserWindowTileTabOperation + * \brief Operations for tab changes + * \ingroup GuiQt + */ + +/** + * Constructor. + * + * @param operation + * The operation + * @param parentWidget + * Parent widget for error dialogs. + * @param windowIndex + * Index of the window. + * @param browserTabIndex + * Index of the browser tab. + */ +EventBrowserWindowTileTabOperation::EventBrowserWindowTileTabOperation(const Operation operation, + QWidget* parentWidget, + const int32_t windowIndex, + const int32_t browserTabIndex) +: Event(EventTypeEnum::EVENT_BROWSER_WINDOW_TILE_TAB_OPERATION), +m_operation(operation), +m_parentWidget(parentWidget), +m_windowIndex(windowIndex), +m_browserTabIndex(browserTabIndex) +{ + CaretAssert(m_parentWidget); + CaretAssert(m_windowIndex >= 0); + CaretAssert(m_browserTabIndex >= 0); +} + +/** + * Destructor. + */ +EventBrowserWindowTileTabOperation::~EventBrowserWindowTileTabOperation() +{ +} + +/** + * Operation that selects the given tab in the given window. + * + * @param parentWidget + * Parent widget for error dialogs. + * @param windowIndex + * Index of the window. + * @param browserTabIndex + * Index of the browser tab. + */ +void +EventBrowserWindowTileTabOperation::selectTabInWindow(QWidget* parentWidget, + const int32_t windowIndex, + const int32_t browserTabIndex) +{ + EventBrowserWindowTileTabOperation tabOperation(Operation::OPERATION_SELECT_TAB, + parentWidget, + windowIndex, + browserTabIndex); + + EventManager::get()->sendEvent(tabOperation.getPointer()); + + if (tabOperation.getEventProcessCount() <= 0) { + WuQMessageBox::errorOk(parentWidget, + "Tab not created, invalid window or tab index"); + } +} + +/** + * @return The mode + */ +EventBrowserWindowTileTabOperation::Operation +EventBrowserWindowTileTabOperation::getOperation() const +{ + return m_operation; +} + +/** + * @return The window index. + */ +int32_t +EventBrowserWindowTileTabOperation::getWindowIndex() const +{ + return m_windowIndex; +} + +/** + * @return The browser tab index. + */ +int32_t +EventBrowserWindowTileTabOperation::getBrowserTabIndex() const +{ + return m_browserTabIndex; +} + + diff --git a/src/GuiQt/EventBrowserWindowTileTabOperation.h b/src/GuiQt/EventBrowserWindowTileTabOperation.h new file mode 100644 index 0000000000000000000000000000000000000000..39540e96e610c51e6dd3ac45df914812c2607560 --- /dev/null +++ b/src/GuiQt/EventBrowserWindowTileTabOperation.h @@ -0,0 +1,83 @@ +#ifndef __EVENT_BROWSER_WINDOW_TILE_TAB_OPERATION_H__ +#define __EVENT_BROWSER_WINDOW_TILE_TAB_OPERATION_H__ + +/*LICENSE_START*/ +/* + * Copyright (C) 2017 Washington University School of Medicine + * + * 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 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ +/*LICENSE_END*/ + + + +#include + +#include "Event.h" + +class QWidget; + +namespace caret { + + class EventBrowserWindowTileTabOperation : public Event { + + public: + static void selectTabInWindow(QWidget* parentWidget, + const int32_t windowIndex, + const int32_t browserTabIndex); + enum Operation { + OPERATION_NEW_TAB_AFTER, + OPERATION_NEW_TAB_BEFORE, + OPERATION_SELECT_TAB + }; + + EventBrowserWindowTileTabOperation(const Operation operation, + QWidget* parentWidget, + const int32_t windowIndex, + const int32_t browserTabIndex); + + virtual ~EventBrowserWindowTileTabOperation(); + + Operation getOperation() const; + + int32_t getWindowIndex() const; + + int32_t getBrowserTabIndex() const; + + // ADD_NEW_METHODS_HERE + + private: + EventBrowserWindowTileTabOperation(const EventBrowserWindowTileTabOperation&); + + EventBrowserWindowTileTabOperation& operator=(const EventBrowserWindowTileTabOperation&); + + const Operation m_operation; + + QWidget* m_parentWidget; + + const int32_t m_windowIndex; + + const int32_t m_browserTabIndex; + + // ADD_NEW_MEMBERS_HERE + + }; + +#ifdef __EVENT_BROWSER_WINDOW_TILE_TAB_OPERATION_DECLARE__ + // +#endif // __EVENT_BROWSER_WINDOW_TILE_TAB_OPERATION_DECLARE__ + +} // namespace +#endif //__EVENT_BROWSER_WINDOW_TILE_TAB_OPERATION_H__ diff --git a/src/GuiQt/EventHelpViewerDisplay.cxx b/src/GuiQt/EventHelpViewerDisplay.cxx index 826f0916237651d971528b44d4decdea1f9a7e50..984b909992fe2fd84b16ec6ab38507c1b0854463 100644 --- a/src/GuiQt/EventHelpViewerDisplay.cxx +++ b/src/GuiQt/EventHelpViewerDisplay.cxx @@ -37,13 +37,19 @@ using namespace caret; */ /** - * Constructor displays help dialog with default topic. + * Constructor displays help dialog with specific topic. + * + * @param brainBrowserWindow + * Parent window. + * @param helpPageName + * Name of HTML file without any path nor ".html" extension. */ -EventHelpViewerDisplay::EventHelpViewerDisplay(BrainBrowserWindow* brainBrowserWindow) -: Event(EventTypeEnum::EVENT_HELP_VIEWER_DISPLAY) +EventHelpViewerDisplay::EventHelpViewerDisplay(BrainBrowserWindow* brainBrowserWindow, + const AString helpPageName) +: Event(EventTypeEnum::EVENT_HELP_VIEWER_DISPLAY), +m_brainBrowserWindow(brainBrowserWindow), +m_helpPageName(helpPageName) { - m_brainBrowserWindow = brainBrowserWindow; - m_helpPageName = ""; } /** @@ -57,7 +63,7 @@ EventHelpViewerDisplay::~EventHelpViewerDisplay() * @return Brain browser window on which a new help viewer dialog is displayed * May be NULL in which case any browser window should be used as parent. */ -BrainBrowserWindow* +const BrainBrowserWindow* EventHelpViewerDisplay::getBrainBrowserWindow() const { return m_brainBrowserWindow; diff --git a/src/GuiQt/EventHelpViewerDisplay.h b/src/GuiQt/EventHelpViewerDisplay.h index f59eb07eded8328f42a80a6c1b516865c2235ed9..72831e07b414f4413c24def4cb0cde05e6ffbd74 100644 --- a/src/GuiQt/EventHelpViewerDisplay.h +++ b/src/GuiQt/EventHelpViewerDisplay.h @@ -30,11 +30,12 @@ namespace caret { class EventHelpViewerDisplay : public Event { public: - EventHelpViewerDisplay(BrainBrowserWindow* brainBrowserWindow); + EventHelpViewerDisplay(BrainBrowserWindow* brainBrowserWindow, + const AString helpPageName); virtual ~EventHelpViewerDisplay(); - BrainBrowserWindow* getBrainBrowserWindow() const; + const BrainBrowserWindow* getBrainBrowserWindow() const; AString getHelpPageName() const; @@ -45,9 +46,9 @@ namespace caret { EventHelpViewerDisplay& operator=(const EventHelpViewerDisplay&); - BrainBrowserWindow* m_brainBrowserWindow; + const BrainBrowserWindow* m_brainBrowserWindow; - AString m_helpPageName; + const AString m_helpPageName; // ADD_NEW_MEMBERS_HERE diff --git a/src/GuiQt/EventImageCapture.cxx b/src/GuiQt/EventImageCapture.cxx index 1cc519a2336cb6ab48891391c0553b8bd6162d73..6ced0e141f153f45eba2a7f230b606577380cff7 100644 --- a/src/GuiQt/EventImageCapture.cxx +++ b/src/GuiQt/EventImageCapture.cxx @@ -37,7 +37,8 @@ using namespace caret; */ /** - * Constructor for capturing image of window without any resizing. + * Constructor for capturing image of window with the image + * dimensions identical to the dimensions of the window. * * @param browserWindowIndex * The browser window index. diff --git a/src/GuiQt/EventImageCapture.h b/src/GuiQt/EventImageCapture.h index fd0a842d36e6af9bdd3518ba29d72b87c51374d3..005be49a41a5ddf61bb6cfeab29dc589b08aaf2d 100644 --- a/src/GuiQt/EventImageCapture.h +++ b/src/GuiQt/EventImageCapture.h @@ -30,6 +30,8 @@ namespace caret { class EventImageCapture : public Event { public: + EventImageCapture(const int32_t browserWindowIndex); + EventImageCapture(const int32_t browserWindowIndex, const int32_t captureOffsetX, const int32_t captureOffsetY, @@ -38,8 +40,6 @@ namespace caret { const int32_t outputWidth, const int32_t outputHeight); - EventImageCapture(const int32_t browserWindowIndex); - virtual ~EventImageCapture(); int32_t getBrowserWindowIndex() const; diff --git a/src/GuiQt/EventOverlaySettingsEditorDialogRequest.cxx b/src/GuiQt/EventOverlaySettingsEditorDialogRequest.cxx index cde63bec0a8367b3112ecc54e3f31d22d7f14be0..c5252c6aa688d2adf03b48ffedfbddaee83d265b 100644 --- a/src/GuiQt/EventOverlaySettingsEditorDialogRequest.cxx +++ b/src/GuiQt/EventOverlaySettingsEditorDialogRequest.cxx @@ -30,27 +30,65 @@ using namespace caret; /** * Constructor. + * + * @param mode + * The mode. * @param browserWindowIndex * Index of browser window. * @param overlay * Overlay for map editor. * @param mapFile * Caret Mappable Data File. - * @param mapIndex - * Map index in mapFile. + * @param selectedIndex + * Selected index in mapFile. */ EventOverlaySettingsEditorDialogRequest::EventOverlaySettingsEditorDialogRequest(const Mode mode, const int32_t browserWindowIndex, Overlay* overlay, CaretMappableDataFile* mapFile, - const int32_t mapIndex) + const int32_t selectedIndex) : Event(EventTypeEnum::EVENT_OVERLAY_SETTINGS_EDITOR_SHOW), m_mode(mode) { m_browserWindowIndex = browserWindowIndex; m_overlay = overlay; + m_chartOverlay = NULL; + m_mapFile = mapFile; + m_chartSelectedIndexType = ChartTwoOverlay::SelectedIndexType::INVALID; + m_selectedIndex = selectedIndex; +} + +/** + * Constructor. + * + * @param mode + * The mode. + * @param browserWindowIndex + * Index of browser window. + * @param overlay + * Overlay for map editor. + * @param mapFile + * Caret Mappable Data File. + * @param selectedIndexType + * Type of index selected in chart. + * @param selectedIndex + * Selected index in mapFile. + */ +EventOverlaySettingsEditorDialogRequest::EventOverlaySettingsEditorDialogRequest(const Mode mode, + const int32_t browserWindowIndex, + ChartTwoOverlay* chartOverlay, + CaretMappableDataFile* mapFile, + ChartTwoOverlay::SelectedIndexType selectedIndexType, + const int32_t selectedIndex) +: Event(EventTypeEnum::EVENT_OVERLAY_SETTINGS_EDITOR_SHOW), +m_mode(mode) +{ + m_browserWindowIndex = browserWindowIndex; + m_overlay = NULL; + m_chartOverlay = chartOverlay; m_mapFile = mapFile; - m_mapIndex = mapIndex; + m_chartSelectedIndexType = selectedIndexType; + m_selectedIndex = selectedIndex; } /* diff --git a/src/GuiQt/EventOverlaySettingsEditorDialogRequest.h b/src/GuiQt/EventOverlaySettingsEditorDialogRequest.h index 2ce02f8d44448381dca9eae933f6ccc5ee387fcc..06689b115a4da10bf70f95f8e1377ca3c73feba8 100644 --- a/src/GuiQt/EventOverlaySettingsEditorDialogRequest.h +++ b/src/GuiQt/EventOverlaySettingsEditorDialogRequest.h @@ -21,12 +21,13 @@ */ /*LICENSE_END*/ - +#include "ChartTwoOverlay.h" #include "Event.h" namespace caret { class CaretMappableDataFile; + class ChartTwoOverlay; class Overlay; /// Event for showing edit map scalar color mapping editor @@ -42,7 +43,14 @@ namespace caret { const int32_t browserWindowIndex, Overlay* overlay, CaretMappableDataFile* mapFile, - const int32_t mapIndex); + const int32_t selectedIndex); + + EventOverlaySettingsEditorDialogRequest(const Mode mode, + const int32_t browserWindowIndex, + ChartTwoOverlay* chartOverlay, + CaretMappableDataFile* mapFile, + ChartTwoOverlay::SelectedIndexType selectedIndexType, + const int32_t selectedIndex); virtual ~EventOverlaySettingsEditorDialogRequest(); @@ -61,10 +69,10 @@ namespace caret { */ CaretMappableDataFile* getCaretMappableDataFile() const { return m_mapFile; } - /** - * @return Index of map in the map file - */ - int32_t getMapIndex() const { return m_mapIndex; } +// /** +// * @return Index of selection in the map file +// */ +// int32_t getSelectedIndex() const { return m_selectedIndex; } /** * @return The overlay for the editor. @@ -76,6 +84,21 @@ namespace caret { */ const Overlay* getOverlay() const { return m_overlay; } + /** + * @return The chart overlay for the editor. + */ + ChartTwoOverlay* getChartTwoOverlay() { return m_chartOverlay; } + + /** + * @return The overlay for the editor. + */ + const ChartTwoOverlay* getChartTwoOverlay() const { return m_chartOverlay; } + +// /** +// * @return The type of index for chart file +// */ +// ChartTwoOverlay::SelectedIndexType getChartSelectedIndexType() const { return m_chartSelectedIndexType; } + private: EventOverlaySettingsEditorDialogRequest(const EventOverlaySettingsEditorDialogRequest&); @@ -90,11 +113,17 @@ namespace caret { /** Overlay for editor. */ Overlay* m_overlay; + /** Chart Overlay for editor. */ + ChartTwoOverlay* m_chartOverlay; + /** Map file containing map whose color palette is edited */ CaretMappableDataFile* m_mapFile; - /** Index of map in the map file */ - int32_t m_mapIndex; + /** Selected index type for charts */ + ChartTwoOverlay::SelectedIndexType m_chartSelectedIndexType; + + /** Selected index in the map file */ + int32_t m_selectedIndex; }; } // namespace diff --git a/src/GuiQt/EventShowDataFileReadWarningsDialog.cxx b/src/GuiQt/EventShowDataFileReadWarningsDialog.cxx new file mode 100644 index 0000000000000000000000000000000000000000..7bdbb3e4e99b0a047e66e31988c7c9e23368a6ca --- /dev/null +++ b/src/GuiQt/EventShowDataFileReadWarningsDialog.cxx @@ -0,0 +1,75 @@ + +/*LICENSE_START*/ +/* + * Copyright (C) 2018 Washington University School of Medicine + * + * 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 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ +/*LICENSE_END*/ + +#define __EVENT_DISPLAY_DATA_FILE_READ_WARNINGS_DIALOG_DECLARE__ +#include "EventShowDataFileReadWarningsDialog.h" +#undef __EVENT_DISPLAY_DATA_FILE_READ_WARNINGS_DIALOG_DECLARE__ + +#include "CaretAssert.h" +#include "EventTypeEnum.h" + +using namespace caret; + + + +/** + * \class caret::EventShowDataFileReadWarningsDialog + * \brief This event is sent after files are read to show any warnings during file reading + * \ingroup GuiQt + * + * All data files are tested for the prescence of warnings from reading of the files. + * If there are warnings, a dialog is displayed listing the file read warnings. + * If there are no file read warnings, no dialog is displayed. + */ + +/** + * Constructor. + * + * @param browserWindowIndex + * Index of browser window on which dialog is displayed. + * If the window index is invalid, the warning + * will be displayed on an open browser window. + */ +EventShowDataFileReadWarningsDialog::EventShowDataFileReadWarningsDialog(const int32_t browserWindowIndex) +: Event(EventTypeEnum::EVENT_SHOW_FILE_DATA_READ_WARNING_DIALOG), +m_browserWindowIndex(browserWindowIndex) +{ + +} + +/** + * Destructor. + */ +EventShowDataFileReadWarningsDialog::~EventShowDataFileReadWarningsDialog() +{ +} + +/** + * @return Index of window on which the dialog is displayed. If + * the index is not a valid window, the dialog is dispalayed over + * any available browser window. + */ +int32_t +EventShowDataFileReadWarningsDialog::getBrowserWindowIndex() const +{ + return m_browserWindowIndex; +} + diff --git a/src/GuiQt/EventShowDataFileReadWarningsDialog.h b/src/GuiQt/EventShowDataFileReadWarningsDialog.h new file mode 100644 index 0000000000000000000000000000000000000000..a81ee8f751ef94bfbea0ab6b7dcb2e388d7a48c8 --- /dev/null +++ b/src/GuiQt/EventShowDataFileReadWarningsDialog.h @@ -0,0 +1,61 @@ +#ifndef __EVENT_DISPLAY_DATA_FILE_READ_WARNINGS_DIALOG_H__ +#define __EVENT_DISPLAY_DATA_FILE_READ_WARNINGS_DIALOG_H__ + +/*LICENSE_START*/ +/* + * Copyright (C) 2018 Washington University School of Medicine + * + * 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 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ +/*LICENSE_END*/ + + + +#include + +#include "Event.h" + + + +namespace caret { + + class EventShowDataFileReadWarningsDialog : public Event { + + public: + EventShowDataFileReadWarningsDialog(const int32_t browserWindowIndex = -1); + + virtual ~EventShowDataFileReadWarningsDialog(); + + int32_t getBrowserWindowIndex() const; + + // ADD_NEW_METHODS_HERE + + private: + EventShowDataFileReadWarningsDialog(const EventShowDataFileReadWarningsDialog&); + + EventShowDataFileReadWarningsDialog& operator=(const EventShowDataFileReadWarningsDialog&); + + const int32_t m_browserWindowIndex; + + // ADD_NEW_MEMBERS_HERE + + }; + +#ifdef __EVENT_DISPLAY_DATA_FILE_READ_WARNINGS_DIALOG_DECLARE__ + // +#endif // __EVENT_DISPLAY_DATA_FILE_READ_WARNINGS_DIALOG_DECLARE__ + +} // namespace +#endif //__EVENT_DISPLAY_DATA_FILE_READ_WARNINGS_DIALOG_H__ diff --git a/src/GuiQt/EventUpdateYokedWindows.cxx b/src/GuiQt/EventUpdateYokedWindows.cxx index 5b6985a2319a898a06f37b72495daf247aa7f1e7..ab714be3d0151c80fb888567578ec03efaf4e206 100644 --- a/src/GuiQt/EventUpdateYokedWindows.cxx +++ b/src/GuiQt/EventUpdateYokedWindows.cxx @@ -40,14 +40,18 @@ using namespace caret; * * @param browserWindowIndexThatIssuedEvent * Index of browser window that issued event. - * @param yokingGroup - * Yoking group that is selected. + * @param brainModelYokingGroup + * brain model yoking group that is selected. + * @param chartModelYokingGroup + * chart model yoking group that is selected. */ EventUpdateYokedWindows::EventUpdateYokedWindows(const int32_t browserWindowIndexThatIssuedEvent, - const YokingGroupEnum::Enum yokingGroup) + const YokingGroupEnum::Enum brainModelYokingGroup, + const YokingGroupEnum::Enum chartModelYokingGroup) : Event(EventTypeEnum::EVENT_UPDATE_YOKED_WINDOWS), m_browserWindowIndexThatIssuedEvent(browserWindowIndexThatIssuedEvent), -m_yokingGroup(yokingGroup) +m_brainModelYokingGroup(brainModelYokingGroup), +m_chartModelYokingGroup(chartModelYokingGroup) { } @@ -56,13 +60,17 @@ m_yokingGroup(yokingGroup) * * @param browserWindowIndexThatIssuedEvent * Index of browser window that issued event. - * @param yokingGroup - * Yoking group that is selected. + * @param brainModelYokingGroup + * brain model yoking group that is selected. + * @param chartModelYokingGroup + * chart model yoking group that is selected. */ -EventUpdateYokedWindows::EventUpdateYokedWindows(const YokingGroupEnum::Enum yokingGroup) +EventUpdateYokedWindows::EventUpdateYokedWindows(const YokingGroupEnum::Enum brainModelYokingGroup, + const YokingGroupEnum::Enum chartModelYokingGroup) : Event(EventTypeEnum::EVENT_UPDATE_YOKED_WINDOWS), m_browserWindowIndexThatIssuedEvent(-1), -m_yokingGroup(yokingGroup) +m_brainModelYokingGroup(brainModelYokingGroup), +m_chartModelYokingGroup(chartModelYokingGroup) { } @@ -84,10 +92,51 @@ EventUpdateYokedWindows::getBrowserWindowIndexThatIssuedEvent() const } /** - * @return The yoking group that should be updated. + * @return Is the given brain model yoking group needing an update? + * + * @param brainModelYokingGroup + * The brain model yoking group. */ -YokingGroupEnum::Enum -EventUpdateYokedWindows::getYokingGroup() const +bool +EventUpdateYokedWindows::isBrainModelYoking(const YokingGroupEnum::Enum brainModelYokingGroup) const { - return m_yokingGroup; + if (m_brainModelYokingGroup == YokingGroupEnum::YOKING_GROUP_OFF) { + return false; + } + + return (m_brainModelYokingGroup == brainModelYokingGroup); +} + +/** + * @return Is the given chart model yoking group needing an update? + * + * @param chartModelYokingGroup + * The chart model yoking group. + */ +bool +EventUpdateYokedWindows::isChartModelYoking(const YokingGroupEnum::Enum chartModelYokingGroup) const +{ + if (m_chartModelYokingGroup == YokingGroupEnum::YOKING_GROUP_OFF) { + return false; + } + + return (m_chartModelYokingGroup == chartModelYokingGroup); } + + +/** + * @return Is the given brain OR chart model yoking group needing an update? + * + * @param brainModelYokingGroup + * The brain model yoking group. + * @param chartModelYokingGroup + * The chart model yoking group. + */ +bool +EventUpdateYokedWindows::isBrainOrChartModelYoking(const YokingGroupEnum::Enum brainModelYokingGroup, + const YokingGroupEnum::Enum chartModelYokingGroup) const +{ + return (isBrainModelYoking(brainModelYokingGroup) + || isChartModelYoking(chartModelYokingGroup)); +} + diff --git a/src/GuiQt/EventUpdateYokedWindows.h b/src/GuiQt/EventUpdateYokedWindows.h index fb3f207e504a02868120dc6e1aab3b4872c9747b..8dee39263d323d344b7c1e48a25c4c225e80833e 100644 --- a/src/GuiQt/EventUpdateYokedWindows.h +++ b/src/GuiQt/EventUpdateYokedWindows.h @@ -31,15 +31,22 @@ namespace caret { public: EventUpdateYokedWindows(const int32_t browserWindowIndexThatIssuedEvent, - const YokingGroupEnum::Enum yokingGroup); + const YokingGroupEnum::Enum brainModelYokingGroup, + const YokingGroupEnum::Enum chartModelYokingGroup); - EventUpdateYokedWindows(const YokingGroupEnum::Enum yokingGroup); + EventUpdateYokedWindows(const YokingGroupEnum::Enum brainModelYokingGroup, + const YokingGroupEnum::Enum chartModelYokingGroup); virtual ~EventUpdateYokedWindows(); int32_t getBrowserWindowIndexThatIssuedEvent() const; - YokingGroupEnum::Enum getYokingGroup() const; + bool isBrainModelYoking(const YokingGroupEnum::Enum brainModelYokingGroup) const; + + bool isChartModelYoking(const YokingGroupEnum::Enum chartModelYokingGroup) const; + + bool isBrainOrChartModelYoking(const YokingGroupEnum::Enum brainModelYokingGroup, + const YokingGroupEnum::Enum chartModelYokingGroup) const; private: EventUpdateYokedWindows(const EventUpdateYokedWindows&); @@ -54,7 +61,9 @@ namespace caret { const int32_t m_browserWindowIndexThatIssuedEvent; - const YokingGroupEnum::Enum m_yokingGroup; + const YokingGroupEnum::Enum m_brainModelYokingGroup; + + const YokingGroupEnum::Enum m_chartModelYokingGroup; // ADD_NEW_MEMBERS_HERE diff --git a/src/GuiQt/FiberSamplesOpenGLWidget.cxx b/src/GuiQt/FiberSamplesOpenGLWidget.cxx index c371ecfb31fa76d833501393798b4a7738816d73..88738a4fdba3568574ce9e7731a7a20eb36754e9 100644 --- a/src/GuiQt/FiberSamplesOpenGLWidget.cxx +++ b/src/GuiQt/FiberSamplesOpenGLWidget.cxx @@ -57,7 +57,11 @@ FiberSamplesOpenGLWidget::FiberSamplesOpenGLWidget(const int32_t browserWindowIn QLabel* fiberMeanLabels[3], QLabel* fiberVarianceLabels[3], QWidget* parent) +#ifdef WORKBENCH_USE_QT5_QOPENGL_WIDGET +: QOpenGLWidget(parent) +#else : QGLWidget(parent) +#endif { m_browserWindowIndex = browserWindowIndex; m_enabledCheckBox = enabledCheckBox; @@ -303,10 +307,10 @@ FiberSamplesOpenGLWidget::paintGL() // 0.7); const float rgba[4] = { - 0.7, - 0.7, - 0.7, - 1.0 + 0.7f, + 0.7f, + 0.7f, + 1.0f }; m_sphereBig->draw(rgba); @@ -679,6 +683,10 @@ FiberSamplesOpenGLWidget::mouseMoveEvent(QMouseEvent* me) me->accept(); - updateGL(); +#ifdef WORKBENCH_USE_QT5_QOPENGL_WIDGET + this->update(); +#else + this->updateGL(); +#endif } diff --git a/src/GuiQt/FiberSamplesOpenGLWidget.h b/src/GuiQt/FiberSamplesOpenGLWidget.h index 58aa458895f67fc1bf3e015d397224612ba6a958..84c0b8641f9dcfeddd95cd9a7e1f5e82f34ba369 100644 --- a/src/GuiQt/FiberSamplesOpenGLWidget.h +++ b/src/GuiQt/FiberSamplesOpenGLWidget.h @@ -21,7 +21,18 @@ */ /*LICENSE_END*/ +/* + * When GLEW is used, CaretOpenGLInclude.h will include "Gl/glew.h". + * Gl/glew.h MUST BE BEFORE Gl/gl.h and Gl/gl.h is included by + * QGLWidget so, we must include CaretOpenGL.h before QGLWidget. + */ +#include "CaretOpenGLInclude.h" + +#ifdef WORKBENCH_USE_QT5_QOPENGL_WIDGET +#include +#else #include +#endif #include "Matrix4x4.h" @@ -33,7 +44,11 @@ namespace caret { class BrainOpenGLShapeRing; class BrainOpenGLShapeSphere; +#ifdef WORKBENCH_USE_QT5_QOPENGL_WIDGET + class FiberSamplesOpenGLWidget : public QOpenGLWidget { +#else class FiberSamplesOpenGLWidget : public QGLWidget { +#endif Q_OBJECT public: diff --git a/src/GuiQt/FociPropertiesEditorDialog.cxx b/src/GuiQt/FociPropertiesEditorDialog.cxx index 4a453443445b0ec4fe76617699d10c9ad7b202fb..f3d71332e1a4f21045ceb1f8f8963fa3713a191d 100644 --- a/src/GuiQt/FociPropertiesEditorDialog.cxx +++ b/src/GuiQt/FociPropertiesEditorDialog.cxx @@ -203,6 +203,7 @@ FociPropertiesEditorDialog::FociPropertiesEditorDialog(const QString& title, SLOT(newFociFileButtonClicked())); QToolButton* newFileToolButton = new QToolButton(); newFileToolButton->setDefaultAction(newFileAction); + WuQtUtilities::setToolButtonStyleForQt5Mac(newFileToolButton); /* * Completer for name @@ -223,6 +224,7 @@ FociPropertiesEditorDialog::FociPropertiesEditorDialog(const QString& title, SLOT(displayNameEditor())); QToolButton* displayNameColorEditorToolButton = new QToolButton(); displayNameColorEditorToolButton->setDefaultAction(displayNameColorEditorAction); + WuQtUtilities::setToolButtonStyleForQt5Mac(displayNameColorEditorToolButton); /* * Class @@ -240,6 +242,7 @@ FociPropertiesEditorDialog::FociPropertiesEditorDialog(const QString& title, SLOT(displayClassEditor())); QToolButton* displayClassEditorToolButton = new QToolButton(); displayClassEditorToolButton->setDefaultAction(displayClassEditorAction); + WuQtUtilities::setToolButtonStyleForQt5Mac(displayClassEditorToolButton); /* * Coordinates @@ -649,9 +652,9 @@ FociPropertiesEditorDialog::loadFromDialogIntoFocusData(Focus* focus) const focus->setName(m_nameComboBox->getSelectedLabelName()); focus->setClassName(m_classComboBox->getSelectedLabelName()); const float xyz[3] = { - m_xCoordSpinBox->value(), - m_yCoordSpinBox->value(), - m_zCoordSpinBox->value() + (float)m_xCoordSpinBox->value(), + (float)m_yCoordSpinBox->value(), + (float)m_zCoordSpinBox->value() }; CaretAssert(focus->getNumberOfProjections() > 0); focus->getProjection(0)->setStereotaxicXYZ(xyz); diff --git a/src/GuiQt/GiftiLabelTableEditor.cxx b/src/GuiQt/GiftiLabelTableEditor.cxx index 8e4890fc78f06105e0fddf705f8da350d18ddb5c..9c8656d90a711cb292371b0bf51d987bb5712b8f 100644 --- a/src/GuiQt/GiftiLabelTableEditor.cxx +++ b/src/GuiQt/GiftiLabelTableEditor.cxx @@ -882,9 +882,7 @@ void GiftiLabelTableEditor::processApplyButton() { if (m_caretMappableDataFile != NULL) { - const PaletteFile* paletteFile = GuiManager::get()->getBrain()->getPaletteFile(); - m_caretMappableDataFile->updateScalarColoringForMap(m_caretMappableDataFileMapIndex, - paletteFile); + m_caretMappableDataFile->updateScalarColoringForMap(m_caretMappableDataFileMapIndex); } EventManager::get()->sendEvent(EventSurfaceColoringInvalidate().getPointer()); EventManager::get()->sendEvent(EventGraphicsUpdateAllWindows().getPointer()); diff --git a/src/GuiQt/GuiManager.cxx b/src/GuiQt/GuiManager.cxx index 6fa8fb34aaace7a0dd461ffd1e8ff021cf1f1f64..285a7fcb99f4e6bd7bbd0e0b81cf62e96b80486f 100644 --- a/src/GuiQt/GuiManager.cxx +++ b/src/GuiQt/GuiManager.cxx @@ -33,7 +33,9 @@ #include "GuiManager.h" #undef __GUI_MANAGER_DEFINE__ +#include "Annotation.h" #include "AnnotationFile.h" +#include "AnnotationManager.h" #include "Brain.h" #include "BrainBrowserWindow.h" #include "BrainOpenGL.h" @@ -43,7 +45,11 @@ #include "CaretAssert.h" #include "CaretLogger.h" #include "CaretMappableDataFile.h" +#include "ChartableTwoFileDelegate.h" +#include "ChartableTwoFileMatrixChart.h" #include "ChartingDataManager.h" +#include "ChartTwoOverlay.h" +#include "ChartTwoOverlaySet.h" #include "CiftiConnectivityMatrixDataFileManager.h" #include "CiftiFiberTrajectoryManager.h" #include "CiftiConnectivityMatrixParcelFile.h" @@ -56,8 +62,10 @@ #include "ElapsedTimer.h" #include "EventAlertUser.h" #include "EventAnnotationGetDrawnInWindow.h" +#include "EventBrowserTabGet.h" #include "EventBrowserTabGetAll.h" #include "EventBrowserWindowNew.h" +#include "EventShowDataFileReadWarningsDialog.h" #include "EventGraphicsUpdateAllWindows.h" #include "EventGraphicsUpdateOneWindow.h" #include "EventHelpViewerDisplay.h" @@ -84,6 +92,7 @@ #include "ImageFile.h" #include "ImageCaptureDialog.h" #include "InformationDisplayDialog.h" +#include "ModelChartTwo.h" #include "OverlaySettingsEditorDialog.h" #include "MacDockMenu.h" #include "MovieDialog.h" @@ -96,6 +105,7 @@ #include "SceneWindowGeometry.h" #include "SelectionManager.h" #include "SelectionItemChartMatrix.h" +#include "SelectionItemChartTwoMatrix.h" #include "SelectionItemCiftiConnectivityMatrixRowColumn.h" #include "SelectionItemSurfaceNode.h" #include "SelectionItemSurfaceNodeIdentificationSymbol.h" @@ -109,7 +119,6 @@ #include "TileTabsConfigurationDialog.h" #include "VolumeMappableInterface.h" #include "WuQMessageBox.h" -//#include "WuQWebView.h" #include "WuQtUtilities.h" #include "CaretAssert.h" @@ -152,8 +161,6 @@ GuiManager::GuiManager(QObject* parent) void GuiManager::initializeGuiManager() { - this->nameOfApplication = "Connectome Workbench"; - //this->brainOpenGL = NULL; this->allowBrowserWindowsToCloseWithoutConfirmation = false; m_bugReportDialog = NULL; @@ -168,12 +175,18 @@ GuiManager::initializeGuiManager() this->connectomeDatabaseWebView = NULL; m_helpViewerDialog = NULL; m_paletteColorMappingEditor = NULL; + m_chartTwoLineSeriesHistoryDialog = NULL; this->sceneDialog = NULL; m_surfacePropertiesEditorDialog = NULL; m_tileTabsConfigurationDialog = NULL; this->cursorManager = new CursorManager(); + /* + * Windows vector never changes size + */ + m_brainBrowserWindows.resize(BrainConstants::MAXIMUM_NUMBER_OF_BROWSER_WINDOWS, NULL); + /* * Information window. */ @@ -281,11 +294,14 @@ GuiManager::initializeGuiManager() EventManager::get()->addEventListener(this, EventTypeEnum::EVENT_ALERT_USER); EventManager::get()->addEventListener(this, EventTypeEnum::EVENT_ANNOTATION_GET_DRAWN_IN_WINDOW); EventManager::get()->addEventListener(this, EventTypeEnum::EVENT_BROWSER_WINDOW_NEW); + EventManager::get()->addEventListener(this, EventTypeEnum::EVENT_GRAPHICS_UPDATE_ALL_WINDOWS); + EventManager::get()->addEventListener(this, EventTypeEnum::EVENT_GRAPHICS_UPDATE_ONE_WINDOW); EventManager::get()->addEventListener(this, EventTypeEnum::EVENT_HELP_VIEWER_DISPLAY); EventManager::get()->addEventListener(this, EventTypeEnum::EVENT_MAC_DOCK_MENU_UPDATE); EventManager::get()->addEventListener(this, EventTypeEnum::EVENT_OVERLAY_SETTINGS_EDITOR_SHOW); EventManager::get()->addEventListener(this, EventTypeEnum::EVENT_OPERATING_SYSTEM_REQUEST_OPEN_DATA_FILE); EventManager::get()->addEventListener(this, EventTypeEnum::EVENT_PALETTE_COLOR_MAPPING_EDITOR_SHOW); + EventManager::get()->addEventListener(this, EventTypeEnum::EVENT_SHOW_FILE_DATA_READ_WARNING_DIALOG); EventManager::get()->addEventListener(this, EventTypeEnum::EVENT_UPDATE_INFORMATION_WINDOWS); EventManager::get()->addEventListener(this, EventTypeEnum::EVENT_USER_INTERFACE_UPDATE); } @@ -301,7 +317,7 @@ GuiManager::~GuiManager() if (this->connectomeDatabaseWebView != NULL) { CaretAssertMessage(0, "Need to uncomment out line below if webkit is renabled"); - //delete this->connectomeDatabaseWebView; + /* delete this->connectomeDatabaseWebView; */ } FociPropertiesEditorDialog::deleteStaticMembers(); @@ -372,22 +388,6 @@ GuiManager::getBrain() const return SessionManager::get()->getBrain(0); } -/** - * Get the Brain OpenGL for drawing with OpenGL. - * - * @return - * Point to the brain. - */ -//BrainOpenGL* -//GuiManager::getBrainOpenGL() -//{ -// if (this->brainOpenGL == NULL) { -// this->brainOpenGL = BrainOpenGL::getBrainOpenGL(); -// } -// -// return this->brainOpenGL; -//} - /** * See if a brain browser window can be closed. If there is only * one brain browser window, the user will be warned that any @@ -551,7 +551,8 @@ GuiManager::getActiveBrowserWindow() const BrainBrowserWindow* GuiManager::getBrowserWindowByWindowIndex(const int32_t browserWindowIndex) { - if (browserWindowIndex < static_cast(m_brainBrowserWindows.size())) { + if ((browserWindowIndex >= 0) + && (browserWindowIndex < static_cast(m_brainBrowserWindows.size()))) { return m_brainBrowserWindows[browserWindowIndex]; } return NULL; @@ -561,51 +562,106 @@ GuiManager::getBrowserWindowByWindowIndex(const int32_t browserWindowIndex) * Create a new BrainBrowser Window. * @param parent * Optional parent that is used only for window placement. + * @param useWindowIndex + * Create the new window in this index if this index is valid * @param browserTabContent * Optional tab for initial windwo tab. * @param createDefaultTabs * If true, create the default tabs in the new window. + * @param errorMessageOut + * Contains message if window could not be created. + * @return + * Pointer to new window or NULL if unable to create a window. */ BrainBrowserWindow* GuiManager::newBrainBrowserWindow(QWidget* parent, + const int32_t useWindowIndex, BrowserTabContent* browserTabContent, - const bool createDefaultTabs) + const bool createDefaultTabs, + AString& errorMessageOut) { + errorMessageOut.clear(); + /* * If no tabs can be created, do not create a new window. */ EventBrowserTabGetAll getAllTabs; EventManager::get()->sendEvent(getAllTabs.getPointer()); if (getAllTabs.getNumberOfBrowserTabs() == BrainConstants::MAXIMUM_NUMBER_OF_BROWSER_TABS) { + errorMessageOut = ("All browser tabs are being used. " + "Close some browser tabs and then try creating a new window."); return NULL; } + EventManager::get()->blockEvent(EventTypeEnum::EVENT_ANNOTATION_COLOR_BAR_GET, true); + int32_t windowIndex = -1; + /* + * Is window required to be at a specific index? + */ int32_t numWindows = static_cast(m_brainBrowserWindows.size()); - for (int32_t i = 0; i < numWindows; i++) { - if (m_brainBrowserWindows[i] == NULL) { - windowIndex = i; - break; + if (useWindowIndex >= 0) { + CaretAssertVectorIndex(m_brainBrowserWindows, useWindowIndex); + windowIndex = useWindowIndex; + } + else { + /* + * Use first available window + */ + for (int32_t i = 0; i < numWindows; i++) { + if (m_brainBrowserWindows[i] == NULL) { + windowIndex = i; + break; + } } } - BrainBrowserWindow* bbw = NULL; + if (windowIndex < 0) { + /* + * No windows are available + */ + errorMessageOut = ("Unable to create a new window as the maximum number of windows (" + + AString::number(BrainConstants::MAXIMUM_NUMBER_OF_BROWSER_WINDOWS) + + ") are currently open."); + return NULL; + } + + BrainBrowserWindow* bbw = NULL; BrainBrowserWindow::CreateDefaultTabsMode tabsMode = (createDefaultTabs ? BrainBrowserWindow::CREATE_DEFAULT_TABS_YES : BrainBrowserWindow::CREATE_DEFAULT_TABS_NO); - if (windowIndex < 0) { - windowIndex = m_brainBrowserWindows.size(); - bbw = new BrainBrowserWindow(windowIndex, browserTabContent, tabsMode); - m_brainBrowserWindows.push_back(bbw); + if (m_brainBrowserWindows[windowIndex] != NULL) { + bbw = m_brainBrowserWindows[windowIndex]; } else { bbw = new BrainBrowserWindow(windowIndex, browserTabContent, tabsMode); m_brainBrowserWindows[windowIndex] = bbw; } + /* + * Did OpenGL Context sharing fail? + */ + if ( ! bbw->isOpenGLContextSharingValid()) { + delete bbw; + errorMessageOut = ("There has been a failure when creating an OpenGL Widget " + "with a \"shared context\" for the new window. " + "This may be caused by a limitation of the OpenGL graphics or the " + "windowing system on your computer.\n" + "\n" + "Please restart wb_view and report this problem as a bug using " + "Help Menu --> Report a Workbench Bug...\n" + "\n" + "You will be able to continue using wb_view but you will not " + "be able to have more than one window open."); + if (windowIndex >= 0) { + m_brainBrowserWindows[windowIndex] = NULL; + } + return NULL; + } + if (parent != NULL) { WuQtUtilities::moveWindowToOffset(parent, bbw, 20, 20); } @@ -614,6 +670,8 @@ GuiManager::newBrainBrowserWindow(QWidget* parent, bbw->resetGraphicsWidgetMinimumSize(); + EventManager::get()->blockEvent(EventTypeEnum::EVENT_ANNOTATION_COLOR_BAR_GET, false); + return bbw; } @@ -652,7 +710,8 @@ GuiManager::testForModifiedFiles(const TestModifiedMode testModifiedMode, switch (testModifiedMode) { case TEST_FOR_MODIFIED_FILES_MODE_FOR_EXIT: break; - case TEST_FOR_MODIFIED_FILES_MODE_FOR_SCENE_ADD: + case TEST_FOR_MODIFIED_FILES_EXCLUDING_PALETTES_MODE_FOR_SCENE_ADD: + case TEST_FOR_MODIFIED_FILES_PALETTE_ONLY_MODE_FOR_SCENE_ADD: dataFileTypesToExclude.push_back(DataFileTypeEnum::SCENE); dataFileTypesToExclude.push_back(DataFileTypeEnum::SPECIFICATION); break; @@ -679,12 +738,10 @@ GuiManager::testForModifiedFiles(const TestModifiedMode testModifiedMode, switch (testModifiedMode) { case TEST_FOR_MODIFIED_FILES_MODE_FOR_EXIT: - modifiedDataFiles.push_back(file); - break; - case TEST_FOR_MODIFIED_FILES_MODE_FOR_SCENE_ADD: + case TEST_FOR_MODIFIED_FILES_MODE_FOR_SCENE_SHOW: { /* - * Is modification just the palette color mapping? + * Show both data modified and palette modified */ CaretMappableDataFile* mappableDataFile = dynamic_cast(file); bool paletteOnlyModFlag = false; @@ -701,11 +758,44 @@ GuiManager::testForModifiedFiles(const TestModifiedMode testModifiedMode, else { modifiedDataFiles.push_back(file); } - } break; - case TEST_FOR_MODIFIED_FILES_MODE_FOR_SCENE_SHOW: - modifiedDataFiles.push_back(file); + case TEST_FOR_MODIFIED_FILES_EXCLUDING_PALETTES_MODE_FOR_SCENE_ADD: + { + /* + * Is modification data but NOT palette + */ + CaretMappableDataFile* mappableDataFile = dynamic_cast(file); + if (mappableDataFile != NULL) { + if ( ! mappableDataFile->isModifiedPaletteColorMapping()) { + if (mappableDataFile->isModifiedExcludingPaletteColorMapping()) { + modifiedDataFiles.push_back(file); + } + } + } + else { + modifiedDataFiles.push_back(file); + } + break; + } + case TEST_FOR_MODIFIED_FILES_PALETTE_ONLY_MODE_FOR_SCENE_ADD: + { + /* + * Is modification just the palette color mapping? + */ + CaretMappableDataFile* mappableDataFile = dynamic_cast(file); + bool paletteOnlyModFlag = false; + if (mappableDataFile != NULL) { + if (mappableDataFile->isModifiedPaletteColorMapping()) { + if ( ! mappableDataFile->isModifiedExcludingPaletteColorMapping()) { + paletteOnlyModFlag = true; + } + } + } + if (paletteOnlyModFlag) { + paletteModifiedDataFiles.push_back(file); + } + } break; } } @@ -721,7 +811,8 @@ GuiManager::testForModifiedFiles(const TestModifiedMode testModifiedMode, switch (testModifiedMode) { case TEST_FOR_MODIFIED_FILES_MODE_FOR_EXIT: break; - case TEST_FOR_MODIFIED_FILES_MODE_FOR_SCENE_ADD: + case TEST_FOR_MODIFIED_FILES_EXCLUDING_PALETTES_MODE_FOR_SCENE_ADD: + case TEST_FOR_MODIFIED_FILES_PALETTE_ONLY_MODE_FOR_SCENE_ADD: /* * Do not need to notify about modified scene annotations * since scene annotations are saved to the scene @@ -734,52 +825,68 @@ GuiManager::testForModifiedFiles(const TestModifiedMode testModifiedMode, if ((modFileCount > 0) || sceneAnnotationsModifiedFlag - || paletteModFileCount) { + || paletteModFileCount > 0) { /* * Display dialog allowing user to save files (goes to Save/Manage * Files dialog), exit without saving, or cancel. */ + AString paletteModifiedMessage("These file(s) contain modified palette color mapping " + "(this may result from loading scenes that contained modified palette color " + "mapping and this modified status is needed if scenes " + "are added or replaced): "); switch (testModifiedMode) { case TEST_FOR_MODIFIED_FILES_MODE_FOR_EXIT: textMessageOut = "Do you want to save changes you made to these files?"; break; - case TEST_FOR_MODIFIED_FILES_MODE_FOR_SCENE_ADD: + case TEST_FOR_MODIFIED_FILES_EXCLUDING_PALETTES_MODE_FOR_SCENE_ADD: + case TEST_FOR_MODIFIED_FILES_PALETTE_ONLY_MODE_FOR_SCENE_ADD: textMessageOut = "Do you want to continue creating the scene?"; + paletteModifiedMessage = ("These file(s) contain modified palette color mapping " + "AND \"Add modified palette color mapping to scene\" " + "is NOT checked on the Create New Scene Dialog:"); break; case TEST_FOR_MODIFIED_FILES_MODE_FOR_SCENE_SHOW: textMessageOut = "Do you want to continue showing the scene?"; break; } - AString infoTextMsg; - if (modFileCount > 0) { - infoTextMsg.appendWithNewLine("Changes to these files will be lost if you don't save them:\n"); - for (std::vector::iterator iter = modifiedDataFiles.begin(); - iter != modifiedDataFiles.end(); - iter++) { - const CaretDataFile* cdf = *iter; - infoTextMsg.appendWithNewLine(" " - + cdf->getFileNameNoPath()); + AString infoTextMsg(""); + + if (sceneAnnotationsModifiedFlag + || (modFileCount > 0)) { + infoTextMsg.appendWithNewLine("These file(s) contain modified data:\n"); + infoTextMsg.appendWithNewLine("
      "); + + if (sceneAnnotationsModifiedFlag) { + infoTextMsg.appendWithNewLine("
    • Scene Annotations (must be saved to a scene)"); + } + + if (modFileCount > 0) { + for (std::vector::iterator iter = modifiedDataFiles.begin(); + iter != modifiedDataFiles.end(); + iter++) { + const CaretDataFile* cdf = *iter; + infoTextMsg.appendWithNewLine("
    • " + + cdf->getFileNameNoPath()); + } } - infoTextMsg.append("\n"); + + infoTextMsg.appendWithNewLine("
    "); } if (paletteModFileCount > 0) { - infoTextMsg.appendWithNewLine("These file(s) contain modified palette color mapping. It is not " - "necessary to save these file(s) if the save palette color mapping " - "option is selected on the scene creation dialog:\n"); + infoTextMsg.appendWithNewLine(paletteModifiedMessage); + infoTextMsg.appendWithNewLine("
      "); for (std::vector::iterator iter = paletteModifiedDataFiles.begin(); iter != paletteModifiedDataFiles.end(); iter++) { const CaretDataFile* cdf = *iter; - infoTextMsg.appendWithNewLine(" " + infoTextMsg.appendWithNewLine("
    • " + cdf->getFileNameNoPath()); } - infoTextMsg.append("\n"); + infoTextMsg.appendWithNewLine("
    "); } - if (sceneAnnotationsModifiedFlag) { - infoTextMsg.appendWithNewLine("Scene annotations are modified."); - } + infoTextMsg.appendWithNewLine(""); modifiedFilesMessageOut = infoTextMsg; return true; @@ -896,6 +1003,17 @@ GuiManager::exitProgram(BrainBrowserWindow* parent) } if (okToExit) { + /* + * While resetting the brain is not absolutely necessary, + * some files may use GraphicsOpenGLBufferObject that are + * created by BrainOpenGL. Resetting the brain will close + * all files which results in the GraphicsOpenGLBufferObject + * being released. If the brain is not reset, the files + * are deleted after BrainOpenGL so BrainOpenGL will report + * that GraphicsOpenGLBufferObjects were not deleted. + */ + get()->getBrain()->resetBrain(); + std::vector bws = this->getAllOpenBrainBrowserWindows(); for (int i = 0; i < static_cast(bws.size()); i++) { bws[i]->deleteLater(); @@ -1120,15 +1238,6 @@ void GuiManager::processTileWindows() CaretLogFine(windowInfo); } -/** - * @return Name of the application. - */ -QString -GuiManager::applicationName() const -{ - return this->nameOfApplication; -} - /** * Receive events from the event manager. * @@ -1188,11 +1297,14 @@ GuiManager::receiveEvent(Event* event) dynamic_cast(event); CaretAssert(eventNewBrowser); - BrainBrowserWindow* bbw = this->newBrainBrowserWindow(eventNewBrowser->getParent(), + AString errorMessage; + BrainBrowserWindow* bbw = this->newBrainBrowserWindow(eventNewBrowser->getParent(), + -1, /* allow any window index */ eventNewBrowser->getBrowserTabContent(), - true); + true, + errorMessage); if (bbw == NULL) { - eventNewBrowser->setErrorMessage("Workench is exhausted. It cannot create any more windows."); + eventNewBrowser->setErrorMessage(errorMessage); eventNewBrowser->setEventProcessed(); return; } @@ -1213,6 +1325,12 @@ GuiManager::receiveEvent(Event* event) EventManager::get()->sendEvent(EventMacDockMenuUpdate().getPointer()); } + else if ((event->getEventType() == EventTypeEnum::EVENT_GRAPHICS_UPDATE_ALL_WINDOWS) + || (event->getEventType() == EventTypeEnum::EVENT_GRAPHICS_UPDATE_ONE_WINDOW)) { + for (auto overlayEditor : m_overlaySettingsEditors) { + overlayEditor->updateChartLinesInDialog(); + } + } else if (event->getEventType() == EventTypeEnum::EVENT_MAC_DOCK_MENU_UPDATE) { EventMacDockMenuUpdate* macDockMenuEvent = dynamic_cast(event); CaretAssert(event); @@ -1245,6 +1363,7 @@ GuiManager::receiveEvent(Event* event) BrainBrowserWindow* browserWindow = m_brainBrowserWindows[browserWindowIndex]; CaretAssert(browserWindow); Overlay* overlay = mapEditEvent->getOverlay(); + ChartTwoOverlay* chartOverlay = mapEditEvent->getChartTwoOverlay(); switch (mode) { case EventOverlaySettingsEditorDialogRequest::MODE_OVERLAY_MAP_CHANGED: @@ -1253,7 +1372,8 @@ GuiManager::receiveEvent(Event* event) overlayEditorIter != m_overlaySettingsEditors.end(); overlayEditorIter++) { OverlaySettingsEditorDialog* med = *overlayEditorIter; - med->updateIfThisOverlayIsInDialog(overlay); + med->updateIfThisOverlayIsInDialog(overlay, + chartOverlay); } } break; @@ -1302,7 +1422,8 @@ GuiManager::receiveEvent(Event* event) } } - overlayEditor->updateDialogContent(overlay); + overlayEditor->updateDialogContent(overlay, + chartOverlay); overlayEditor->show(); overlayEditor->raise(); overlayEditor->activateWindow(); @@ -1391,6 +1512,21 @@ GuiManager::receiveEvent(Event* event) showHideHelpDialog(true, helpEvent->getBrainBrowserWindow()); m_helpViewerDialog->showHelpPageWithName(helpEvent->getHelpPageName()); + + helpEvent->setEventProcessed(); + } + else if (event->getEventType() == EventTypeEnum::EVENT_SHOW_FILE_DATA_READ_WARNING_DIALOG) { + EventShowDataFileReadWarningsDialog* warningEvent = dynamic_cast(event); + CaretAssert(warningEvent); + + BrainBrowserWindow* bbw = getBrowserWindowByWindowIndex(warningEvent->getBrowserWindowIndex()); + if (bbw == NULL) { + bbw = getActiveBrowserWindow(); + } + CaretAssert(bbw); + bbw->showDataFileReadWarningsDialog(); + + warningEvent->setEventProcessed(); } } @@ -1537,9 +1673,7 @@ GuiManager::processShowSurfacePropertiesEditorDialog(BrainBrowserWindow* browser m_surfacePropertiesEditorDialog->setSaveWindowPositionForNextTime(true); wasCreatedFlag = true; } - m_surfacePropertiesEditorDialog->setVisible(true); - m_surfacePropertiesEditorDialog->show(); - m_surfacePropertiesEditorDialog->activateWindow(); + m_surfacePropertiesEditorDialog->showDialog(); if (wasCreatedFlag) { WuQtUtilities::moveWindowToSideOfParent(browserWindow, @@ -1626,8 +1760,9 @@ GuiManager::showHideSceneDialog(const bool status, } } - this->sceneDialog->show(); - this->sceneDialog->activateWindow(); + this->sceneDialog->showDialog(); + + this->sceneDialog->createDefaultSceneFile(); } else { this->sceneDialog->close(); @@ -1705,9 +1840,7 @@ GuiManager::processShowBugReportDialog(BrainBrowserWindow* browserWindow, this->addNonModalDialog(m_bugReportDialog); } - m_bugReportDialog->setVisible(true); - m_bugReportDialog->show(); - m_bugReportDialog->activateWindow(); + m_bugReportDialog->showDialog(); } /** @@ -1731,52 +1864,36 @@ GuiManager::getHelpViewerDialogDisplayAction() */ void GuiManager::showHideHelpDialog(const bool status, - BrainBrowserWindow* parentBrainBrowserWindow) + const BrainBrowserWindow* parentBrainBrowserWindow) { - bool dialogWasCreated = false; - - QWidget* moveWindowParent = parentBrainBrowserWindow; + BrainBrowserWindow* helpDialogParentWindow = getActiveBrowserWindow(); + if (parentBrainBrowserWindow != NULL) { + for (auto bbw : m_brainBrowserWindows) { + if (bbw == parentBrainBrowserWindow) { + helpDialogParentWindow = bbw; + break; + } + } + } + CaretAssert(helpDialogParentWindow); if (status) { if (m_helpViewerDialog == NULL) { - BrainBrowserWindow* helpDialogParent = parentBrainBrowserWindow; - if (helpDialogParent == NULL) { - helpDialogParent = getActiveBrowserWindow(); - } - - m_helpViewerDialog = new HelpViewerDialog(helpDialogParent); + m_helpViewerDialog = new HelpViewerDialog(helpDialogParentWindow); this->addNonModalDialog(m_helpViewerDialog); QObject::connect(m_helpViewerDialog, SIGNAL(dialogWasClosed()), this, SLOT(helpDialogWasClosed())); - dialogWasCreated = true; - - /* - * If there was no parent dialog for placement of the help - * dialog and there is only one browser window, use the browser - * for placement of the help dialog. - */ - if (moveWindowParent == NULL) { - if (getAllOpenBrainBrowserWindows().size() == 1) { - moveWindowParent = helpDialogParent; - } - } + WuQtUtilities::moveWindowToSideOfParent(helpDialogParentWindow, + m_helpViewerDialog); } - m_helpViewerDialog->show(); - m_helpViewerDialog->activateWindow(); + m_helpViewerDialog->showDialog(); } else { m_helpViewerDialog->close(); } - if (dialogWasCreated) { - if (moveWindowParent != NULL) { - WuQtUtilities::moveWindowToSideOfParent(moveWindowParent, - m_helpViewerDialog); - } - } - m_helpViewerDialogDisplayAction->blockSignals(true); m_helpViewerDialogDisplayAction->setChecked(status); m_helpViewerDialogDisplayAction->blockSignals(false); @@ -1885,9 +2002,7 @@ GuiManager::processShowInformationDisplayDialog(const bool forceDisplayOfDialog) if (forceDisplayOfDialog || m_informationDisplayDialogEnabledAction->isChecked()) { if (m_informationDisplayDialog != NULL) { - m_informationDisplayDialog->setVisible(true); - m_informationDisplayDialog->show(); - m_informationDisplayDialog->activateWindow(); + m_informationDisplayDialog->showDialog(); } } } @@ -1950,8 +2065,7 @@ GuiManager::showHideIdentfyBrainordinateDialog(const bool status, } } - m_identifyBrainordinateDialog->show(); - m_identifyBrainordinateDialog->activateWindow(); + m_identifyBrainordinateDialog->showDialog(); } else { m_identifyBrainordinateDialog->close(); @@ -2023,9 +2137,7 @@ GuiManager::processShowClippingPlanesDialog(BrainBrowserWindow* browserWindow) const int32_t browserWindowIndex = browserWindow->getBrowserWindowIndex(); m_clippingPlanesDialog->updateContent(browserWindowIndex); - m_clippingPlanesDialog->setVisible(true); - m_clippingPlanesDialog->show(); - m_clippingPlanesDialog->activateWindow(); + m_clippingPlanesDialog->showDialog(); } @@ -2044,9 +2156,7 @@ GuiManager::processShowCustomViewDialog(BrainBrowserWindow* browserWindow) const int32_t browserWindowIndex = browserWindow->getBrowserWindowIndex(); m_customViewDialog->updateContent(browserWindowIndex); - m_customViewDialog->setVisible(true); - m_customViewDialog->show(); - m_customViewDialog->activateWindow(); + m_customViewDialog->showDialog(); } @@ -2064,9 +2174,7 @@ GuiManager::processShowTileTabsConfigurationDialog(caret::BrainBrowserWindow *br } m_tileTabsConfigurationDialog->updateDialogWithSelectedTileTabsFromWindow(browserWindow); - m_tileTabsConfigurationDialog->setVisible(true); - m_tileTabsConfigurationDialog->show(); - m_tileTabsConfigurationDialog->activateWindow(); + m_tileTabsConfigurationDialog->showDialog(); } /** @@ -2083,9 +2191,7 @@ GuiManager::processShowImageCaptureDialog(BrainBrowserWindow* browserWindow) } this->imageCaptureDialog->updateDialog(); this->imageCaptureDialog->setBrowserWindowIndex(browserWindow->getBrowserWindowIndex()); - this->imageCaptureDialog->setVisible(true); - this->imageCaptureDialog->show(); - this->imageCaptureDialog->activateWindow(); + this->imageCaptureDialog->showDialog(); } /** @@ -2101,10 +2207,7 @@ GuiManager::processShowGapsAndMarginsDialog(BrainBrowserWindow* browserWindow) this->addNonModalDialog(m_gapsAndMarginsDialog); } m_gapsAndMarginsDialog->updateDialog(); - //m_tabMarginsDialog->setBrowserWindowIndex(browserWindow->getBrowserWindowIndex()); - m_gapsAndMarginsDialog->setVisible(true); - m_gapsAndMarginsDialog->show(); - m_gapsAndMarginsDialog->activateWindow(); + m_gapsAndMarginsDialog->showDialog(); } /** @@ -2119,11 +2222,10 @@ GuiManager::processShowMovieDialog(BrainBrowserWindow* browserWindow) this->movieDialog = new MovieDialog(browserWindow); this->addNonModalDialog(this->movieDialog); } - //this->movieDialog->updateDialog(); - //this->movieDialog->setBrowserWindowIndex(browserWindow->getBrowserWindowIndex()); this->movieDialog->setVisible(true); this->movieDialog->show(); this->movieDialog->activateWindow(); + this->movieDialog->raise(); } @@ -2141,9 +2243,7 @@ GuiManager::processShowPreferencesDialog(BrainBrowserWindow* browserWindow) this->addNonModalDialog(this->preferencesDialog); } this->preferencesDialog->updateDialog(); - this->preferencesDialog->setVisible(true); - this->preferencesDialog->show(); - this->preferencesDialog->activateWindow(); + this->preferencesDialog->showDialog(); } /** @@ -2214,6 +2314,19 @@ GuiManager::saveToScene(const SceneAttributes* sceneAttributes, "GuiManager", 1); + /* + * BrowserWindowContent is saved and restored by SessionManager. + * Some of the data in BrowserWindowContent needs to be updated + * by BrainBrowserWindow which is not saved until AFTER SessionManager + * so direct each window to update its BrowserWindowContent so that + * BrowserWindowContent can be saved by the SessionManager. + */ + for (auto browserWindow : m_brainBrowserWindows) { + if (browserWindow != NULL) { + browserWindow->saveBrowserWindowContentForScene(); + } + } + /* * Save session manager (brain, etc) */ @@ -2387,22 +2500,52 @@ GuiManager::restoreFromScene(const SceneAttributes* sceneAttributes, EventManager::get()->sendEvent(progressEvent.getPointer()); const SceneClassArray* browserWindowArray = sceneClass->getClassArray("m_brainBrowserWindows"); if (browserWindowArray != NULL) { + bool windowWasRestoredFlag = false; const int32_t numBrowserClasses = browserWindowArray->getNumberOfArrayElements(); for (int32_t i = 0; i < numBrowserClasses; i++) { const SceneClass* browserClass = browserWindowArray->getClassAtIndex(i); + CaretAssert(browserClass); + const int32_t windowIndex = browserClass->getIntegerValue("m_browserWindowIndex", -1); + BrainBrowserWindow* bbw = NULL; - if (availableWindows.empty() == false) { + AString errorMessage; + if ( ! availableWindows.empty()) { bbw = availableWindows.front(); - availableWindows.pop_front(); + + if (windowIndex >= 0) { + /* + * Use window only if it has same index as window being restored. + */ + if (bbw->getBrowserWindowIndex() != windowIndex) { + bbw = NULL; + } + } + + if (bbw != NULL) { + availableWindows.pop_front(); + } } - else { + if (bbw == NULL) { bbw = newBrainBrowserWindow(NULL, + windowIndex, NULL, - false); + false, + errorMessage); } if (bbw != NULL) { bbw->restoreFromScene(sceneAttributes, browserClass); + windowWasRestoredFlag = true; + } + else { + sceneAttributes->addToErrorMessage("\n" + errorMessage); + } + } + + if (windowWasRestoredFlag) { + if ( ! availableWindows.empty()) { + BrainBrowserWindow* bbw = availableWindows.front(); + bbw->close(); } } } @@ -2583,9 +2726,9 @@ GuiManager::processIdentification(const int32_t tabIndex, double doubleXYZ[3]; idVoxel->getModelXYZ(doubleXYZ); const float voxelXYZ[3] = { - doubleXYZ[0], - doubleXYZ[1], - doubleXYZ[2] + (float)doubleXYZ[0], + (float)doubleXYZ[1], + (float)doubleXYZ[2] }; Surface* surface = brain->getPrimaryAnatomicalSurfaceNearestCoordinate(voxelXYZ, 3.0); @@ -2685,15 +2828,15 @@ GuiManager::processIdentification(const int32_t tabIndex, } } - SelectionItemChartMatrix* idChartMatrix = selectionManager->getChartMatrixIdentification(); - if (idChartMatrix->isValid()) { - ChartableMatrixInterface* chartMatrixInterface = idChartMatrix->getChartableMatrixInterface(); + SelectionItemChartMatrix* idChartOneMatrix = selectionManager->getChartMatrixIdentification(); + if (idChartOneMatrix->isValid()) { + ChartableMatrixInterface* chartMatrixInterface = idChartOneMatrix->getChartableMatrixInterface(); if (chartMatrixInterface != NULL) { CiftiConnectivityMatrixParcelFile* ciftiParcelFile = dynamic_cast(chartMatrixInterface); if (ciftiParcelFile != NULL) { if (ciftiParcelFile->isMapDataLoadingEnabled(0)) { - const int32_t rowIndex = idChartMatrix->getMatrixRowIndex(); - const int32_t columnIndex = idChartMatrix->getMatrixColumnIndex(); + const int32_t rowIndex = idChartOneMatrix->getMatrixRowIndex(); + const int32_t columnIndex = idChartOneMatrix->getMatrixColumnIndex(); if ((rowIndex >= 0) && (columnIndex >= 0)) { try { @@ -2716,7 +2859,7 @@ GuiManager::processIdentification(const int32_t tabIndex, CiftiScalarDataSeriesFile* scalarDataSeriesFile = dynamic_cast(chartMatrixInterface); if (scalarDataSeriesFile != NULL) { - const int32_t rowIndex = idChartMatrix->getMatrixRowIndex(); + const int32_t rowIndex = idChartOneMatrix->getMatrixRowIndex(); if (rowIndex >= 0) { scalarDataSeriesFile->setSelectedMapIndex(tabIndex, rowIndex); @@ -2749,8 +2892,7 @@ GuiManager::processIdentification(const int32_t tabIndex, const int32_t columnIndex = idCiftiConnMatrix->getMatrixColumnIndex(); if ((rowIndex >= 0) || (columnIndex >= 0)) { - ciftiConnectivityManager->loadRowOrColumnFromConnectivityMatrixFile(brain, - matrixFile, + ciftiConnectivityManager->loadRowOrColumnFromConnectivityMatrixFile(matrixFile, rowIndex, columnIndex, ciftiLoadingInfo); @@ -2760,6 +2902,167 @@ GuiManager::processIdentification(const int32_t tabIndex, } } + SelectionItemChartTwoMatrix* idChartTwoMatrix = selectionManager->getChartTwoMatrixIdentification(); + if (idChartTwoMatrix != NULL) { + if (idChartTwoMatrix->isValid()) { + ChartableTwoFileMatrixChart* matrixChart = idChartTwoMatrix->getFileMatrixChart(); + const int32_t rowIndex = idChartTwoMatrix->getRowIndex(); + const int32_t colIndex = idChartTwoMatrix->getColumnIndex(); + + CaretMappableDataFile* cmdf = matrixChart->getCaretMappableDataFile(); + CaretAssert(cmdf); + + bool loadMapFlag = false; + + ChartTwoOverlay* chartOverlayContainingDataFile = NULL; + + EventBrowserTabGet eventBrowserTab(tabIndex); + EventManager::get()->sendEvent(eventBrowserTab.getPointer()); + BrowserTabContent* tabContent = eventBrowserTab.getBrowserTab(); + if (tabContent != NULL) { + ModelChartTwo* chartTwoModel = tabContent->getDisplayedChartTwoModel(); + if (chartTwoModel != NULL) { + if (chartTwoModel->getSelectedChartTwoDataType(tabIndex) == ChartTwoDataTypeEnum::CHART_DATA_TYPE_MATRIX) { + ChartTwoOverlaySet* chartOverlaySet = tabContent->getChartTwoOverlaySet(); + chartOverlayContainingDataFile = chartOverlaySet->getDisplayedOverlayContainingDataFile(cmdf); + } + } + } + + bool foundChartOverlayFlag = false; + + if (chartOverlayContainingDataFile != NULL) { + ChartableTwoFileMatrixChart* matrixChart = cmdf->getChartingDelegate()->getMatrixCharting(); + if (matrixChart != NULL) { + if (matrixChart->isValid()) { + int32_t rowColumnIndex = -1; + switch (matrixChart->getSelectedRowColumnDimension()) { + case ChartTwoMatrixLoadingDimensionEnum::CHART_MATRIX_LOADING_BY_COLUMN: + rowColumnIndex = colIndex; + break; + case ChartTwoMatrixLoadingDimensionEnum::CHART_MATRIX_LOADING_BY_ROW: + rowColumnIndex = rowIndex; + break; + } + chartOverlayContainingDataFile->setSelectionData(cmdf, + rowColumnIndex); + const MapYokingGroupEnum::Enum mapYoking = chartOverlayContainingDataFile->getMapYokingGroup(); + if (mapYoking != MapYokingGroupEnum::MAP_YOKING_GROUP_OFF) { + EventMapYokingSelectMap selectMapEvent(mapYoking, + cmdf, + rowColumnIndex, + true); + EventManager::get()->sendEvent(selectMapEvent.getPointer()); + } + updateGraphicsFlag = true; + foundChartOverlayFlag = true; + chartingDataManager->loadChartForCiftiMappableFileRow(matrixChart->getCiftiMappableDataFile(), + rowIndex); + } + } + } + + if ( ! foundChartOverlayFlag) { + CaretLogWarning("PROGRAMMER NOTE: Failed to find chart overlay containing identified matrix row/column"); + + switch (matrixChart->getMatrixContentType()) { + case ChartTwoMatrixContentTypeEnum::MATRIX_CONTENT_BRAINORDINATE_MAPPABLE: + { + CiftiConnectivityMatrixParcelFile* ciftiParcelFile = dynamic_cast(cmdf); + if (ciftiParcelFile != NULL) { + if (ciftiParcelFile->isMapDataLoadingEnabled(0)) { + if ((rowIndex >= 0) + && (colIndex >= 0)) { + try { + ciftiConnectivityManager->loadRowOrColumnFromParcelFile(brain, + ciftiParcelFile, + rowIndex, + colIndex, + ciftiLoadingInfo); + + } + catch (const DataFileException& e) { + cursor.restoreCursor(); + QMessageBox::critical(parentWidget, "", e.whatString()); + cursor.showWaitCursor(); + } + updateGraphicsFlag = true; + } + } + } + else { + CiftiMappableConnectivityMatrixDataFile* matrixFile = dynamic_cast(cmdf); + if (matrixFile != NULL) { + if ((rowIndex >= 0) + || (colIndex >= 0)) { + ciftiConnectivityManager->loadRowOrColumnFromConnectivityMatrixFile(matrixFile, + rowIndex, + colIndex, + ciftiLoadingInfo); + updateGraphicsFlag = true; + + } + } + else { + loadMapFlag = true; + } + } + } + break; + case ChartTwoMatrixContentTypeEnum::MATRIX_CONTENT_SCALARS: + { + CiftiScalarDataSeriesFile* scalarDataSeriesFile = dynamic_cast(cmdf); + if (scalarDataSeriesFile != NULL) { + if (rowIndex >= 0) { + scalarDataSeriesFile->setSelectedMapIndex(tabIndex, + rowIndex); + + const MapYokingGroupEnum::Enum mapYoking = scalarDataSeriesFile->getMatrixRowColumnMapYokingGroup(tabIndex); + + if (mapYoking != MapYokingGroupEnum::MAP_YOKING_GROUP_OFF) { + EventMapYokingSelectMap selectMapEvent(mapYoking, + scalarDataSeriesFile, + rowIndex, + true); + EventManager::get()->sendEvent(selectMapEvent.getPointer()); + } + else { + chartingDataManager->loadChartForCiftiMappableFileRow(scalarDataSeriesFile, + rowIndex); + } + + updateGraphicsFlag = true; + } + } + } + break; + case ChartTwoMatrixContentTypeEnum::MATRIX_CONTENT_UNSUPPORTED: + break; + } + + if (loadMapFlag) { + ChartableTwoFileMatrixChart* matrixChart = cmdf->getChartingDelegate()->getMatrixCharting(); + if (matrixChart != NULL) { + if (matrixChart->isValid()) { + int32_t rowColumnIndex = -1; + switch (matrixChart->getSelectedRowColumnDimension()) { + case ChartTwoMatrixLoadingDimensionEnum::CHART_MATRIX_LOADING_BY_COLUMN: + rowColumnIndex = colIndex; + break; + case ChartTwoMatrixLoadingDimensionEnum::CHART_MATRIX_LOADING_BY_ROW: + rowColumnIndex = rowIndex; + break; + } + matrixChart->setSelectedRowColumnIndex(tabIndex, + rowColumnIndex); + updateGraphicsFlag = true; + } + } + } + } + } + } + /* * Generate identification manager */ @@ -2883,7 +3186,7 @@ GuiManager::processIdentification(const int32_t tabIndex, EventManager::get()->sendEvent(EventGraphicsUpdateAllWindows().getPointer()); EventManager::get()->sendEvent(EventUserInterfaceUpdate().addToolBar().addToolBox().getPointer()); } -} // tabIndex +} diff --git a/src/GuiQt/GuiManager.h b/src/GuiQt/GuiManager.h index a7d9a74dfab155c44834299bfcb248be7eaab058..9ffd35b2e53a6466f94d8a67aef16c6cd31236ac 100644 --- a/src/GuiQt/GuiManager.h +++ b/src/GuiQt/GuiManager.h @@ -45,6 +45,7 @@ namespace caret { class BrainBrowserWindow; class BrowserTabContent; class BugReportDialog; + class ChartTwoLineSeriesHistoryDialog; class ClippingPlanesDialog; class CursorManager; class CustomViewDialog; @@ -104,9 +105,6 @@ namespace caret { void processShowSaveManageFilesDialog(BrainBrowserWindow* browserWindow); - QString applicationName() const; - - //BrainOpenGL* getBrainOpenGL(); BrowserTabContent* getBrowserTabContentForBrowserWindow(const int32_t browserWindowIndex, const bool allowInvalidBrowserWindowIndex); @@ -174,8 +172,10 @@ namespace caret { enum TestModifiedMode { /** Testing when user is exiting Workbench */ TEST_FOR_MODIFIED_FILES_MODE_FOR_EXIT, - /** Testing when a scene is added */ - TEST_FOR_MODIFIED_FILES_MODE_FOR_SCENE_ADD, + /** Testing when a scene is added for modified but not palette modified */ + TEST_FOR_MODIFIED_FILES_EXCLUDING_PALETTES_MODE_FOR_SCENE_ADD, + /** Testing when a scene is added for modified palettes only */ + TEST_FOR_MODIFIED_FILES_PALETTE_ONLY_MODE_FOR_SCENE_ADD, /** Testing when a scene is shown */ TEST_FOR_MODIFIED_FILES_MODE_FOR_SCENE_SHOW }; @@ -214,8 +214,10 @@ namespace caret { void initializeGuiManager(); BrainBrowserWindow* newBrainBrowserWindow(QWidget* parent, + const int32_t useWindowIndex, BrowserTabContent* browserTabContent, - const bool createDefaultTabs); + const bool createDefaultTabs, + AString& errorMessageOut); void reparentNonModalDialogs(BrainBrowserWindow* closingBrainBrowserWindow); @@ -223,11 +225,7 @@ namespace caret { BrainBrowserWindow* parentBrainBrowserWindow); void showHideHelpDialog(const bool status, - BrainBrowserWindow* parentBrainBrowserWindow); - -// void processShowHelpViewerDialog(BrainBrowserWindow* browserWindow, -// const AString& helpPageName); - + const BrainBrowserWindow* parentBrainBrowserWindow); void addNonModalDialog(QWidget* dialog); @@ -236,28 +234,20 @@ namespace caret { /** One instance of the GuiManager */ static GuiManager* singletonGuiManager; - /** - * Contains pointers to Brain Browser windows - * As BrainBrowser windows are closed, some of - * the elements may be NULL. - */ + /** Contains pointers to Brain Browser windows */ std::vector m_brainBrowserWindows; - /** Name of application */ - QString nameOfApplication; - /** Skips confirmation of browser window closing when all tabs are moved to one window */ bool allowBrowserWindowsToCloseWithoutConfirmation; - /* Performs OpenGL drawing commands */ - //BrainOpenGL* brainOpenGL; - /* Editor for overlay settings. */ std::set m_overlaySettingsEditors; /** Editor for palette color mapping editing */ PaletteColorMappingEditorDialog* m_paletteColorMappingEditor; + ChartTwoLineSeriesHistoryDialog* m_chartTwoLineSeriesHistoryDialog; + TileTabsConfigurationDialog* m_tileTabsConfigurationDialog; ClippingPlanesDialog* m_clippingPlanesDialog; diff --git a/src/GuiQt/HelpViewerDialog.cxx b/src/GuiQt/HelpViewerDialog.cxx index 69e0f2eadf4722b0b383317af5f4b3f6e1f29f74..eb4df8bf7115f7676f966d8432b35cebb8d8e57f 100644 --- a/src/GuiQt/HelpViewerDialog.cxx +++ b/src/GuiQt/HelpViewerDialog.cxx @@ -32,8 +32,13 @@ #include #include #include -#include -#include +#if QT_VERSION >= 0x050000 +#include +#include +#else // QT_VERSION +#include +#include +#endif // QT_VERSION #include #include #include @@ -48,6 +53,7 @@ #include "CaretLogger.h" #include "CommandOperation.h" #include "CommandOperationManager.h" +#include "FileInformation.h" #include "WuQMessageBox.h" #include "WuQtUtilities.h" @@ -264,7 +270,6 @@ HelpViewerDialog::createHelpViewerWidget() connect(printButton, SIGNAL(clicked()), this, SLOT(helpPagePrintButtonClicked())); printButton->setText("Print"); - printButton->hide(); /** * Copy button @@ -326,15 +331,25 @@ HelpViewerDialog::createHelpViewerWidget() void HelpViewerDialog::showHelpPageWithName(const AString& helpPageName) { - CaretAssertMessage(0, "Not implmented yet."); const AString pageName = QString(helpPageName).replace('_', ' '); if (pageName.isEmpty()) { return; } - - CaretLogSevere("Could not find help page \"" - + helpPageName - + "\" for loading."); + + for (auto searchItem : m_allHelpSearchListWidgetItems) { + CaretAssert(searchItem); + HelpTreeWidgetItem* helpItem = searchItem->m_matchingTreeWidgetItem; + if (helpItem != NULL) { + if (helpItem->text(0) == pageName) { + displayHelpTextForHelpTreeWidgetItem(helpItem); + return; + } + } + } + + WuQMessageBox::errorOk(this, "Error: Help page \"" + + helpPageName + + "\" not found."); } /** @@ -400,10 +415,15 @@ HelpViewerDialog::loadWorkbenchHelpInfoFromDirectory(QTreeWidgetItem* parent, */ QListIterator otherHtmlPageIter(otherHtmlPagesList); while (otherHtmlPageIter.hasNext()) { - const QFileInfo pageInfo = otherHtmlPageIter.next(); +// const QFileInfo pageInfo = otherHtmlPageIter.next(); +// createHelpTreeWidgetItemForHelpPage(treeItem, +// pageInfo.baseName(), +// pageInfo.absoluteFilePath()); + const QFileInfo qtPageInfo = otherHtmlPageIter.next(); + const FileInformation pageInfo(qtPageInfo.absoluteFilePath()); createHelpTreeWidgetItemForHelpPage(treeItem, - pageInfo.baseName(), - pageInfo.absoluteFilePath()); + pageInfo.getFileNameNoExtension(), + qtPageInfo.absoluteFilePath()); } /* @@ -446,11 +466,9 @@ HelpViewerDialog::loadHelpTopicsIntoIndexTree() QDir resourceHelpDirectory(":/HelpFiles"); + QTreeWidgetItem* bestPracticesItem = NULL; QTreeWidgetItem* glossaryItem = NULL; - // CAN BE SET TO FIND FILES WITHOUT FULL PATH - //m_helpBrowser->setSearchPaths(QStringList(":/HelpFiles/Menus/File_Menu")); - QFileInfoList subDirList = resourceHelpDirectory.entryInfoList((QDir::AllDirs | QDir::NoDotAndDotDot), QDir::Name); QListIterator subDirIter(subDirList); @@ -460,6 +478,17 @@ HelpViewerDialog::loadHelpTopicsIntoIndexTree() HelpTreeWidgetItem* item = loadWorkbenchHelpInfoFromDirectory(workbenchItem, subDirInfo); + /* + * Is this the Best Practices? + * If so, move it so that it is a top level item. + */ + if (subDirInfo.baseName().toLower() == "best_practices_guides") { + if (bestPracticesItem != NULL) { + CaretAssertMessage(0, "There should be only one best practices subdirectory !!!!"); + } + bestPracticesItem = item; + workbenchItem->removeChild(bestPracticesItem); + } /* * Is this the GLOSSARY? * If so, move it so that it is a top level item. @@ -470,10 +499,15 @@ HelpViewerDialog::loadHelpTopicsIntoIndexTree() } glossaryItem = item; workbenchItem->removeChild(glossaryItem); - m_topicIndexTreeWidget->addTopLevelItem(glossaryItem); } } + if (bestPracticesItem != NULL) { + m_topicIndexTreeWidget->addTopLevelItem(bestPracticesItem); + } + if (glossaryItem != NULL) { + m_topicIndexTreeWidget->addTopLevelItem(glossaryItem); + } /* * Load commands diff --git a/src/GuiQt/IdentifyBrainordinateDialog.cxx b/src/GuiQt/IdentifyBrainordinateDialog.cxx index 93c377c1b5d03f579db267510937dd79e386c0b3..002d6abb8af0fe7960592eb9cc3b2fb7a6ee2336 100644 --- a/src/GuiQt/IdentifyBrainordinateDialog.cxx +++ b/src/GuiQt/IdentifyBrainordinateDialog.cxx @@ -327,16 +327,27 @@ IdentifyBrainordinateDialog::createCiftiRowWidget(const std::vectorgetBrain(), - supportedFileTypes); + m_ciftiRowFileSelectionModel = CaretDataFileSelectionModel::newInstanceForCaretDataFileTypes(supportedFileTypes); m_ciftiRowFileComboBox = new CaretDataFileSelectionComboBox(this); m_ciftiRowFileIndexLabel = new QLabel("Row Index"); - m_ciftiRowFileIndexSpinBox = WuQFactory::newSpinBoxWithMinMaxStep(0, + m_ciftiRowFileIndexSpinBox = WuQFactory::newSpinBoxWithMinMaxStep(CiftiMappableDataFile::getCiftiFileRowColumnIndexBaseForGUI(), std::numeric_limits::max(), 1); + QObject::connect(m_ciftiRowFileIndexSpinBox, SIGNAL(valueChanged(int)), + this, SLOT(apply())); m_ciftiRowFileIndexSpinBox->setFixedWidth(INDEX_SPIN_BOX_WIDTH); - m_ciftiRowFileIndexSpinBox->setToolTip("Row indices start at zero."); + switch (CiftiMappableDataFile::getCiftiFileRowColumnIndexBaseForGUI()) { + case 0: + m_ciftiRowFileIndexSpinBox->setToolTip("Row indices start at zero."); + break; + case 1: + m_ciftiRowFileIndexSpinBox->setToolTip("Row indices start at one."); + break; + default: + CaretAssert(0); + m_ciftiRowFileIndexSpinBox->setToolTip("PROGRAM ERROR CIFTI FILE ROW/COLUMN BASE INDEX SHOULD BE ONE OR ZERO."); + } QWidget* widget = new QWidget(); QGridLayout* ciftiRowLayout = new QGridLayout(widget); @@ -748,7 +759,7 @@ IdentifyBrainordinateDialog::processCiftiRowWidget(AString& errorMessageOut) CiftiMappableDataFile* ciftiMapFile = dynamic_cast(dataFile); CiftiFiberTrajectoryFile* ciftiTrajFile = dynamic_cast(dataFile); - const int32_t selectedCiftiRowIndex = m_ciftiRowFileIndexSpinBox->value(); + const int32_t selectedCiftiRowIndex = m_ciftiRowFileIndexSpinBox->value() - m_ciftiRowFileIndexSpinBox->minimum(); try { StructureEnum::Enum surfaceStructure; diff --git a/src/GuiQt/ImageCaptureDialog.cxx b/src/GuiQt/ImageCaptureDialog.cxx index c7522121b177188f21792a9c6957d86d5ef4ae2c..ac3a43aaf78a3f01b4d55c84f43c5ee72962311e 100644 --- a/src/GuiQt/ImageCaptureDialog.cxx +++ b/src/GuiQt/ImageCaptureDialog.cxx @@ -134,16 +134,6 @@ ImageCaptureDialog::ImageCaptureDialog(BrainBrowserWindow* parent) EventManager::get()->addEventListener(this, EventTypeEnum::EVENT_BROWSER_WINDOW_GRAPHICS_HAVE_BEEN_REDRAWN); updateBrowserWindowWidthAndHeightLabel(); - /* - * Initialize the custom image size - */ -// m_imageDimensionsModel->setPixelWidthAndHeight(512, -// 512); -// updateDialogWithImageDimensionsModel(); - -// m_scaleProportionallyCheckBox->setChecked(true); -// scaleProportionallyCheckBoxClicked(m_scaleProportionallyCheckBox->isChecked()); - setSizePolicy(QSizePolicy::Fixed, QSizePolicy::Fixed); @@ -186,8 +176,6 @@ ImageCaptureDialog::createImageSourceSection() QGroupBox* groupBox = new QGroupBox("Source"); QGridLayout* gridLayout = new QGridLayout(groupBox); -// gridLayout->setColumnStretch(0, 0); -// gridLayout->setColumnStretch(1, 0); gridLayout->addWidget(windowLabel, 0, 0); gridLayout->addWidget(m_windowSelectionSpinBox, 0, 1, Qt::AlignLeft); gridLayout->addWidget(m_windowCropToLockAspectRegionCheckBox, 1, 0, 1, 2, Qt::AlignLeft); @@ -391,9 +379,6 @@ ImageCaptureDialog::createImageDimensionsSection() layout->addWidget(m_customDimensionsWidget, 0, Qt::AlignLeft); layout->addWidget(WuQtUtilities::createHorizontalLineWidget()); layout->addWidget(imageBytesWidget); -// -// m_imageSizeWindowRadioButton->setChecked(true); -// sizeRadioButtonClicked(sizeButtonGroup->checkedButton()); return groupBox; } @@ -416,8 +401,6 @@ ImageCaptureDialog::sizeRadioButtonClicked(QAbstractButton* /*button*/) } updateDimensionsSection(); -// updateDialogWithImageDimensionsModel(); -// updateImageNumberOfBytesLabel(); } /** @@ -583,32 +566,6 @@ ImageCaptureDialog::updateDestinationSection() m_imageFileNameLineEdit->setText(imageCaptureSettings->getImageFileName()); } - -///** -// * Update the dialog with data from the image dimensions model. -// */ -//void -//ImageCaptureDialog::updateDialogWithImageDimensionsModel() -//{ -// ImageCaptureSettings* imageCaptureSettings = SessionManager::get()->getImageCaptureDialogSettings(); -// const ImageCaptureDimensionsModeEnum::Enum captureMode = imageCaptureSettings->getImageCaptureDimensionsMode(); -// switch (captureMode) { -// case ImageCaptureDimensionsModeEnum::IMAGE_CAPTURE_DIMENSIONS_MODE_CUSTOM: -// m_imageSizeCustomRadioButton->setChecked(true); -// m_customDimensionsWidget->setEnabled(true); -// break; -// case ImageCaptureDimensionsModeEnum::IMAGE_CAPTURE_DIMENSIONS_MODE_WINDOW_SIZE: -// m_imageSizeWindowRadioButton->setChecked(true); -// m_customDimensionsWidget->setEnabled(false); -// break; -// } -// -// m_scaleProportionallyCheckBox->setChecked(imageCaptureSettings->isScaleProportionately()); -// -// -// updateImageNumberOfBytesLabel(); -//} - /** * Update the image number of bytes label. */ @@ -981,22 +938,6 @@ ImageCaptureDialog::getSelectedWindowWidthAndHeight(int32_t& widthOut, graphicsWidth, graphicsHeight, aspectRatioOut); - -// const int selectedBrowserWindowIndex = m_windowSelectionSpinBox->value() - 1; -// BrainBrowserWindow* browserWindow = GuiManager::get()->getBrowserWindowByWindowIndex(selectedBrowserWindowIndex); -// -// if (browserWindow != NULL) { -// browserWindow->getGraphicsWidgetSize(widthOut, -// heightOut, -// m_windowCropToLockAspectRegionCheckBox->isChecked()); -// if ((widthOut > 0) -// && (heightOut > 0)) { -// aspectRatioOut = (static_cast(heightOut) -// / static_cast(widthOut)); -// return true; -// } -// } -// return false; } @@ -1205,9 +1146,9 @@ ImageCaptureDialog::applyButtonClicked() QWidget* parent = getDialogButtonBox()->button(QDialogButtonBox::Apply); CaretAssert(parent); - WuQTimedMessageDisplay::show(parent, - 2.0, - "Image captured"); + WuQTimedMessageDisplay::showNonModal(parent, + 3.0, + "Image captured"); } EventManager::get()->sendEvent(EventGraphicsUpdateOneWindow(browserWindowIndex).getPointer()); diff --git a/src/GuiQt/ImageCaptureDialog.h b/src/GuiQt/ImageCaptureDialog.h index c905011d63ac704ca1942f4dfaef321f01392be0..66716c08ae23a25f883f6f3016abd1cd2d1e5b7d 100644 --- a/src/GuiQt/ImageCaptureDialog.h +++ b/src/GuiQt/ImageCaptureDialog.h @@ -117,8 +117,6 @@ namespace caret { int32_t& heightOut, float& aspectRatioOut) const; - //void updateDialogWithImageDimensionsModel(); - void updateImageNumberOfBytesLabel(); QCheckBox* m_saveImageToFileCheckBox; diff --git a/src/GuiQt/ImageFileConvertToVolumeFileDialog.cxx b/src/GuiQt/ImageFileConvertToVolumeFileDialog.cxx index c14df35ef564890e8ba2363e5d34b76ef004f2fa..9bd03cede52b91decbad1d974c480615885c7e27 100644 --- a/src/GuiQt/ImageFileConvertToVolumeFileDialog.cxx +++ b/src/GuiQt/ImageFileConvertToVolumeFileDialog.cxx @@ -286,8 +286,6 @@ ImageFileConvertToVolumeFileDialog::createControlPointWidget() void ImageFileConvertToVolumeFileDialog::okButtonClicked() { - Brain* brain = GuiManager::get()->getBrain(); - EventBrowserTabGet tabEvent(m_tabIndex); EventManager::get()->sendEvent(tabEvent.getPointer()); BrowserTabContent* tabContent = tabEvent.getBrowserTab(); @@ -329,7 +327,6 @@ ImageFileConvertToVolumeFileDialog::okButtonClicked() const ImageFile::CONVERT_TO_VOLUME_COLOR_MODE colorMode = static_cast(colorConversionIndex); AString errorMessage; VolumeFile* volumeFile = m_imageFile->convertToVolumeFile(colorMode, - brain->getPaletteFile(), errorMessage); if (volumeFile == NULL) { diff --git a/src/GuiQt/InformationDisplayDialog.cxx b/src/GuiQt/InformationDisplayDialog.cxx index 2cd3b5d44533224f9cadf1097e13f2ed570cc25a..0855fbd358bfe902dde8ce87d54ee5f1f7932162 100644 --- a/src/GuiQt/InformationDisplayDialog.cxx +++ b/src/GuiQt/InformationDisplayDialog.cxx @@ -177,6 +177,10 @@ InformationDisplayDialog::updateDialog() const AString text = idManager->getIdentificationText(); m_informationTextBrowser->setContentToHtml(text); + if (m_propertiesDialog != NULL) { + m_propertiesDialog->updateDialog(); + } + m_contralateralIdentificationAction->blockSignals(true); m_contralateralIdentificationAction->setChecked(idManager->isContralateralIdentificationEnabled()); m_contralateralIdentificationAction->blockSignals(false); @@ -318,6 +322,6 @@ InformationDisplayDialog::showPropertiesDialog() if (m_propertiesDialog == NULL) { m_propertiesDialog = new InformationDisplayPropertiesDialog(this); } - m_propertiesDialog->show(); + m_propertiesDialog->showDialog(); } diff --git a/src/GuiQt/InformationDisplayPropertiesDialog.cxx b/src/GuiQt/InformationDisplayPropertiesDialog.cxx index b2e72c6b92852e8bede359f5a1e67875d903104e..55696e7970d5cee45f45a35a09442ace5633ea67 100644 --- a/src/GuiQt/InformationDisplayPropertiesDialog.cxx +++ b/src/GuiQt/InformationDisplayPropertiesDialog.cxx @@ -35,6 +35,7 @@ #include "GuiManager.h" #include "IdentificationManager.h" #include "WuQFactory.h" +#include "WuQTrueFalseComboBox.h" #include "WuQtUtilities.h" using namespace caret; @@ -58,6 +59,16 @@ InformationDisplayPropertiesDialog::InformationDisplayPropertiesDialog(QWidget* { const int WIDGET_WIDTH = 100; + QLabel* surfaceLabel = new QLabel("Show Surface ID Symbols"); + m_surfaceIdentificationSymbolComboBox = new WuQTrueFalseComboBox("On", "Off", this); + QObject::connect(m_surfaceIdentificationSymbolComboBox, SIGNAL(statusChanged(bool)), + this, SLOT(informationPropertyChanged())); + + QLabel* volumeLabel = new QLabel("Show Volume ID Symbols"); + m_volumeIdentificationSymbolComboBox = new WuQTrueFalseComboBox("On", "Off", this); + QObject::connect(m_volumeIdentificationSymbolComboBox, SIGNAL(statusChanged(bool)), + this, SLOT(informationPropertyChanged())); + QLabel* idColorLabel = new QLabel("ID Symbol Color: "); m_idColorComboBox = new CaretColorEnumComboBox(this); m_idColorComboBox->getWidget()->setFixedWidth(WIDGET_WIDTH); @@ -94,14 +105,19 @@ InformationDisplayPropertiesDialog::InformationDisplayPropertiesDialog(QWidget* QObject::connect(m_mostRecentSymbolSizeSpinBox, SIGNAL(valueChanged(double)), this, SLOT(informationPropertyChanged())); - QLabel* prefInfoLabel = new QLabel("Display of ID symbols is enabled/disabled on the Preference Dialog"); - prefInfoLabel->setWordWrap(true); - const int COLUMN_LABEL = 0; const int COLUMN_WIDGET = 1; QWidget* widget = new QWidget(); QGridLayout* gridLayout = new QGridLayout(widget); int row = 0; + gridLayout->addWidget(surfaceLabel, row, COLUMN_LABEL); + gridLayout->addWidget(m_surfaceIdentificationSymbolComboBox->getWidget(), row, COLUMN_WIDGET); + row++; + gridLayout->addWidget(volumeLabel, row, COLUMN_LABEL); + gridLayout->addWidget(m_volumeIdentificationSymbolComboBox->getWidget(), row, COLUMN_WIDGET); + row++; + + gridLayout->addWidget(idColorLabel, row, COLUMN_LABEL); gridLayout->addWidget(m_idColorComboBox->getWidget(), row, COLUMN_WIDGET); row++; @@ -116,8 +132,6 @@ InformationDisplayPropertiesDialog::InformationDisplayPropertiesDialog(QWidget* row++; gridLayout->addWidget(WuQtUtilities::createHorizontalLineWidget(), row, COLUMN_LABEL, 1, 2); row++; - gridLayout->addWidget(prefInfoLabel, row, COLUMN_LABEL, 1, 2); - row++; setCentralWidget(widget, WuQDialog::SCROLL_AREA_NEVER); @@ -146,7 +160,9 @@ InformationDisplayPropertiesDialog::updateDialog() { Brain* brain = GuiManager::get()->getBrain(); IdentificationManager* info = brain->getIdentificationManager(); - + + m_surfaceIdentificationSymbolComboBox->setStatus(info->isShowSurfaceIdentificationSymbols()); + m_volumeIdentificationSymbolComboBox->setStatus(info->isShowVolumeIdentificationSymbols()); m_idColorComboBox->setSelectedColor(info->getIdentificationSymbolColor()); m_idContralateralColorComboBox->setSelectedColor(info->getIdentificationContralateralSymbolColor()); m_symbolSizeSpinBox->blockSignals(true); @@ -166,6 +182,8 @@ InformationDisplayPropertiesDialog::informationPropertyChanged() Brain* brain = GuiManager::get()->getBrain(); IdentificationManager* info = brain->getIdentificationManager(); + info->setShowSurfaceIdentificationSymbols(m_surfaceIdentificationSymbolComboBox->isTrue()); + info->setShowVolumeIdentificationSymbols(m_volumeIdentificationSymbolComboBox->isTrue()); info->setIdentificationSymbolColor(m_idColorComboBox->getSelectedColor()); info->setIdentificationContralateralSymbolColor(m_idContralateralColorComboBox->getSelectedColor()); info->setIdentificationSymbolSize(m_symbolSizeSpinBox->value()); diff --git a/src/GuiQt/InformationDisplayPropertiesDialog.h b/src/GuiQt/InformationDisplayPropertiesDialog.h index cb72e91cca8dd5203f0e2f64cb5effd2cfd91dd6..65cbaaf269770a0bbd6fb1c9eaf4889ba501b9a4 100644 --- a/src/GuiQt/InformationDisplayPropertiesDialog.h +++ b/src/GuiQt/InformationDisplayPropertiesDialog.h @@ -29,6 +29,7 @@ namespace caret { class CaretColorEnumComboBox; + class WuQTrueFalseComboBox; class InformationDisplayPropertiesDialog : public WuQDialogNonModal { @@ -60,6 +61,10 @@ namespace caret { QDoubleSpinBox* m_mostRecentSymbolSizeSpinBox; + WuQTrueFalseComboBox* m_surfaceIdentificationSymbolComboBox; + + WuQTrueFalseComboBox* m_volumeIdentificationSymbolComboBox; + // ADD_NEW_MEMBERS_HERE }; diff --git a/src/GuiQt/LockAspectWarningDialog.cxx b/src/GuiQt/LockAspectWarningDialog.cxx new file mode 100644 index 0000000000000000000000000000000000000000..e3fbb1a152a29479db789e18cdf05f1b73a815af --- /dev/null +++ b/src/GuiQt/LockAspectWarningDialog.cxx @@ -0,0 +1,258 @@ + +/*LICENSE_START*/ +/* + * Copyright (C) 2018 Washington University School of Medicine + * + * 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 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ +/*LICENSE_END*/ + +#define __LOCK_ASPECT_WARNING_DIALOG_DECLARE__ +#include "LockAspectWarningDialog.h" +#undef __LOCK_ASPECT_WARNING_DIALOG_DECLARE__ + +#include + +#include +#include +#include +#include +#include +#include +#include + +#include "BrainBrowserWindow.h" +#include "BrowserTabContent.h" +#include "BrowserWindowContent.h" +#include "CaretAssert.h" +#include "EventHelpViewerDisplay.h" +#include "EventManager.h" +#include "GuiManager.h" +#include "WuQtUtilities.h" + +using namespace caret; + + + +/** + * \class caret::LockAspectWarningDialog + * \brief Dialog that warns and allows user to lock tab/window aspect. + * \ingroup GuiQt + */ + +/** + * Run the lock aspect warning dialog and return the result. + * + * @param browserWindowIndex + * Index of the browser window. + * @return + * Result of user interaction with dialog. + */ +LockAspectWarningDialog::Result +LockAspectWarningDialog::runDialog(const int32_t browserWindowIndex) +{ + BrainBrowserWindow* bbw = GuiManager::get()->getBrowserWindowByWindowIndex(browserWindowIndex); + CaretAssert(bbw); + const BrowserWindowContent* browserWindowContent = bbw->getBrowerWindowContent(); + CaretAssert(browserWindowContent); + + const bool aspectLockedFlag = (browserWindowContent->isWindowAspectLocked() + && browserWindowContent->isAllTabsInWindowAspectRatioLocked()); + if (aspectLockedFlag) { + return Result::NO_CHANGES; + } + + const BrowserTabContent* selectedTab = bbw->getBrowserTabContent(); + if (selectedTab == NULL) { + return Result::NO_CHANGES; + } + + if (s_doNotShowAgainStatusFlag) { + return Result::NO_CHANGES; + } + + LockAspectWarningDialog dialog(bbw); + + if (dialog.exec() == LockAspectWarningDialog::Accepted) { + s_doNotShowAgainStatusFlag = dialog.isDoNotShowAgainChecked(); + return dialog.getOkResult(); + } + + return Result::CANCEL; +} + +/** + * Constructor. + * + * @param tabMode + * The mode for tabs (selected or all) + * @param tileTabsEnabled + * True if tile tabs is enabled. + * @param browserWindowAspectLocked + * True if window aspect is locked. + * @param tabAspectLockedCount + * Count of tabs with aspect locked. + * @param tabCount + * Count of tabs. + * @param parent + * The parent widget. + */ +LockAspectWarningDialog::LockAspectWarningDialog(BrainBrowserWindow* brainBrowserWindow) +: WuQDialogModal("Enter Annotations Mode", + brainBrowserWindow), +m_brainBrowserWindow(brainBrowserWindow) +{ + const QString mainInstructions("Do you want to lock the aspect ratio while entering annotations mode?"); + + const QString supplementalInstructions("" + "Prior to locking the aspect ratio, the user should adjust the size " + "of the window and optionally enable Tile Tabs for a multi-tab view. " + "If this has not been done, click the Cancel button, make those " + "adjustments, and then click the Toolbar's Annotate Mode button." + "

    " + "View the Best Practices Guide (this dialog " + "will close). This guide explains the importance of aspect " + "locking and documents procedures for successful creation of annotations " + "and scenes." + ""); + + const QString lockAspectInstructions("Locking the aspect ratio, and never unlocking the aspect " + "ratio, ensures annotations stay in the correct location."); + const QString leaveUnlockedInstructions("Advanced users may choose to lock and unlock the aspect ratio"); + + QLabel* mainInstructionsLabel = new QLabel(mainInstructions); + QFont font = mainInstructionsLabel->font(); + font.setPointSize(font.pointSize() * 1.4); + font.setBold(true); + mainInstructionsLabel->setFont(font); + + QLabel* supplementalInstructionLabel = new QLabel(supplementalInstructions); + supplementalInstructionLabel->setWordWrap(true); + QObject::connect(supplementalInstructionLabel, &QLabel::linkActivated, + this, &LockAspectWarningDialog::detailsLabelLinkActivated); + + QLabel* lockAspectLabel = new QLabel(lockAspectInstructions); + lockAspectLabel->setWordWrap(true); + QLabel* lockAspectRadioButtonLabel = new QLabel("Lock Aspect Ratio (Recommended)"); + m_lockAspectRadioButton = new QRadioButton(); + + QLabel* leaveUnlockedAspectRadioButtonLabel = new QLabel("Leave Aspect Ratio Unlocked"); + m_leaveUnlockedAspectRadioButton = new QRadioButton(); + QLabel* leaveUnlockedLabel = new QLabel(leaveUnlockedInstructions); + + QButtonGroup* buttGroup = new QButtonGroup(this); + buttGroup->addButton(m_lockAspectRadioButton); + buttGroup->addButton(m_leaveUnlockedAspectRadioButton); + m_lockAspectRadioButton->setChecked(true); + + m_doNotShowAgainCheckBox = new QCheckBox("Do not show again. User will not be warned about " + "aspect locking and unlocking."); + + /* + * No text is added to readio buttons since + * radio buttons and labels seem to get a different + * height in a grid layout. + */ + const int COL_RADIO_EMPTY = 0; + const int COL_RADIO_BUTTON = 1; + const int COL_RADIO_LABEL = 2; + const int COL_RADIO_INFO = 3; + const int COL_RADIO_STRETCH = 4; + QGridLayout* buttonGridLayout = new QGridLayout(); + buttonGridLayout->setVerticalSpacing(4); + buttonGridLayout->setColumnMinimumWidth(COL_RADIO_EMPTY, 20); + buttonGridLayout->setColumnMinimumWidth(COL_RADIO_LABEL, 20); + buttonGridLayout->setColumnStretch(COL_RADIO_EMPTY, 0); + buttonGridLayout->setColumnStretch(COL_RADIO_BUTTON, 0); + buttonGridLayout->setColumnStretch(COL_RADIO_STRETCH, 100); + int row = 0; + buttonGridLayout->addWidget(m_lockAspectRadioButton, row, COL_RADIO_BUTTON); + buttonGridLayout->addWidget(lockAspectRadioButtonLabel, row, COL_RADIO_LABEL, 1, 2); + row++; + buttonGridLayout->addWidget(lockAspectLabel, row, COL_RADIO_INFO); + row++; + buttonGridLayout->addWidget(m_leaveUnlockedAspectRadioButton, row, COL_RADIO_BUTTON); + buttonGridLayout->addWidget(leaveUnlockedAspectRadioButtonLabel, row, COL_RADIO_LABEL, 1, 2); + row++; + buttonGridLayout->addWidget(leaveUnlockedLabel, row, COL_RADIO_INFO); + + QWidget* dialogWidget = new QWidget; + QVBoxLayout* dialogLayout = new QVBoxLayout(dialogWidget); + dialogLayout->addWidget(mainInstructionsLabel); + dialogLayout->addLayout(buttonGridLayout); + dialogLayout->addSpacing(10); + dialogLayout->addWidget(supplementalInstructionLabel); + dialogLayout->addSpacing(10); + dialogLayout->addWidget(m_doNotShowAgainCheckBox); + + setCentralWidget(dialogWidget, + WuQDialogModal::SCROLL_AREA_NEVER); + + setSizePolicy(QSizePolicy::Fixed, QSizePolicy::Fixed); + +} + +/** + * Destructor. + */ +LockAspectWarningDialog::~LockAspectWarningDialog() +{ +} + +/** + * @return The result if the user clicked the OK button. + */ +LockAspectWarningDialog::Result +LockAspectWarningDialog::getOkResult() const +{ + if (m_lockAspectRadioButton->isChecked()) { + return Result::LOCK_ASPECT; + } + else if (m_leaveUnlockedAspectRadioButton->isChecked()) { + return Result::NO_CHANGES; + } + else { + CaretAssert(0); + } + + return m_result; +} + +/** + * Called when link in details label is clicked + * + * @param text of the link. + */ +void +LockAspectWarningDialog::detailsLabelLinkActivated(const QString& /*link*/) +{ + EventHelpViewerDisplay helpViewerEvent(m_brainBrowserWindow, + "Annotation_and_Scenes_Best_Practices"); + EventManager::get()->sendEvent(helpViewerEvent.getPointer()); + + reject(); +} + + +/** + * @return True if the "do not show again" checkbox is checked. + */ +bool +LockAspectWarningDialog::isDoNotShowAgainChecked() const +{ + return m_doNotShowAgainCheckBox->isChecked(); +} + + diff --git a/src/GuiQt/LockAspectWarningDialog.h b/src/GuiQt/LockAspectWarningDialog.h new file mode 100644 index 0000000000000000000000000000000000000000..85f8c398c31cff6259ecc0ae853be8938184c971 --- /dev/null +++ b/src/GuiQt/LockAspectWarningDialog.h @@ -0,0 +1,89 @@ +#ifndef __LOCK_ASPECT_WARNING_DIALOG_H__ +#define __LOCK_ASPECT_WARNING_DIALOG_H__ + +/*LICENSE_START*/ +/* + * Copyright (C) 2018 Washington University School of Medicine + * + * 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 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ +/*LICENSE_END*/ + + + +#include + +#include "WuQDialogModal.h" + +class QCheckBox; +class QRadioButton; + +namespace caret { + + class BrainBrowserWindow; + + class LockAspectWarningDialog : public WuQDialogModal { + + Q_OBJECT + + public: + enum class Result { + LOCK_ASPECT, + NO_CHANGES, + CANCEL + }; + + static Result runDialog(const int32_t browserWindowIndex); + + LockAspectWarningDialog(BrainBrowserWindow* brainBrowserWindow); + + virtual ~LockAspectWarningDialog(); + + Result getOkResult() const; + + bool isDoNotShowAgainChecked() const; + + // ADD_NEW_METHODS_HERE + + private slots: + void detailsLabelLinkActivated(const QString& link); + + private: + LockAspectWarningDialog(const LockAspectWarningDialog&); + + LockAspectWarningDialog& operator=(const LockAspectWarningDialog&); + + BrainBrowserWindow* m_brainBrowserWindow; + + Result m_result = Result::CANCEL; + + QCheckBox* m_doNotShowAgainCheckBox; + + QRadioButton* m_lockAspectRadioButton; + + QRadioButton* m_leaveUnlockedAspectRadioButton; + + static bool s_doNotShowAgainStatusFlag; + + // ADD_NEW_MEMBERS_HERE + + }; + +#ifdef __LOCK_ASPECT_WARNING_DIALOG_DECLARE__ + bool LockAspectWarningDialog::s_doNotShowAgainStatusFlag = false; +#endif // __LOCK_ASPECT_WARNING_DIALOG_DECLARE__ + +} // namespace +#endif //__LOCK_ASPECT_WARNING_DIALOG_H__ diff --git a/src/GuiQt/MapSettingsChartTwoLineHistoryWidget.cxx b/src/GuiQt/MapSettingsChartTwoLineHistoryWidget.cxx new file mode 100644 index 0000000000000000000000000000000000000000..a416b961d4d4c61839e9e8f6c015eeec78c1b96c --- /dev/null +++ b/src/GuiQt/MapSettingsChartTwoLineHistoryWidget.cxx @@ -0,0 +1,583 @@ + +/*LICENSE_START*/ +/* + * Copyright (C) 2017 Washington University School of Medicine + * + * 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 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ +/*LICENSE_END*/ + +#define __MAP_SETTINGS_CHART_TWO_LINE_HISTORY_WIDGET_DECLARE__ +#include "MapSettingsChartTwoLineHistoryWidget.h" +#undef __MAP_SETTINGS_CHART_TWO_LINE_HISTORY_WIDGET_DECLARE__ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "CaretAssert.h" +#include "CaretColorEnumComboBox.h" +#include "CaretMappableDataFile.h" +#include "ChartTwoDataCartesian.h" +#include "ChartableTwoFileDelegate.h" +#include "ChartableTwoFileLineSeriesChart.h" +#include "ChartTwoLineSeriesHistory.h" +#include "ChartTwoOverlay.h" +#include "EventGraphicsUpdateAllWindows.h" +#include "EventManager.h" +#include "MapFileDataSelector.h" +#include "WuQDoubleSpinBox.h" +#include "WuQImageLabel.h" +#include "WuQMessageBox.h" +#include "WuQtUtilities.h" + +using namespace caret; + + + +/** + * \class caret::MapSettingsChartTwoLineHistoryWidget + * \brief Dialog for controlling the display of line series chart data. + * \ingroup GuiQt + */ + +/** + * Constructor. + */ +MapSettingsChartTwoLineHistoryWidget::MapSettingsChartTwoLineHistoryWidget(QWidget* parent) +: QWidget(parent) +{ + const AString maxHistoryString(AString::number(ChartTwoLineSeriesHistory::getMaximumRetainedHistoryCount())); + const AString defaultColorToolTip("Color assigned to newly added line-series items"); + const AString displayMaximumToolTip("Maximum number of line-series items enabled for viewing\n" + "As new items are added, older items are disabled for viewing\n" + "The number of history items retained (including those not\n" + "displayed) is limited to this value or " + maxHistoryString + ", whichever is larger."); + + m_removeAllHistoryIcon = WuQtUtilities::loadIcon(":/SpecFileDialog/delete_icon.png"); + + m_filenameLabel = new QLabel(""); + + QLabel* defaultColorLabel = new QLabel("Default Color: "); + defaultColorLabel->setToolTip(defaultColorToolTip); + m_defaultColorComboBox = new CaretColorEnumComboBox(this); + m_defaultColorComboBox->getWidget()->setToolTip(defaultColorToolTip); + m_defaultColorComboBox->getComboBox()->setSizeAdjustPolicy(QComboBox::AdjustToContentsOnFirstShow); + QObject::connect(m_defaultColorComboBox, &CaretColorEnumComboBox::colorSelected, + this, &MapSettingsChartTwoLineHistoryWidget::defaultColorSelected); + + QLabel* defaultLineWidthLabel = new QLabel("Default Line Width: "); + m_defaultLineWidthSpinBox = new WuQDoubleSpinBox(this); + m_defaultLineWidthSpinBox->setRangePercentage(0.0, 100.0); + m_defaultLineWidthSpinBox->setToolTip("Default width of new chart lines"); + defaultLineWidthLabel->setToolTip(m_defaultLineWidthSpinBox->getWidget()->toolTip()); + QObject::connect(m_defaultLineWidthSpinBox, static_cast(&WuQDoubleSpinBox::valueChanged), + this, &MapSettingsChartTwoLineHistoryWidget::defaultLineWidthChanged); + + QToolButton* removeAllHistoryToolButton = new QToolButton(); + removeAllHistoryToolButton->setText("Remove All History..."); + QObject::connect(removeAllHistoryToolButton, &QToolButton::clicked, + this, &MapSettingsChartTwoLineHistoryWidget::removeAllHistoryButtonClicked); + + QLabel* viewedMaximumLabel = new QLabel("Viewed Maximum: "); + viewedMaximumLabel->setToolTip(displayMaximumToolTip); + m_viewedMaximumCountSpinBox = new QSpinBox(); + m_viewedMaximumCountSpinBox->setToolTip(displayMaximumToolTip); + m_viewedMaximumCountSpinBox->setRange(1, 10000); + QObject::connect(m_viewedMaximumCountSpinBox, SIGNAL(valueChanged(int)), + this, SLOT(viewedMaximumSpinBoxValueChanged(int))); + + m_removeHistoryItemSignalMapper = new QSignalMapper(this); + QObject::connect(m_removeHistoryItemSignalMapper, SIGNAL(mapped(int)), + this, SLOT(removeHistoryItemSelected(int))); + + m_colorItemSignalMapper = new QSignalMapper(this); + QObject::connect(m_colorItemSignalMapper, SIGNAL(mapped(int)), + this, SLOT(colorItemSelected(int))); + + m_lineWidthItemSignalMapper = new QSignalMapper(this); + QObject::connect(m_lineWidthItemSignalMapper, SIGNAL(mapped(int)), + this, SLOT(lineWidthItemSelected(int))); + + m_tableWidget = new QTableWidget(); + m_tableWidget->setSelectionBehavior(QTableWidget::SelectItems); + m_tableWidget->setSelectionMode(QTableWidget::SingleSelection); + QObject::connect(m_tableWidget, &QTableWidget::cellChanged, + this, &MapSettingsChartTwoLineHistoryWidget::tableWidgetCellChanged); + + QGridLayout* topLayout = new QGridLayout(); + topLayout->setColumnStretch(0, 0); + topLayout->setColumnStretch(1, 0); + topLayout->setColumnStretch(2, 0); + topLayout->setColumnStretch(3, 0); + topLayout->setColumnStretch(4, 100); + int gridRow = 0; + topLayout->addWidget(m_filenameLabel, gridRow, 0, 1, 5, Qt::AlignLeft); + gridRow++; + topLayout->addWidget(defaultLineWidthLabel, gridRow, 0); + topLayout->addWidget(m_defaultLineWidthSpinBox->getWidget(), gridRow, 1); + topLayout->addWidget(defaultColorLabel, gridRow, 2); + topLayout->addWidget(m_defaultColorComboBox->getWidget(), gridRow, 3); + gridRow++; + topLayout->addWidget(viewedMaximumLabel, gridRow, 0); + topLayout->addWidget(m_viewedMaximumCountSpinBox, gridRow, 1); + topLayout->addWidget(removeAllHistoryToolButton, gridRow, 2, 1, 2, Qt::AlignLeft); + + QVBoxLayout* layout = new QVBoxLayout(this); + layout->addLayout(topLayout, 0); + layout->addWidget(m_tableWidget, 100); +} + +/** + * Destructor. + */ +MapSettingsChartTwoLineHistoryWidget::~MapSettingsChartTwoLineHistoryWidget() +{ +} + +/** + * @return The chart overlay in this dialog (May be NULL !) + */ +ChartTwoOverlay* +MapSettingsChartTwoLineHistoryWidget::getChartOverlay() +{ + ChartTwoOverlay* chartOverlay = NULL; + if (m_chartOverlayWeakPointer.expired()) { + m_chartOverlayWeakPointer.reset(); + } + else { + chartOverlay = m_chartOverlayWeakPointer.lock().get(); + } + + return chartOverlay; +} + + +/** + * Update dialog if the given chart overlay is in the dialog. + * This may occur when the user changes the file in the chart overlay. + * + * @param chartOverlay + * Chart overlay for history display. + */ +void +MapSettingsChartTwoLineHistoryWidget::updateIfThisChartOverlayIsInDialog(const ChartTwoOverlay* chartOverlay) +{ + ChartTwoOverlay* currentChartOverlay = getChartOverlay(); + + if (currentChartOverlay != NULL) { + if (chartOverlay != currentChartOverlay) { + return; + } + } + + updateDialogContentPrivate(); +} + +/** + * Update the content of the widget. + * + * @param chartOverlay + * Chart overlay for history display. + */ +void +MapSettingsChartTwoLineHistoryWidget::updateContent(ChartTwoOverlay* chartOverlay) +{ + CaretAssert(chartOverlay); + m_chartOverlayWeakPointer = chartOverlay->getWeakPointerToSelf(); + updateDialogContentPrivate(); +} + +void +MapSettingsChartTwoLineHistoryWidget::updateDialogContentPrivate() +{ + ChartTwoOverlay* chartOverlay = getChartOverlay(); + if (chartOverlay == NULL) { + close(); + return; + } + + ChartTwoLineSeriesHistory* lineSeriesHistory = getLineSeriesHistory(); + + if (lineSeriesHistory != NULL) { + const CaretMappableDataFile* mapFile = getMapFile(); + if (mapFile != NULL) { + m_filenameLabel->setText("Filename: " + mapFile->getFileNameNoPath()); + m_filenameLabel->setToolTip(mapFile->getFileName()); + } + m_defaultColorComboBox->setSelectedColor(lineSeriesHistory->getDefaultColor()); + m_defaultLineWidthSpinBox->blockSignals(true); + m_defaultLineWidthSpinBox->setValue(lineSeriesHistory->getDefaultLineWidth()); + m_defaultLineWidthSpinBox->blockSignals(false); + + m_viewedMaximumCountSpinBox->blockSignals(true); + m_viewedMaximumCountSpinBox->setValue(lineSeriesHistory->getDisplayCount()); + m_viewedMaximumCountSpinBox->blockSignals(false); + + loadHistoryIntoTableWidget(lineSeriesHistory); + } + else { + close(); + } +} + + +/** + * @return Map file in the dialog. + */ +CaretMappableDataFile* +MapSettingsChartTwoLineHistoryWidget::getMapFile() +{ + CaretMappableDataFile* mapFile = NULL; + ChartTwoOverlay* chartOverlay = getChartOverlay(); + if (chartOverlay != NULL) { + if (chartOverlay->getChartTwoDataType() == ChartTwoDataTypeEnum::CHART_DATA_TYPE_LINE_SERIES) { + ChartTwoOverlay::SelectedIndexType indexType = ChartTwoOverlay::SelectedIndexType::INVALID; + int32_t mapIndex = -1; + chartOverlay->getSelectionData(mapFile, indexType, mapIndex); + } + } + + return mapFile; +} + +/** + * @return The line series history for the current chart overlay + */ +ChartTwoLineSeriesHistory* +MapSettingsChartTwoLineHistoryWidget::getLineSeriesHistory() +{ + ChartTwoLineSeriesHistory* lineSeriesHistory = NULL; + + CaretMappableDataFile* mapFile = getMapFile(); + if (mapFile != NULL) { + ChartableTwoFileLineSeriesChart* chartTwoLineSeries = mapFile->getChartingDelegate()->getLineSeriesCharting(); + lineSeriesHistory = chartTwoLineSeries->getHistory(); + } + + return lineSeriesHistory; +} + + +/** + * Load the line-series history into the table widget. + * + * @param lineSeriesHistory + * The line series history. + */ +void +MapSettingsChartTwoLineHistoryWidget::loadHistoryIntoTableWidget(ChartTwoLineSeriesHistory* lineSeriesHistory) +{ + CaretAssert(lineSeriesHistory); + + /* + * NOTE: We never remove items from a table due to issues + * with widget ownership (and destruction) and the signal mappers. + * + * When new rows are needed, they are added. + * When there are two many rows, they are hidden. + */ + + const QSignalBlocker tableSignalBlocker(m_tableWidget); + + const int32_t oldRowCount = m_tableWidget->rowCount(); + const int32_t numHistory = lineSeriesHistory->getHistoryCount(); + + /* + * Setup table dimensions + */ + m_tableWidget->setRowCount(std::max(numHistory, oldRowCount)); + m_tableWidget->setColumnCount(COLUMN_COUNT); + + /* + * If needed, add cells + */ + for (int32_t iRow = oldRowCount; iRow < numHistory; iRow++) { + for (int32_t j = 0; j < COLUMN_COUNT; j++) { + if (j == COLUMN_VIEW) { + QTableWidgetItem* item = new QTableWidgetItem(""); + item->setFlags(Qt::ItemIsEnabled + | Qt::ItemIsSelectable + | Qt::ItemIsUserCheckable); + m_tableWidget->setItem(iRow, j, item); + } + else if (j == COLUMN_REMOVE) { + WuQImageLabel* removeHistoryLabel = new WuQImageLabel(m_removeAllHistoryIcon, + "Remove"); + QObject::connect(removeHistoryLabel, SIGNAL(clicked()), + m_removeHistoryItemSignalMapper, SLOT(map())); + m_removeHistoryItemSignalMapper->setMapping(removeHistoryLabel, iRow); + m_tableWidget->setCellWidget(iRow, + COLUMN_REMOVE, + removeHistoryLabel); + } + else if (j == COLUMN_COLOR) { + CaretColorEnumComboBox* caretColorComboBox = new CaretColorEnumComboBox(this); + QObject::connect(caretColorComboBox, SIGNAL(colorSelected(const CaretColorEnum::Enum)), + m_colorItemSignalMapper, SLOT(map())); + m_colorItemSignalMapper->setMapping(caretColorComboBox, iRow); + m_tableWidget->setCellWidget(iRow, + COLUMN_COLOR, + caretColorComboBox->getWidget()); + m_colorComboBoxes.push_back(caretColorComboBox); + } + else if (j == COLUMN_LINE_WIDTH) { + WuQDoubleSpinBox* lineWidthSpinBox = new WuQDoubleSpinBox(this); + lineWidthSpinBox->setRangePercentage(0.0, 100.0); + QObject::connect(lineWidthSpinBox, SIGNAL(valueChanged(double)), + m_lineWidthItemSignalMapper, SLOT(map())); + m_lineWidthItemSignalMapper->setMapping(lineWidthSpinBox, iRow); + m_tableWidget->setCellWidget(iRow, + COLUMN_LINE_WIDTH, + lineWidthSpinBox->getWidget()); + m_lineWidthSpinBoxes.push_back(lineWidthSpinBox); + } + else if (j == COLUMN_DESCRIPTION) { + QTableWidgetItem* item = new QTableWidgetItem(""); + item->setFlags(Qt::ItemIsEnabled + | Qt::ItemIsSelectable); + m_tableWidget->setItem(iRow, j, item); + } + else { + CaretAssert(0); + } + } + } + + CaretAssert(m_lineWidthSpinBoxes.size() == m_colorComboBoxes.size()); + + /* + * Hide rows not needed + */ + for (int32_t iRow = numHistory; iRow < oldRowCount; iRow++) { + m_tableWidget->setRowHidden(iRow, true); + } + + /* + * Load cells + */ + CaretAssert(numHistory <= m_tableWidget->rowCount()); + for (int32_t iRow = 0; iRow < numHistory; iRow++) { + ChartTwoDataCartesian* historyData = lineSeriesHistory->getHistoryItem(iRow); + const MapFileDataSelector* mapFileDataSelector = historyData->getMapFileDataSelector(); + + m_tableWidget->item(iRow, COLUMN_VIEW)->setCheckState(historyData->isSelected() + ? Qt::Checked + : Qt::Unchecked); + + CaretAssertVectorIndex(m_colorComboBoxes, iRow); + m_colorComboBoxes[iRow]->setSelectedColor(historyData->getColor()); + + CaretAssertVectorIndex(m_lineWidthSpinBoxes, iRow); + m_lineWidthSpinBoxes[iRow]->setValue(historyData->getLineWidth()); + + QTableWidgetItem* item = m_tableWidget->item(iRow, COLUMN_DESCRIPTION); + item->setText(mapFileDataSelector->toString()); + + m_tableWidget->setRowHidden(iRow, false); + } + + /* + * If table was empty, add column titles + * and adjust column widths + */ + if ((oldRowCount == 0) + && (numHistory > 0)) { + m_tableWidget->setHorizontalHeaderItem(COLUMN_VIEW, + new QTableWidgetItem("View")); + m_tableWidget->setHorizontalHeaderItem(COLUMN_REMOVE, + new QTableWidgetItem("Remove")); + m_tableWidget->setHorizontalHeaderItem(COLUMN_COLOR, + new QTableWidgetItem("Color")); + m_tableWidget->setHorizontalHeaderItem(COLUMN_LINE_WIDTH, + new QTableWidgetItem("Line Width")); + m_tableWidget->setHorizontalHeaderItem(COLUMN_DESCRIPTION, + new QTableWidgetItem("Description")); + + m_tableWidget->resizeColumnToContents(COLUMN_VIEW); + m_tableWidget->resizeColumnToContents(COLUMN_REMOVE); + m_tableWidget->resizeColumnToContents(COLUMN_COLOR); + m_tableWidget->resizeColumnToContents(COLUMN_LINE_WIDTH); + m_tableWidget->setColumnWidth(COLUMN_DESCRIPTION, + 350); + + QSize tableSize = WuQtUtilities::estimateTableWidgetSize(m_tableWidget); + + WuQtUtilities::resizeWindow(this, + tableSize.width() + 30, + sizeHint().height()); + } +} + +/** + * Called when the content of a cell changes. + * Update corresponding item in the spec file. + * + * @param rowIndex + * The row of the cell that was clicked. + * @param columnIndex + * The columnof the cell that was clicked. + */ +void +MapSettingsChartTwoLineHistoryWidget::tableWidgetCellChanged(int rowIndex, int columnIndex) +{ + QTableWidgetItem* item = m_tableWidget->item(rowIndex, columnIndex); + if (item != NULL) { + if (columnIndex == COLUMN_VIEW) { + ChartTwoLineSeriesHistory* lineSeriesHistory = getLineSeriesHistory(); + if (lineSeriesHistory != NULL) { + ChartTwoDataCartesian* data = lineSeriesHistory->getHistoryItem(rowIndex); + CaretAssert(data); + data->setSelected(WuQtUtilities::checkStateToBool(item->checkState())); + + EventManager::get()->sendEvent(EventGraphicsUpdateAllWindows().getPointer()); + } + } + } +} + +/** + * Remove a history item selected + * + * @param rowIndex + * Row index of item. + */ +void +MapSettingsChartTwoLineHistoryWidget::removeHistoryItemSelected(int rowIndex) +{ + /* + * May be possible for user to press another remove button + * before this one is finished + */ + const QSignalBlocker removeSignalBlocker(m_removeHistoryItemSignalMapper); + + ChartTwoLineSeriesHistory* lineSeriesHistory = getLineSeriesHistory(); + lineSeriesHistory->removeHistoryItem(rowIndex); + updateDialogContentPrivate(); + EventManager::get()->sendEvent(EventGraphicsUpdateAllWindows().getPointer()); +} + +/** + * Color item selected + * + * @param rowIndex + * Row index of item. + */ +void +MapSettingsChartTwoLineHistoryWidget::colorItemSelected(int rowIndex) +{ + CaretAssertVectorIndex(m_colorComboBoxes, rowIndex); + CaretAssert(m_colorComboBoxes[rowIndex]); + + ChartTwoLineSeriesHistory* lineSeriesHistory = getLineSeriesHistory(); + if (lineSeriesHistory != NULL) { + ChartTwoDataCartesian* data = lineSeriesHistory->getHistoryItem(rowIndex); + CaretAssert(data); + data->setColor(m_colorComboBoxes[rowIndex]->getSelectedColor()); + + EventManager::get()->sendEvent(EventGraphicsUpdateAllWindows().getPointer()); + } +} + +/** + * line width item selected + * + * @param rowIndex + * Row index of item. + */ +void +MapSettingsChartTwoLineHistoryWidget::lineWidthItemSelected(int rowIndex) +{ + CaretAssertVectorIndex(m_lineWidthSpinBoxes, rowIndex); + CaretAssert(m_lineWidthSpinBoxes[rowIndex]); + + ChartTwoLineSeriesHistory* lineSeriesHistory = getLineSeriesHistory(); + if (lineSeriesHistory != NULL) { + ChartTwoDataCartesian* data = lineSeriesHistory->getHistoryItem(rowIndex); + CaretAssert(data); + data->setLineWidth(m_lineWidthSpinBoxes[rowIndex]->value()); + + EventManager::get()->sendEvent(EventGraphicsUpdateAllWindows().getPointer()); + } +} + +/** + * Called when the "Remove All History" button is clicked. + */ +void +MapSettingsChartTwoLineHistoryWidget::removeAllHistoryButtonClicked() +{ + if (WuQMessageBox::warningOkCancel(this, + ("Remove all line series history from the file " + + getMapFile()->getFileNameNoPath()))) { + ChartTwoLineSeriesHistory* lineSeriesHistory = getLineSeriesHistory(); + lineSeriesHistory->clearHistory(); + updateDialogContentPrivate(); + EventManager::get()->sendEvent(EventGraphicsUpdateAllWindows().getPointer()); + } +} + +/** + * Called when the default color is changed. + * + * @param color + * New default color. + */ +void +MapSettingsChartTwoLineHistoryWidget::defaultColorSelected(const CaretColorEnum::Enum color) +{ + ChartTwoLineSeriesHistory* lineSeriesHistory = getLineSeriesHistory(); + if (lineSeriesHistory != NULL) { + lineSeriesHistory->setDefaultColor(color); + } +} + +/** + * Called when line width is changed + * + * @param defaultLineWidth + * New default line width. + */ +void +MapSettingsChartTwoLineHistoryWidget::defaultLineWidthChanged(double defaultLineWidth) +{ + ChartTwoLineSeriesHistory* lineSeriesHistory = getLineSeriesHistory(); + if (lineSeriesHistory != NULL) { + lineSeriesHistory->setDefaultLineWidth(defaultLineWidth); + } +} + +/** + * Gets called when display last count is changed. + * + * @param num + * Number of history items to display. + */ +void +MapSettingsChartTwoLineHistoryWidget::viewedMaximumSpinBoxValueChanged(int num) +{ + ChartTwoLineSeriesHistory* lineSeriesHistory = getLineSeriesHistory(); + if (lineSeriesHistory != NULL) { + lineSeriesHistory->setDisplayCount(num); + updateDialogContentPrivate(); + EventManager::get()->sendEvent(EventGraphicsUpdateAllWindows().getPointer()); + } +} \ No newline at end of file diff --git a/src/GuiQt/MapSettingsChartTwoLineHistoryWidget.h b/src/GuiQt/MapSettingsChartTwoLineHistoryWidget.h new file mode 100644 index 0000000000000000000000000000000000000000..2594fa802caccdff55d8346cd66e667d632f9bc5 --- /dev/null +++ b/src/GuiQt/MapSettingsChartTwoLineHistoryWidget.h @@ -0,0 +1,134 @@ +#ifndef __MAP_SETTINGS_CHART_TWO_LINE_HISTORY_WIDGET_H__ +#define __MAP_SETTINGS_CHART_TWO_LINE_HISTORY_WIDGET_H__ + +/*LICENSE_START*/ +/* + * Copyright (C) 2017 Washington University School of Medicine + * + * 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 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ +/*LICENSE_END*/ + + + +#include + +#include + +#include "CaretColorEnum.h" + +class QLabel; +class QIcon; +class QSignalMapper; +class QSpinBox; +class QTableWidget; + +namespace caret { + + class CaretColorEnumComboBox; + class CaretMappableDataFile; + class ChartTwoLineSeriesHistory; + class ChartTwoOverlay; + class WuQDoubleSpinBox; + + class MapSettingsChartTwoLineHistoryWidget : public QWidget { + + Q_OBJECT + + public: + MapSettingsChartTwoLineHistoryWidget(QWidget* parent = 0); + + virtual ~MapSettingsChartTwoLineHistoryWidget(); + + void updateContent(ChartTwoOverlay* chartOverlay); + + void updateIfThisChartOverlayIsInDialog(const ChartTwoOverlay* chartOverlay); + + // ADD_NEW_METHODS_HERE + + private slots: + void tableWidgetCellChanged(int rowIndex, int columnIndex); + + void removeHistoryItemSelected(int rowIndex); + + void colorItemSelected(int rowIndex); + + void lineWidthItemSelected(int rowIndex); + + void removeAllHistoryButtonClicked(); + + void defaultColorSelected(const CaretColorEnum::Enum color); + + void defaultLineWidthChanged(double); + + void viewedMaximumSpinBoxValueChanged(int); + + private: + + MapSettingsChartTwoLineHistoryWidget(const MapSettingsChartTwoLineHistoryWidget&); + + MapSettingsChartTwoLineHistoryWidget& operator=(const MapSettingsChartTwoLineHistoryWidget&); + + void updateDialogContentPrivate(); + + void loadHistoryIntoTableWidget(ChartTwoLineSeriesHistory* lineSeriesHistory); + + ChartTwoOverlay* getChartOverlay(); + + ChartTwoLineSeriesHistory* getLineSeriesHistory(); + + CaretMappableDataFile* getMapFile(); + + std::weak_ptr m_chartOverlayWeakPointer; + + QTableWidget* m_tableWidget; + + QIcon* m_removeAllHistoryIcon; + + QSignalMapper* m_removeHistoryItemSignalMapper; + + QSignalMapper* m_colorItemSignalMapper; + + QSignalMapper* m_lineWidthItemSignalMapper; + + std::vector m_colorComboBoxes; + + std::vector m_lineWidthSpinBoxes; + + QLabel* m_filenameLabel; + + CaretColorEnumComboBox* m_defaultColorComboBox; + + WuQDoubleSpinBox* m_defaultLineWidthSpinBox; + + QSpinBox* m_viewedMaximumCountSpinBox; + + static const int32_t COLUMN_VIEW = 0; + static const int32_t COLUMN_REMOVE = 1; + static const int32_t COLUMN_COLOR = 2; + static const int32_t COLUMN_LINE_WIDTH = 3; + static const int32_t COLUMN_DESCRIPTION = 4; + static const int32_t COLUMN_COUNT = 5; + + // ADD_NEW_MEMBERS_HERE + + }; + +#ifdef __MAP_SETTINGS_CHART_TWO_LINE_HISTORY_WIDGET_DECLARE__ + // +#endif // __MAP_SETTINGS_CHART_TWO_LINE_HISTORY_WIDGET_DECLARE__ + +} // namespace +#endif //__MAP_SETTINGS_CHART_TWO_LINE_HISTORY_WIDGET_H__ diff --git a/src/GuiQt/MapSettingsColorBarPaletteOptionsWidget.cxx b/src/GuiQt/MapSettingsColorBarPaletteOptionsWidget.cxx new file mode 100644 index 0000000000000000000000000000000000000000..d66cf0d84b3c7df8745afc52ed2296271c4af2f0 --- /dev/null +++ b/src/GuiQt/MapSettingsColorBarPaletteOptionsWidget.cxx @@ -0,0 +1,195 @@ + +/*LICENSE_START*/ +/* + * Copyright (C) 2018 Washington University School of Medicine + * + * 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 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ +/*LICENSE_END*/ + +#define __MAP_SETTINGS_COLOR_BAR_PALETTE_OPTIONS_WIDGET_DECLARE__ +#include "MapSettingsColorBarPaletteOptionsWidget.h" +#undef __MAP_SETTINGS_COLOR_BAR_PALETTE_OPTIONS_WIDGET_DECLARE__ + +#include +#include +#include +#include + +#include "CaretAssert.h" +#include "CaretMappableDataFile.h" +#include "CopyPaletteColorMappingToFilesDialog.h" +#include "EventGraphicsUpdateAllWindows.h" +#include "EventManager.h" +#include "EventSurfaceColoringInvalidate.h" +#include "WuQMessageBox.h" +#include "WuQtUtilities.h" + +using namespace caret; + + + +/** + * \class caret::MapSettingsColorBarPaletteOptionsWidget + * \brief Widget for apply to all maps checkbox and apply to files button for color bar and palette + * \ingroup GuiQt + */ + +/** + * Constructor. + * + * @param parent + * The parent widget. + */ +MapSettingsColorBarPaletteOptionsWidget::MapSettingsColorBarPaletteOptionsWidget(QWidget* parent) +: QGroupBox("Color Bar and Palette Settings", + parent) +{ + m_applyToAllMapsCheckBox = new QCheckBox("Apply to All Maps"); + QObject::connect(m_applyToAllMapsCheckBox, &QCheckBox::clicked, + this, &MapSettingsColorBarPaletteOptionsWidget::applyToAllMapsCheckBoxClicked); + m_applyToAllMapsCheckBox->setToolTip("When checked, these color bar and palette settings\n" + "are applied to all maps in the file.\n" + "When disabled, the file uses one palette for all maps."); + + m_applyToAllFilesAction = new QAction(this); + m_applyToAllFilesAction->setText("Apply to Files..."); + m_applyToAllFilesAction->setToolTip("Displays a dialog that allows selection of data\n" + "files to which the palette settings are applied"); + QObject::connect(m_applyToAllFilesAction, &QAction::triggered, + this, &MapSettingsColorBarPaletteOptionsWidget::applyToAllFilesToolButtonClicked); + + QToolButton* applyToFilesToolButton = new QToolButton(); + applyToFilesToolButton->setDefaultAction(m_applyToAllFilesAction); + + QVBoxLayout* layout = new QVBoxLayout(this); + layout->addWidget(m_applyToAllMapsCheckBox); + layout->addWidget(applyToFilesToolButton); + setSizePolicy(QSizePolicy(QSizePolicy::MinimumExpanding, + QSizePolicy::Fixed)); +} + +/** + * Destructor. + */ +MapSettingsColorBarPaletteOptionsWidget::~MapSettingsColorBarPaletteOptionsWidget() +{ +} + +/** + * Update contents for editing a map settings for data in a caret + * mappable data file. + * + * @param caretMappableDataFile + * Data file containing palette that is edited. + * @param mapIndex + * Index of map for palette that is edited. + */ +void +MapSettingsColorBarPaletteOptionsWidget::updateEditor(CaretMappableDataFile* caretMappableDataFile, + const int32_t mapIndex) +{ + m_mapFile = caretMappableDataFile; + m_mapFileIndex = mapIndex; + + if ((m_mapFile != NULL) + && (m_mapFileIndex >= 0)) { + + if (m_mapFile->isOnePaletteUsedForAllMaps()) { + m_applyToAllMapsCheckBox->setEnabled(false); + m_applyToAllMapsCheckBox->setChecked(false); + } + else { + m_applyToAllMapsCheckBox->setEnabled(true); + m_applyToAllMapsCheckBox->setChecked(m_mapFile->isApplyPaletteColorMappingToAllMaps()); + } + + setEnabled(true); + } + else { + setEnabled(false); + } +} + +/** + * Called by users of this widget to apply the selected options. + */ +void +MapSettingsColorBarPaletteOptionsWidget::applyOptions() +{ + if (m_mapFile != NULL) { + if (m_mapFile->isApplyPaletteColorMappingToAllMaps()) { + m_mapFile->applyPaletteColorMappingToAllMaps(m_mapFileIndex); + m_mapFile->updateScalarColoringForAllMaps(); + } + } + + updateColoringAndGraphics(); +} + +/** + * Called when the "Apply to All Maps" checkbox is clicked + * + * @param checked + * New status of button. + */ +void +MapSettingsColorBarPaletteOptionsWidget::applyToAllMapsCheckBoxClicked(bool checked) +{ + if (m_mapFile != NULL) { + m_mapFile->setApplyPaletteColorMappingToAllMaps(checked); + } + + applyOptions(); +} + +/** + * Called when the Apply to Files tool button is clicked + */ +void +MapSettingsColorBarPaletteOptionsWidget::applyToAllFilesToolButtonClicked() +{ + if (m_mapFile != NULL) { + AString errorMessage; + if ( ! CopyPaletteColorMappingToFilesDialog::run(m_mapFile, + m_mapFile->getMapPaletteColorMapping(m_mapFileIndex), + this, + errorMessage)) { + if ( ! errorMessage.isEmpty()) { + WuQMessageBox::errorOk(this, errorMessage); + } + } + + updateColoringAndGraphics(); + } +} + +/** + * Update coloring and graphics + */ +void +MapSettingsColorBarPaletteOptionsWidget::updateColoringAndGraphics() +{ + if (m_applyToAllMapsCheckBox->isChecked()) { + m_mapFile->updateScalarColoringForAllMaps(); + } + else { + m_mapFile->updateScalarColoringForMap(m_mapFileIndex); + } + EventManager::get()->sendEvent(EventSurfaceColoringInvalidate().getPointer()); + EventManager::get()->sendEvent(EventGraphicsUpdateAllWindows().getPointer()); +} + + diff --git a/src/GuiQt/MapSettingsColorBarPaletteOptionsWidget.h b/src/GuiQt/MapSettingsColorBarPaletteOptionsWidget.h new file mode 100644 index 0000000000000000000000000000000000000000..b7d6e42047041e85f01713356aeb63e1dc4631de --- /dev/null +++ b/src/GuiQt/MapSettingsColorBarPaletteOptionsWidget.h @@ -0,0 +1,83 @@ +#ifndef __MAP_SETTINGS_COLOR_BAR_PALETTE_OPTIONS_WIDGET_H__ +#define __MAP_SETTINGS_COLOR_BAR_PALETTE_OPTIONS_WIDGET_H__ + +/*LICENSE_START*/ +/* + * Copyright (C) 2018 Washington University School of Medicine + * + * 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 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ +/*LICENSE_END*/ + + + +#include + + +#include + +class QAction; +class QCheckBox; + +namespace caret { + + class CaretMappableDataFile; + + class MapSettingsColorBarPaletteOptionsWidget : public QGroupBox { + + Q_OBJECT + + public: + MapSettingsColorBarPaletteOptionsWidget(QWidget* parent = 0); + + virtual ~MapSettingsColorBarPaletteOptionsWidget(); + + void updateEditor(CaretMappableDataFile* caretMappableDataFile, + const int32_t mapIndex); + + void applyOptions(); + + // ADD_NEW_METHODS_HERE + + private slots: + void applyToAllMapsCheckBoxClicked(bool checked); + + void applyToAllFilesToolButtonClicked(); + + private: + MapSettingsColorBarPaletteOptionsWidget(const MapSettingsColorBarPaletteOptionsWidget&); + + MapSettingsColorBarPaletteOptionsWidget& operator=(const MapSettingsColorBarPaletteOptionsWidget&); + + void updateColoringAndGraphics(); + + QCheckBox* m_applyToAllMapsCheckBox; + + QAction* m_applyToAllFilesAction; + + CaretMappableDataFile* m_mapFile = NULL; + + int32_t m_mapFileIndex = -1; + + // ADD_NEW_MEMBERS_HERE + + }; + +#ifdef __MAP_SETTINGS_COLOR_BAR_PALETTE_OPTIONS_WIDGET_DECLARE__ + // +#endif // __MAP_SETTINGS_COLOR_BAR_PALETTE_OPTIONS_WIDGET_DECLARE__ + +} // namespace +#endif //__MAP_SETTINGS_COLOR_BAR_PALETTE_OPTIONS_WIDGET_H__ diff --git a/src/GuiQt/MapSettingsColorBarWidget.cxx b/src/GuiQt/MapSettingsColorBarWidget.cxx index 14da03ba2c575ede79d5b2d70f8e9495d2713a31..5eed2fe4d9a6034d74aeaab227a9fac37854c48e 100644 --- a/src/GuiQt/MapSettingsColorBarWidget.cxx +++ b/src/GuiQt/MapSettingsColorBarWidget.cxx @@ -36,6 +36,7 @@ #include "EnumComboBoxTemplate.h" #include "EventGraphicsUpdateAllWindows.h" #include "EventManager.h" +#include "MapSettingsColorBarPaletteOptionsWidget.h" #include "NumericFormatModeEnum.h" #include "Overlay.h" #include "PaletteColorBarValuesModeEnum.h" @@ -65,12 +66,14 @@ MapSettingsColorBarWidget::MapSettingsColorBarWidget(QWidget* parent) QWidget* numericsWidget = this->createDataNumericsSection(); + m_paletteOptionsWidget = new MapSettingsColorBarPaletteOptionsWidget(); QGridLayout* layout = new QGridLayout(this); WuQtUtilities::setLayoutSpacingAndMargins(layout, 6, 6); layout->addWidget(locationPositionWidget, 0, 0, Qt::AlignLeft); layout->addWidget(numericsWidget, 1, 0, Qt::AlignLeft); + layout->addWidget(m_paletteOptionsWidget); layout->setSizeConstraint(QLayout::SetFixedSize); setSizePolicy(QSizePolicy::Fixed, @@ -113,10 +116,22 @@ MapSettingsColorBarWidget::~MapSettingsColorBarWidget() // } //} +/** + * Update the content of the widget. + * + * @param caretMappableDataFile + * Data file containing palette that is edited. + * @param mapIndex + * Index of map for palette that is edited. + */ void -MapSettingsColorBarWidget::updateContent(AnnotationColorBar* annotationColorBar, +MapSettingsColorBarWidget::updateContent(CaretMappableDataFile* caretMappableDataFile, + const int32_t mapIndex, + AnnotationColorBar* annotationColorBar, PaletteColorMapping* paletteColorMapping) { + m_caretMappableDataFile = caretMappableDataFile; + m_mapIndex = mapIndex; m_colorBar = annotationColorBar; m_paletteColorMapping = paletteColorMapping; @@ -144,6 +159,9 @@ MapSettingsColorBarWidget::updateContentPrivate() std::vector annotationTwoDimVector; annotationTwoDimVector.push_back(m_colorBar); + + m_paletteOptionsWidget->updateEditor(m_caretMappableDataFile, + m_mapIndex); enableWidget = true; } @@ -162,12 +180,14 @@ MapSettingsColorBarWidget::applySelections() { if (m_paletteColorMapping != NULL) { m_paletteColorMapping->setColorBarValuesMode(m_colorBarDataModeComboBox->getSelectedItem()); - m_paletteColorMapping->setNumericFormatMode(m_colorBarNumericFormatModeComboBox->getSelectedItem()); - m_paletteColorMapping->setPrecisionDigits(m_colorBarDecimalsSpinBox->value()); - m_paletteColorMapping->setNumericSubdivisionCount(m_colorBarNumericSubdivisionsSpinBox->value()); - m_paletteColorMapping->setShowTickMarksSelected(m_showTickMarksCheckBox->isChecked()); + m_paletteColorMapping->setColorBarNumericFormatMode(m_colorBarNumericFormatModeComboBox->getSelectedItem()); + m_paletteColorMapping->setColorBarPrecisionDigits(m_colorBarDecimalsSpinBox->value()); + m_paletteColorMapping->setColorBarNumericSubdivisionCount(m_colorBarNumericSubdivisionsSpinBox->value()); + m_paletteColorMapping->setColorBarShowTickMarksSelected(m_showTickMarksCheckBox->isChecked()); } + m_paletteOptionsWidget->applyOptions(); + EventManager::get()->sendEvent(EventGraphicsUpdateAllWindows().getPointer()); } @@ -369,7 +389,7 @@ MapSettingsColorBarWidget::updateColorBarAttributes() break; } - const NumericFormatModeEnum::Enum numericFormat = m_paletteColorMapping->getNumericFormatMode(); + const NumericFormatModeEnum::Enum numericFormat = m_paletteColorMapping->getColorBarNumericFormatMode(); switch (numericFormat) { case NumericFormatModeEnum::AUTO: precisionDigitsSpinBoxEnabled = false; @@ -388,17 +408,17 @@ MapSettingsColorBarWidget::updateColorBarAttributes() m_colorBarDecimalsLabel->setEnabled(precisionDigitsSpinBoxEnabled); m_colorBarDecimalsSpinBox->blockSignals(true); - m_colorBarDecimalsSpinBox->setValue(m_paletteColorMapping->getPrecisionDigits()); + m_colorBarDecimalsSpinBox->setValue(m_paletteColorMapping->getColorBarPrecisionDigits()); m_colorBarDecimalsSpinBox->blockSignals(false); m_colorBarDecimalsSpinBox->setEnabled(precisionDigitsSpinBoxEnabled); m_colorBarNumericSubdivisionsLabel->setEnabled(subdivisionsSpinBoxEnabled); m_colorBarNumericSubdivisionsSpinBox->blockSignals(true); - m_colorBarNumericSubdivisionsSpinBox->setValue(m_paletteColorMapping->getNumericSubdivisionCount()); + m_colorBarNumericSubdivisionsSpinBox->setValue(m_paletteColorMapping->getColorBarNumericSubdivisionCount()); m_colorBarNumericSubdivisionsSpinBox->blockSignals(false); m_colorBarNumericSubdivisionsSpinBox->setEnabled(subdivisionsSpinBoxEnabled); - m_showTickMarksCheckBox->setChecked(m_paletteColorMapping->isShowTickMarksSelected()); + m_showTickMarksCheckBox->setChecked(m_paletteColorMapping->isColorBarShowTickMarksSelected()); } } diff --git a/src/GuiQt/MapSettingsColorBarWidget.h b/src/GuiQt/MapSettingsColorBarWidget.h index 038ff55503cd3da28226a08f2761bfdfc9954459..627494d4a66cf293000f963c4ddbf8e9023aac70 100644 --- a/src/GuiQt/MapSettingsColorBarWidget.h +++ b/src/GuiQt/MapSettingsColorBarWidget.h @@ -31,7 +31,9 @@ class QSpinBox; namespace caret { class AnnotationColorBar; + class CaretMappableDataFile; class EnumComboBoxTemplate; + class MapSettingsColorBarPaletteOptionsWidget; class PaletteColorMapping; class Overlay; @@ -47,7 +49,9 @@ namespace caret { // void updateContent(Overlay* overlay); - void updateContent(AnnotationColorBar* annotationColorBar, + void updateContent(CaretMappableDataFile* caretMappableDataFile, + const int32_t mapIndex, + AnnotationColorBar* annotationColorBar, PaletteColorMapping* paletteColorMapping); // ADD_NEW_METHODS_HERE @@ -74,6 +78,10 @@ namespace caret { void updateColorBarAttributes(); + CaretMappableDataFile* m_caretMappableDataFile = NULL; + + int32_t m_mapIndex = -1; + AnnotationColorBar* m_colorBar; PaletteColorMapping* m_paletteColorMapping; @@ -95,6 +103,8 @@ namespace caret { QCheckBox* m_showTickMarksCheckBox; + MapSettingsColorBarPaletteOptionsWidget* m_paletteOptionsWidget; + // ADD_NEW_MEMBERS_HERE }; diff --git a/src/GuiQt/MapSettingsFiberTrajectoryWidget.cxx b/src/GuiQt/MapSettingsFiberTrajectoryWidget.cxx index 6740b90d901170bbc5503301de918b0c4b5a6c4c..6d730e458bbc3267ce8018954239fc13f1b339e4 100644 --- a/src/GuiQt/MapSettingsFiberTrajectoryWidget.cxx +++ b/src/GuiQt/MapSettingsFiberTrajectoryWidget.cxx @@ -100,7 +100,7 @@ MapSettingsFiberTrajectoryWidget::createAttributesWidget() m_colorSelectionComboBox = WuQFactory::newComboBoxSignalInt(this, SLOT(processAttributesChanges())); - QGroupBox* attributesGroupBox = new QGroupBox("Attribues"); + QGroupBox* attributesGroupBox = new QGroupBox("Attributes"); QGridLayout* attributesGridLayout = new QGridLayout(attributesGroupBox); int row = 0; attributesGridLayout->addWidget(colorLabel, row, 0); diff --git a/src/GuiQt/MapSettingsPaletteColorMappingWidget.cxx b/src/GuiQt/MapSettingsPaletteColorMappingWidget.cxx index 6021778e0afe11d9d995ebae55280a101e33d67c..a166bf0c3c8f19803c7e8c98fe75c3f122ef880f 100644 --- a/src/GuiQt/MapSettingsPaletteColorMappingWidget.cxx +++ b/src/GuiQt/MapSettingsPaletteColorMappingWidget.cxx @@ -43,26 +43,29 @@ #undef __MAP_SETTINGS_PALETTE_COLOR_MAPPING_WIDGET_DECLARE__ #include "Brain.h" +#include "CaretColorEnumComboBox.h" #include "CaretMappableDataFile.h" +#include "CaretMappableDataFileAndMapSelectorObject.h" +#include "CopyPaletteColorMappingToFilesDialog.h" #include "CursorDisplayScoped.h" #include "EnumComboBoxTemplate.h" #include "EventCaretMappableDataFilesGet.h" -#include "EventGraphicsUpdateAllWindows.h" -#include "EventSurfaceColoringInvalidate.h" #include "EventManager.h" #include "FastStatistics.h" #include "GuiManager.h" #include "Histogram.h" +#include "MapSettingsColorBarPaletteOptionsWidget.h" #include "MathFunctions.h" #include "NodeAndVoxelColoring.h" #include "NumericTextFormatting.h" #include "Palette.h" #include "PaletteColorMapping.h" #include "PaletteFile.h" +#include "ThresholdingSetMapsDialog.h" #include "VolumeFile.h" -#include "WuQDataEntryDialog.h" #include "WuQWidgetObjectGroup.h" #include "WuQDoubleSlider.h" +#include "WuQDoubleSpinBox.h" #include "WuQFactory.h" #include "WuQMessageBox.h" #include "WuQtUtilities.h" @@ -99,16 +102,12 @@ static const int32_t BIG_NUMBER = 500000000; MapSettingsPaletteColorMappingWidget::MapSettingsPaletteColorMappingWidget(QWidget* parent) : QWidget(parent) { - m_previousCaretMappableDataFile = NULL; - /* * No context menu, it screws things up * but one is used on the histogram plot */ this->setContextMenuPolicy(Qt::NoContextMenu); - this->isHistogramColored = true; - this->caretMappableDataFile = NULL; this->mapFileIndex = -1; @@ -120,46 +119,39 @@ MapSettingsPaletteColorMappingWidget::MapSettingsPaletteColorMappingWidget(QWidg QWidget* histogramWidget = this->createHistogramSection(); QWidget* histogramControlWidget = this->createHistogramControlSection(); - QWidget* dataOptionsWidget = this->createDataOptionsSection(); + m_paletteOptionsWidget = new MapSettingsColorBarPaletteOptionsWidget(); QWidget* normalizationWidget = this->createNormalizationControlSection(); QWidget* paletteWidget = this->createPaletteSection(); QWidget* thresholdWidget = this->createThresholdSection(); + thresholdWidget->setMaximumWidth(paletteWidget->sizeHint().width() + 50); QWidget* leftWidget = new QWidget(); QVBoxLayout* leftLayout = new QVBoxLayout(leftWidget); this->setLayoutSpacingAndMargins(leftLayout); leftLayout->addWidget(thresholdWidget); leftLayout->addWidget(paletteWidget); - leftLayout->addStretch(); QVBoxLayout* dataLayout = new QVBoxLayout(); dataLayout->addWidget(normalizationWidget); - dataLayout->addWidget(dataOptionsWidget); - //dataLayout->addStretch(); - -// QHBoxLayout* histoColorBarLayout = new QHBoxLayout(); -// histoColorBarLayout->addWidget(histogramControlWidget); -// histoColorBarLayout->addStretch(); + dataLayout->addWidget(m_paletteOptionsWidget); QWidget* bottomRightWidget = new QWidget(); QHBoxLayout* bottomRightLayout = new QHBoxLayout(bottomRightWidget); this->setLayoutSpacingAndMargins(bottomRightLayout); -// bottomRightLayout->addLayout(histoColorBarLayout); bottomRightLayout->addWidget(histogramControlWidget); bottomRightLayout->addLayout(dataLayout); bottomRightLayout->addStretch(); bottomRightWidget->setFixedHeight(bottomRightWidget->sizeHint().height()); -// bottomRightWidget->setFixedSize(bottomRightWidget->sizeHint()); QWidget* rightWidget = new QWidget(); QVBoxLayout* rightLayout = new QVBoxLayout(rightWidget); this->setLayoutSpacingAndMargins(rightLayout); rightLayout->addWidget(histogramWidget, 100); rightLayout->addWidget(bottomRightWidget, 0); - rightLayout->addStretch(); + rightWidget->setFixedHeight(leftWidget->sizeHint().height()); QHBoxLayout* layout = new QHBoxLayout(this); this->setLayoutSpacingAndMargins(layout); @@ -202,6 +194,43 @@ MapSettingsPaletteColorMappingWidget::thresholdTypeChanged(int indx) this->applySelections(); } +/** + * Called when set all maps is selected for thresholding + */ +void +MapSettingsPaletteColorMappingWidget::thresholdSetAllMapsToolButtonClicked() +{ + if (this->caretMappableDataFile->getNumberOfMaps() <= 1) { + WuQMessageBox::errorOk(this, + "Data file contains one map (must have more than one map for this option)"); + return; + } + + CaretMappableDataFile* threshFile = this->thresholdMapFileIndexSelector->getModel()->getSelectedFile(); + if (threshFile == NULL) { + WuQMessageBox::errorOk(this, + "No thresholding file is selected"); + return; + } + + const int32_t threshFileNumberOfMaps = threshFile->getNumberOfMaps(); + if (threshFileNumberOfMaps <= 0) { + WuQMessageBox::errorOk(this, + "Thresholding file contains no data"); + return; + } + + ThresholdingSetMapsDialog threshDialog(this->caretMappableDataFile, + threshFile, + (this->thresholdMapFileIndexSelector->getModel()->getSelectedMapIndex()), + this); + if (threshDialog.exec() == ThresholdingSetMapsDialog::Accepted) { + this->updateEditorInternal(this->caretMappableDataFile, + this->mapFileIndex); + this->applySelections(); + } +} + /** * Update the minimum and maximum values for the thresholding controls. */ @@ -363,8 +392,7 @@ MapSettingsPaletteColorMappingWidget::updateAfterThresholdValuesChanged(const fl this->paletteColorMapping->setThresholdMinimum(threshType, lowThreshold); this->paletteColorMapping->setThresholdMaximum(threshType, highThreshold); updateThresholdSection(); - updateHistogramPlot(); - updateColoringAndGraphics(); + this->applySelections(); } /** @@ -461,6 +489,15 @@ MapSettingsPaletteColorMappingWidget::thresholdHighSliderValueChanged(double thr thresholdHigh); } +/** + * Gets called when the threshold map file/index is changed. + */ +void +MapSettingsPaletteColorMappingWidget::thresholdMapFileIndexSelectorChanged() +{ + this->applySelections(); +} + /** * Called when the threshold link check box is toggled. * @@ -495,24 +532,6 @@ MapSettingsPaletteColorMappingWidget::thresholdLinkCheckBoxToggled(bool checked) } } -/** - * Update coloring and graphics - */ -void -MapSettingsPaletteColorMappingWidget::updateColoringAndGraphics() -{ - PaletteFile* paletteFile = GuiManager::get()->getBrain()->getPaletteFile(); - if (this->applyAllMapsCheckBox->isChecked()) { - this->caretMappableDataFile->updateScalarColoringForAllMaps(paletteFile); - } - else { - this->caretMappableDataFile->updateScalarColoringForMap(this->mapFileIndex, - paletteFile); - } - EventManager::get()->sendEvent(EventSurfaceColoringInvalidate().getPointer()); - EventManager::get()->sendEvent(EventGraphicsUpdateAllWindows().getPointer()); -} - /** * Create the threshold section of the dialog. * @return @@ -527,7 +546,7 @@ MapSettingsPaletteColorMappingWidget::createThresholdSection() /* * Threshold types on/off */ - QLabel* thresholdTypeLabel = new QLabel("Type"); + QLabel* thresholdTypeLabel = new QLabel("Source"); std::vector thresholdTypes; PaletteThresholdTypeEnum::getAllEnums(thresholdTypes); const int32_t numThresholdTypes = static_cast(thresholdTypes.size()); @@ -542,6 +561,12 @@ MapSettingsPaletteColorMappingWidget::createThresholdSection() QObject::connect(this->thresholdTypeComboBox, SIGNAL(currentIndexChanged(int)), this, SLOT(thresholdTypeChanged(int))); + this->thresholdSetAllMapsToolButton = new QToolButton(); + this->thresholdSetAllMapsToolButton->setText("Set All Maps..."); + this->thresholdSetAllMapsToolButton->setToolTip("Setup thresholding for all maps in the data file"); + QObject::connect(this->thresholdSetAllMapsToolButton, &QToolButton::clicked, + this, &MapSettingsPaletteColorMappingWidget::thresholdSetAllMapsToolButtonClicked); + QLabel* thresholdRangeLabel = new QLabel("Range"); this->thresholdRangeModeComboBox = new EnumComboBoxTemplate(this); QObject::connect(this->thresholdRangeModeComboBox, SIGNAL(itemActivated()), @@ -553,6 +578,34 @@ MapSettingsPaletteColorMappingWidget::createThresholdSection() " Unlimited: Range is +/- infinity."); this->thresholdRangeModeComboBox->getWidget()->setToolTip(WuQtUtilities::createWordWrappedToolTipText(rangeModeToolTip)); + /* + * Threshold file and map selector + */ + this->thresholdMapFileIndexSelector = new CaretMappableDataFileAndMapSelectorObject(CaretMappableDataFileAndMapSelectorObject::OPTION_SHOW_MAP_INDEX_SPIN_BOX, + this); + QObject::connect(this->thresholdMapFileIndexSelector, &CaretMappableDataFileAndMapSelectorObject::selectionWasPerformed, + this, &MapSettingsPaletteColorMappingWidget::thresholdMapFileIndexSelectorChanged); + QWidget* threshFileComboBox(0); + QWidget* threshMapIndexSpinBox(0); + QWidget* threshMapNameComboBox(0); + this->thresholdMapFileIndexSelector->getWidgetsForAddingToLayout(threshFileComboBox, + threshMapIndexSpinBox, + threshMapNameComboBox); + this->thresholdFileWidget = new QWidget(); + QGridLayout* threshFileLayout = new QGridLayout(this->thresholdFileWidget); + QLabel* threshFileLabel = new QLabel("File"); + QLabel* threshMapLabel = new QLabel("Map"); + threshFileLayout->setContentsMargins(0, 0, 0, 0); + threshFileLayout->setSpacing(4); + threshFileLayout->setColumnStretch(0, 0); + threshFileLayout->setColumnStretch(1, 0); + threshFileLayout->setColumnStretch(2, 100); + threshFileLayout->addWidget(threshFileLabel, 0, 0); + threshFileLayout->addWidget(threshFileComboBox, 0, 1, 1, 2); + threshFileLayout->addWidget(threshMapLabel, 1, 0); + threshFileLayout->addWidget(threshMapIndexSpinBox, 1, 1); + threshFileLayout->addWidget(threshMapNameComboBox, 1, 2); + /* * Linking of low/high thresholds */ @@ -664,37 +717,43 @@ MapSettingsPaletteColorMappingWidget::createThresholdSection() QObject::connect(thresholdShowButtonGroup, SIGNAL(buttonClicked(int)), this, SLOT(applyAndUpdate())); - QWidget* thresholdAdjustmentWidget = new QWidget(); + QHBoxLayout* rangeLayout = new QHBoxLayout(); + rangeLayout->addWidget(thresholdRangeLabel); + rangeLayout->addWidget(this->thresholdRangeModeComboBox->getWidget()); + + this->thresholdAdjustmentWidget = new QWidget(); QGridLayout* thresholdAdjustmentLayout = new QGridLayout(thresholdAdjustmentWidget); this->setLayoutSpacingAndMargins(thresholdAdjustmentLayout); thresholdAdjustmentLayout->setColumnStretch(0, 0); thresholdAdjustmentLayout->setColumnStretch(1, 0); thresholdAdjustmentLayout->setColumnStretch(2, 100); - thresholdAdjustmentLayout->setColumnStretch(3, 0); + thresholdAdjustmentLayout->setColumnStretch(3, 100); + thresholdAdjustmentLayout->setColumnStretch(4, 0); thresholdAdjustmentLayout->addLayout(linkLayout, 0, 0, 2, 1, Qt::AlignCenter); thresholdAdjustmentLayout->addWidget(thresholdHighLabel, 0, 1); - thresholdAdjustmentLayout->addWidget(this->thresholdHighSlider->getWidget(), 0, 2); - thresholdAdjustmentLayout->addWidget(this->thresholdHighSpinBox, 0, 3); + thresholdAdjustmentLayout->addWidget(this->thresholdHighSlider->getWidget(), 0, 2, 1, 2); + thresholdAdjustmentLayout->addWidget(this->thresholdHighSpinBox, 0, 4); thresholdAdjustmentLayout->addWidget(thresholdLowLabel, 1, 1); - thresholdAdjustmentLayout->addWidget(this->thresholdLowSlider->getWidget(), 1, 2); - thresholdAdjustmentLayout->addWidget(this->thresholdLowSpinBox, 1, 3); - thresholdAdjustmentLayout->addWidget(this->thresholdShowInsideRadioButton, 2, 0, 1, 4, Qt::AlignLeft); - thresholdAdjustmentLayout->addWidget(this->thresholdShowOutsideRadioButton, 3, 0, 1, 4, Qt::AlignLeft); + thresholdAdjustmentLayout->addWidget(this->thresholdLowSlider->getWidget(), 1, 2, 1, 2); + thresholdAdjustmentLayout->addWidget(this->thresholdLowSpinBox, 1, 4); + thresholdAdjustmentLayout->addWidget(this->thresholdShowInsideRadioButton, 2, 0, 1, 3, Qt::AlignLeft); + thresholdAdjustmentLayout->addWidget(this->thresholdShowOutsideRadioButton, 3, 0, 1, 3, Qt::AlignLeft); + thresholdAdjustmentLayout->addLayout(rangeLayout, 2, 3, 2, 2, Qt::AlignRight); thresholdAdjustmentWidget->setFixedHeight(thresholdAdjustmentWidget->sizeHint().height()); QWidget* topWidget = new QWidget(); QHBoxLayout* topLayout = new QHBoxLayout(topWidget); - this->setLayoutSpacingAndMargins(topLayout); + topLayout->setContentsMargins(0, 0, 0, 0); topLayout->addWidget(thresholdTypeLabel); topLayout->addWidget(this->thresholdTypeComboBox); - topLayout->addWidget(thresholdRangeLabel); - topLayout->addWidget(this->thresholdRangeModeComboBox->getWidget()); + topLayout->addWidget(this->thresholdSetAllMapsToolButton); topLayout->addStretch(); QGroupBox* thresholdGroupBox = new QGroupBox("Threshold"); QVBoxLayout* layout = new QVBoxLayout(thresholdGroupBox); this->setLayoutSpacingAndMargins(layout); layout->addWidget(topWidget, 0, Qt::AlignLeft); + layout->addWidget(this->thresholdFileWidget); layout->addWidget(WuQtUtilities::createHorizontalLineWidget()); layout->addWidget(thresholdAdjustmentWidget); thresholdGroupBox->setFixedHeight(thresholdGroupBox->sizeHint().height()); @@ -703,14 +762,18 @@ MapSettingsPaletteColorMappingWidget::createThresholdSection() this->thresholdAdjustmentWidgetGroup->add(this->thresholdLinkCheckBox); this->thresholdAdjustmentWidgetGroup->add(thresholdLowLabel); this->thresholdAdjustmentWidgetGroup->add(thresholdHighLabel); - this->thresholdAdjustmentWidgetGroup->add(this->thresholdLowSlider); - this->thresholdAdjustmentWidgetGroup->add(this->thresholdHighSlider); + this->thresholdAdjustmentWidgetGroup->add(this->thresholdLowSlider->getWidget()); + this->thresholdAdjustmentWidgetGroup->add(this->thresholdHighSlider->getWidget()); this->thresholdAdjustmentWidgetGroup->add(this->thresholdLowSpinBox); this->thresholdAdjustmentWidgetGroup->add(this->thresholdHighSpinBox); this->thresholdAdjustmentWidgetGroup->add(this->thresholdShowInsideRadioButton); this->thresholdAdjustmentWidgetGroup->add(this->thresholdShowOutsideRadioButton); this->thresholdAdjustmentWidgetGroup->add(thresholdRangeLabel); this->thresholdAdjustmentWidgetGroup->add(this->thresholdRangeModeComboBox->getWidget()); + this->thresholdAdjustmentWidgetGroup->add(threshFileComboBox); + this->thresholdAdjustmentWidgetGroup->add(threshMapIndexSpinBox); + this->thresholdAdjustmentWidgetGroup->add(threshMapNameComboBox); + this->thresholdAdjustmentWidgetGroup->add(this->thresholdSetAllMapsToolButton); return thresholdGroupBox; } @@ -727,15 +790,6 @@ MapSettingsPaletteColorMappingWidget::thresholdRangeModeChanged() applySelections(); } -/** - * Called when a histogram control is changed. - */ -void -MapSettingsPaletteColorMappingWidget::histogramControlChanged() -{ - this->updateWidget(); -} - /** * Create the statistics section * @return the statistics section widget. @@ -743,44 +797,101 @@ MapSettingsPaletteColorMappingWidget::histogramControlChanged() QWidget* MapSettingsPaletteColorMappingWidget::createHistogramControlSection() { - /* - * Control section - */ - this->histogramAllRadioButton = new QRadioButton("All"); - WuQtUtilities::setToolTipAndStatusTip(this->histogramAllRadioButton, - "Displays all map data in the histogram"); - this->histogramMatchPaletteRadioButton = new QRadioButton("Match\nPalette"); - WuQtUtilities::setToolTipAndStatusTip(this->histogramMatchPaletteRadioButton, - "Use the palette mapping selections"); - - this->histogramAllRadioButton->setChecked(true); - - QButtonGroup* buttGroup = new QButtonGroup(this); - buttGroup->addButton(this->histogramAllRadioButton); - buttGroup->addButton(this->histogramMatchPaletteRadioButton); - QObject::connect(buttGroup, SIGNAL(buttonClicked(int)), - this, SLOT(histogramControlChanged())); - - this->histogramUsePaletteColors = new QCheckBox("Colorize"); - WuQtUtilities::setToolTipAndStatusTip(this->histogramUsePaletteColors, - "If checked, histogram colors match colors mapped to data"); - this->histogramUsePaletteColors->setChecked(true); - QObject::connect(this->histogramUsePaletteColors, SIGNAL(toggled(bool)), - this, SLOT(histogramControlChanged())); + QLabel* showLabel = new QLabel("Show "); + m_histogramBarsVisibleCheckBox = new QCheckBox("Bars"); + QObject::connect(m_histogramBarsVisibleCheckBox, &QCheckBox::clicked, + [=] { this->applyAndUpdate(); } ); + + m_histogramEnvelopeVisibleCheckBox = new QCheckBox("Envelope"); + QObject::connect(m_histogramEnvelopeVisibleCheckBox, &QCheckBox::clicked, + [=] { this->applyAndUpdate(); }); + + QHBoxLayout* showLayout = new QHBoxLayout(); + showLayout->addWidget(m_histogramBarsVisibleCheckBox); + showLayout->addStretch(); + showLayout->addWidget(m_histogramEnvelopeVisibleCheckBox); + + QLabel* horizRangeLabel = new QLabel("Range"); + m_histogramHorizontalRangeComboBox = new EnumComboBoxTemplate(this); + QObject::connect(this->m_histogramHorizontalRangeComboBox, SIGNAL(itemActivated()), + this, SLOT(applyAndUpdate())); + this->m_histogramHorizontalRangeComboBox->setup(); + this->m_histogramHorizontalRangeComboBox->getWidget()->setToolTip("Horizontal range of histogram"); + + QIcon colorBarIcon; + const bool colorBarIconValid = WuQtUtilities::loadIcon(":/LayersPanel/colorbar.png", + colorBarIcon); + + QLabel* barsColorLabel = new QLabel("Bars Color"); + if (colorBarIconValid) { + m_histogramBarsColorComboBox = new CaretColorEnumComboBox("Palette", + colorBarIcon, + this); + } + else { + m_histogramBarsColorComboBox = new CaretColorEnumComboBox("Palette", + this); + } + WuQtUtilities::setToolTipAndStatusTip(m_histogramBarsColorComboBox->getWidget(), + "Set histogram bars coloring"); + QObject::connect(m_histogramBarsColorComboBox, SIGNAL(colorSelected(const CaretColorEnum::Enum)), + this, SLOT(applyAndUpdate())); + + QLabel* envelopeColorLabel = new QLabel("Envelope"); + if (colorBarIconValid) { + m_histogramEnvelopeColorComboBox = new CaretColorEnumComboBox("Palette", + colorBarIcon, + this); + } + else { + m_histogramEnvelopeColorComboBox = new CaretColorEnumComboBox("Palette", + this); + } + WuQtUtilities::setToolTipAndStatusTip(m_histogramEnvelopeColorComboBox->getWidget(), + "Set histogram envelope coloring"); + QObject::connect(m_histogramEnvelopeColorComboBox, SIGNAL(colorSelected(const CaretColorEnum::Enum)), + this, SLOT(applyAndUpdate())); + + QLabel* envelopeLineWidthLabel = new QLabel("Envelope Width"); + m_histogramEnvelopeLineWidthPercentageSpinBox = new WuQDoubleSpinBox(this); + m_histogramEnvelopeLineWidthPercentageSpinBox->setRangePercentage(0.0, 100.0); + QObject::connect(m_histogramEnvelopeLineWidthPercentageSpinBox, static_cast(&WuQDoubleSpinBox::valueChanged), + [=](double) { this->applyAndUpdate(); }); + + const int32_t maxBuckets = 100000; + QLabel* bucketsLabel = new QLabel("Buckets"); + m_histogramBucketsSpinBox = WuQFactory::newSpinBoxWithMinMaxStep(1, maxBuckets, 1); + QObject::connect(m_histogramBucketsSpinBox, SIGNAL(valueChanged(int)), + this, SLOT(applyAndUpdate())); QWidget* controlWidget = new QWidget(); QGridLayout* controlLayout = new QGridLayout(controlWidget); this->setLayoutSpacingAndMargins(controlLayout); - controlLayout->addWidget(this->histogramAllRadioButton); - controlLayout->addWidget(this->histogramMatchPaletteRadioButton); - controlLayout->addWidget(WuQtUtilities::createHorizontalLineWidget()); - controlLayout->addWidget(this->histogramUsePaletteColors); + int rowIndex = 0; + controlLayout->addWidget(showLabel, rowIndex, 0); + controlLayout->addLayout(showLayout, rowIndex, 1); + rowIndex++; + controlLayout->addWidget(barsColorLabel, rowIndex, 0); + controlLayout->addWidget(m_histogramBarsColorComboBox->getWidget(), rowIndex, 1); + rowIndex++; + controlLayout->addWidget(envelopeColorLabel, rowIndex, 0); + controlLayout->addWidget(m_histogramEnvelopeColorComboBox->getWidget(), rowIndex, 1); + rowIndex++; + controlLayout->addWidget(envelopeLineWidthLabel, rowIndex, 0); + controlLayout->addWidget(m_histogramEnvelopeLineWidthPercentageSpinBox->getWidget(), rowIndex, 1); + rowIndex++; + controlLayout->addWidget(horizRangeLabel, rowIndex, 0); + controlLayout->addWidget(m_histogramHorizontalRangeComboBox->getWidget(), rowIndex, 1); + rowIndex++; + controlLayout->addWidget(bucketsLabel, rowIndex, 0); + controlLayout->addWidget(m_histogramBucketsSpinBox, rowIndex, 1); + rowIndex++; controlWidget->setFixedSize(controlWidget->sizeHint()); /* * Statistics */ - const AString blankText(""); //" "); + const AString blankText(""); this->statisticsMinimumValueLabel = new QLabel(blankText); this->statisticsMinimumValueLabel->setAlignment(Qt::AlignRight); this->statisticsMaximumValueLabel = new QLabel(blankText); @@ -804,7 +915,7 @@ MapSettingsPaletteColorMappingWidget::createHistogramControlSection() statisticsLayout->addWidget(this->statisticsMaximumValueLabel, 2, 1); statisticsLayout->addWidget(new QLabel("Min"), 3, 0); statisticsLayout->addWidget(this->statisticsMinimumValueLabel, 3, 1); - //statisticsWidget->setFixedHeight(statisticsWidget->sizeHint().height()); + statisticsLayout->setRowStretch(statisticsLayout->rowCount(), 100); QGroupBox* groupBox = new QGroupBox("Histogram"); @@ -813,7 +924,6 @@ MapSettingsPaletteColorMappingWidget::createHistogramControlSection() layout->addWidget(controlWidget); layout->addWidget(WuQtUtilities::createVerticalLineWidget()); layout->addWidget(statisticsWidget); - //groupBox->setFixedHeight(groupBox->sizeHint().height()); groupBox->setSizePolicy(QSizePolicy::Minimum, QSizePolicy::Fixed); return groupBox; @@ -827,7 +937,6 @@ MapSettingsPaletteColorMappingWidget::createHistogramControlSection() QWidget* MapSettingsPaletteColorMappingWidget::createHistogramSection() { - //this->thresholdPlot = new QwtPlot(); this->thresholdPlot = new WuQwtPlot(); QObject::connect(this->thresholdPlot, SIGNAL(contextMenuDisplay(QContextMenuEvent*, float, @@ -966,6 +1075,7 @@ MapSettingsPaletteColorMappingWidget::scaleAutoPercentageNegativeMaximumValueCha this->scaleAutoPercentageNegativeMinimumSpinBox->blockSignals(false); } applySelections(); + updatePaletteMappedToDataValueLabels(); } /** @@ -986,6 +1096,7 @@ MapSettingsPaletteColorMappingWidget::scaleAutoPercentageNegativeMinimumValueCha this->scaleAutoPercentageNegativeMaximumSpinBox->blockSignals(false); } applySelections(); + updatePaletteMappedToDataValueLabels(); } @@ -1007,6 +1118,7 @@ MapSettingsPaletteColorMappingWidget::scaleAutoPercentagePositiveMinimumValueCha this->scaleAutoPercentagePositiveMaximumSpinBox->blockSignals(false); } applySelections(); + updatePaletteMappedToDataValueLabels(); } @@ -1028,6 +1140,7 @@ MapSettingsPaletteColorMappingWidget::scaleAutoPercentagePositiveMaximumValueCha this->scaleAutoPercentagePositiveMinimumSpinBox->blockSignals(false); } applySelections(); + updatePaletteMappedToDataValueLabels(); } /** @@ -1048,6 +1161,7 @@ MapSettingsPaletteColorMappingWidget::scaleAutoAbsolutePercentageMinimumValueCha this->scaleAutoAbsolutePercentageMaximumSpinBox->blockSignals(false); } applySelections(); + updatePaletteMappedToDataValueLabels(); } /** @@ -1068,6 +1182,7 @@ MapSettingsPaletteColorMappingWidget::scaleAutoAbsolutePercentageMaximumValueCha this->scaleAutoAbsolutePercentageMinimumSpinBox->blockSignals(false); } applySelections(); + updatePaletteMappedToDataValueLabels(); } /** @@ -1088,6 +1203,7 @@ MapSettingsPaletteColorMappingWidget::scaleFixedNegativeMaximumValueChanged(doub this->scaleFixedNegativeMinimumSpinBox->blockSignals(false); } applySelections(); + updatePaletteMappedToDataValueLabels(); } /** @@ -1108,6 +1224,7 @@ MapSettingsPaletteColorMappingWidget::scaleFixedNegativeMinimumValueChanged(doub this->scaleFixedNegativeMaximumSpinBox->blockSignals(false); } applySelections(); + updatePaletteMappedToDataValueLabels(); } /** @@ -1128,6 +1245,7 @@ MapSettingsPaletteColorMappingWidget::scaleFixedPositiveMinimumValueChanged(doub this->scaleFixedPositiveMaximumSpinBox->blockSignals(false); } applySelections(); + updatePaletteMappedToDataValueLabels(); } /** @@ -1148,6 +1266,7 @@ MapSettingsPaletteColorMappingWidget::scaleFixedPositiveMaximumValueChanged(doub this->scaleFixedPositiveMinimumSpinBox->blockSignals(false); } applySelections(); + updatePaletteMappedToDataValueLabels(); } /** @@ -1161,6 +1280,7 @@ MapSettingsPaletteColorMappingWidget::createPaletteSection() /* * Selection */ + QLabel* paletteNameLabel = new QLabel("Name"); this->paletteNameComboBox = new QComboBox(); WuQtUtilities::setToolTipAndStatusTip(this->paletteNameComboBox, "Select palette for coloring map data"); @@ -1170,28 +1290,54 @@ MapSettingsPaletteColorMappingWidget::createPaletteSection() /* * Interpolate Colors */ - this->interpolateColorsCheckBox = new QCheckBox("Interpolate Colors"); + this->interpolateColorsCheckBox = new QCheckBox("Interpolate\nColors"); WuQtUtilities::setToolTipAndStatusTip(this->interpolateColorsCheckBox, "Smooth colors for data between palette colors"); this->paletteWidgetGroup->add(this->interpolateColorsCheckBox); QObject::connect(this->interpolateColorsCheckBox, SIGNAL(toggled(bool)), this, SLOT(applySelections())); - + + /* + * Invert palette checkbox + */ + QLabel* invertLabel = new QLabel("Invert"); + this->invertPaletteModeComboBox = new EnumComboBoxTemplate(this); + QObject::connect(this->invertPaletteModeComboBox, SIGNAL(itemActivated()), + this, SLOT(applySelections())); + this->invertPaletteModeComboBox->setup(); + const AString invertModeToolTip = ("" + "Controls inversion of Palette:

    " + " " + PaletteInvertModeEnum::toGuiName(PaletteInvertModeEnum::POSITIVE_WITH_NEGATIVE) + + " - Positive and negative sections of the palette are swapped

    " + " " + PaletteInvertModeEnum::toGuiName(PaletteInvertModeEnum::POSITIVE_NEGATIVE_SEPARATE) + + " - Swaps the negative range within the negative range AND " + " swaps the positive range within the positive range" + ""); + this->invertPaletteModeComboBox->getWidget()->setToolTip(invertModeToolTip); + this->paletteWidgetGroup->add(this->invertPaletteModeComboBox->getComboBox()); + QWidget* paletteSelectionWidget = new QWidget(); - QVBoxLayout* paletteSelectionLayout = new QVBoxLayout(paletteSelectionWidget); + QGridLayout* paletteSelectionLayout = new QGridLayout(paletteSelectionWidget); + paletteSelectionLayout->setColumnStretch(0, 0); + paletteSelectionLayout->setColumnStretch(1, 0); + paletteSelectionLayout->setColumnStretch(2, 100); this->setLayoutSpacingAndMargins(paletteSelectionLayout); - paletteSelectionLayout->addWidget(this->paletteNameComboBox); - paletteSelectionLayout->addWidget(this->interpolateColorsCheckBox); + paletteSelectionLayout->addWidget(paletteNameLabel, 0, 0); + paletteSelectionLayout->addWidget(this->paletteNameComboBox, 0, 1); + paletteSelectionLayout->addWidget(this->interpolateColorsCheckBox, 0, 2, 2, 1, Qt::AlignCenter); + paletteSelectionLayout->addWidget(invertLabel, 1, 0); + paletteSelectionLayout->addWidget(this->invertPaletteModeComboBox->getComboBox(), 1, 1); + paletteSelectionWidget->setFixedHeight(paletteSelectionWidget->sizeHint().height()); /* * Color Mapping */ - this->scaleAutoRadioButton = new QRadioButton("Full"); //Auto Scale"); + this->scaleAutoRadioButton = new QRadioButton("Full"); this->scaleAutoAbsolutePercentageRadioButton = new QRadioButton("Abs Pct"); - this->scaleAutoPercentageRadioButton = new QRadioButton("Percent"); //"Auto Scale Percentage"); - this->scaleFixedRadioButton = new QRadioButton("Fixed"); //"Fixed Scale"); + this->scaleAutoPercentageRadioButton = new QRadioButton("Percent"); + this->scaleFixedRadioButton = new QRadioButton("Fixed"); WuQtUtilities::setToolTipAndStatusTip(this->scaleAutoRadioButton, "Map (most negative, zero, most positive) data values to (-1, 0, 0, 1) in palette"); @@ -1211,10 +1357,10 @@ MapSettingsPaletteColorMappingWidget::createPaletteSection() this, SLOT(applyAndUpdate())); /* - * Spin box width + * Spin box width (fixed may have much larger data values) */ const int percentSpinBoxWidth = 75; - const int fixedSpinBoxWidth = 100; // fixed may have much larger data values + const int fixedSpinBoxWidth = 82; /* * Percentage mapping */ @@ -1306,7 +1452,7 @@ MapSettingsPaletteColorMappingWidget::createPaletteSection() WuQFactory::newDoubleSpinBoxWithMinMaxStepDecimalsSignalDouble(-BIG_NUMBER, 0.0, 1.0, - 3, + 2, this, SLOT(scaleFixedNegativeMaximumValueChanged(double))); @@ -1319,7 +1465,7 @@ MapSettingsPaletteColorMappingWidget::createPaletteSection() WuQFactory::newDoubleSpinBoxWithMinMaxStepDecimalsSignalDouble(-BIG_NUMBER, 0.0, 1.0, - 3, + 2, this, SLOT(scaleFixedNegativeMinimumValueChanged(double))); @@ -1332,7 +1478,7 @@ MapSettingsPaletteColorMappingWidget::createPaletteSection() WuQFactory::newDoubleSpinBoxWithMinMaxStepDecimalsSignalDouble(0.0, BIG_NUMBER, 1.0, - 3, + 2, this, SLOT(scaleFixedPositiveMinimumValueChanged(double))); @@ -1345,7 +1491,7 @@ MapSettingsPaletteColorMappingWidget::createPaletteSection() WuQFactory::newDoubleSpinBoxWithMinMaxStepDecimalsSignalDouble(0.0, BIG_NUMBER, 1.0, - 3, + 2, this, SLOT(scaleFixedPositiveMaximumValueChanged(double))); @@ -1354,16 +1500,31 @@ MapSettingsPaletteColorMappingWidget::createPaletteSection() this->paletteWidgetGroup->add(this->scaleFixedPositiveMaximumSpinBox); this->scaleFixedPositiveMaximumSpinBox->setFixedWidth(fixedSpinBoxWidth); + /* + * Labels for values at palette percentages + */ + const AString bigText(7, QChar(' ')); + this->scaleNegativeMaximumValueLabel = new QLabel(bigText); + this->scaleNegativeMinimumValueLabel = new QLabel(bigText); + this->scalePositiveMinimumValueLabel = new QLabel(bigText); + this->scalePositiveMaximumValueLabel = new QLabel(bigText); + + /* + * Layout widgets + */ QWidget* colorMappingWidget = new QWidget(); QGridLayout* colorMappingLayout = new QGridLayout(colorMappingWidget); colorMappingLayout->setColumnStretch(0, 0); colorMappingLayout->setColumnStretch(1, 100); colorMappingLayout->setColumnStretch(2, 100); + colorMappingLayout->setColumnStretch(3, 100); + colorMappingLayout->setColumnStretch(4, 100); this->setLayoutSpacingAndMargins(colorMappingLayout); colorMappingLayout->addWidget(this->scaleAutoRadioButton, 0, 0, Qt::AlignHCenter); colorMappingLayout->addWidget(this->scaleAutoAbsolutePercentageRadioButton, 0, 1, Qt::AlignHCenter); colorMappingLayout->addWidget(this->scaleAutoPercentageRadioButton, 0, 2, Qt::AlignHCenter); colorMappingLayout->addWidget(this->scaleFixedRadioButton, 0, 3, Qt::AlignHCenter); + colorMappingLayout->addWidget(new QLabel("Data"), 0, 4, Qt::AlignRight); colorMappingLayout->addWidget(new QLabel("Pos Max"), 1, 0, Qt::AlignRight); colorMappingLayout->addWidget(new QLabel("Pos Min"), 2, 0, Qt::AlignRight); colorMappingLayout->addWidget(new QLabel("Neg Min"), 3, 0, Qt::AlignRight); @@ -1378,6 +1539,10 @@ MapSettingsPaletteColorMappingWidget::createPaletteSection() colorMappingLayout->addWidget(this->scaleFixedPositiveMinimumSpinBox, 2, 3); colorMappingLayout->addWidget(this->scaleFixedNegativeMinimumSpinBox, 3, 3); colorMappingLayout->addWidget(this->scaleFixedNegativeMaximumSpinBox, 4, 3); + colorMappingLayout->addWidget(this->scalePositiveMaximumValueLabel, 1, 4, Qt::AlignRight); + colorMappingLayout->addWidget(this->scalePositiveMinimumValueLabel, 2, 4, Qt::AlignRight); + colorMappingLayout->addWidget(this->scaleNegativeMinimumValueLabel, 3, 4, Qt::AlignRight); + colorMappingLayout->addWidget(this->scaleNegativeMaximumValueLabel, 4, 4, Qt::AlignRight); /* * Display Mode @@ -1400,10 +1565,8 @@ MapSettingsPaletteColorMappingWidget::createPaletteSection() WuQtUtilities::setToolTipAndStatusTip(this->displayModeZeroCheckBox, "Enable/Disable the display of zero data.\n" "A value in the range [" - // JWH 24 April 2015+ AString::number(NodeAndVoxelColoring::SMALL_NEGATIVE, 'f', 6) + AString::number(PaletteColorMapping::SMALL_NEGATIVE, 'f', 6) + ", " - // JWH 24 April 2015+ AString::number(NodeAndVoxelColoring::SMALL_POSITIVE, 'f', 6) + AString::number(PaletteColorMapping::SMALL_POSITIVE, 'f', 6) + "]\n" "is considered to be zero."); @@ -1430,7 +1593,7 @@ MapSettingsPaletteColorMappingWidget::createPaletteSection() paletteLayout->addWidget(colorMappingWidget); paletteLayout->addWidget(WuQtUtilities::createHorizontalLineWidget()); paletteLayout->addWidget(displayModeWidget); - paletteGroupBox->setFixedHeight(paletteGroupBox->sizeHint().height()); + paletteGroupBox->setSizePolicy(QSizePolicy::Fixed, QSizePolicy::Fixed); return paletteGroupBox; } @@ -1448,16 +1611,7 @@ void MapSettingsPaletteColorMappingWidget::updateEditor(CaretMappableDataFile* caretMappableDataFile, const int32_t mapIndex) { - const bool palettesEqualFlag = caretMappableDataFile->isPaletteColorMappingEqualForAllMaps(); updateEditorInternal(caretMappableDataFile, mapIndex); - - if (m_previousCaretMappableDataFile != caretMappableDataFile) { - this->applyAllMapsCheckBox->blockSignals(true); - this->applyAllMapsCheckBox->setChecked(palettesEqualFlag); - this->applyAllMapsCheckBox->blockSignals(false); - } - - m_previousCaretMappableDataFile = caretMappableDataFile; } /** @@ -1476,6 +1630,9 @@ MapSettingsPaletteColorMappingWidget::updateThresholdSection() break; } } + + CaretAssert(this->caretMappableDataFile); + this->thresholdMapFileIndexSelector->updateFileAndMapSelector(this->caretMappableDataFile->getMapThresholdFileSelectionModel(this->mapFileIndex)); const bool enableThresholdControls = (this->paletteColorMapping->getThresholdType() != PaletteThresholdTypeEnum::THRESHOLD_TYPE_OFF); this->thresholdAdjustmentWidgetGroup->setEnabled(enableThresholdControls); @@ -1511,7 +1668,86 @@ MapSettingsPaletteColorMappingWidget::updateThresholdSection() this->thresholdLinkCheckBox->setChecked(this->paletteColorMapping->isThresholdNegMinPosMaxLinked()); + this->thresholdAdjustmentWidget->setEnabled(paletteColorMapping->getThresholdType() == PaletteThresholdTypeEnum::THRESHOLD_TYPE_NORMAL); + this->thresholdFileWidget->setEnabled(paletteColorMapping->getThresholdType() == PaletteThresholdTypeEnum::THRESHOLD_TYPE_FILE); this->thresholdWidgetGroup->blockAllSignals(false); + + bool enableSetAllMapsButtonFlag = false; + if (this->paletteColorMapping->getThresholdType() == PaletteThresholdTypeEnum::THRESHOLD_TYPE_FILE) { + if (this->caretMappableDataFile->getNumberOfMaps() > 1) { + const CaretMappableDataFile* threshFile = this->caretMappableDataFile->getMapThresholdFileSelectionModel(this->mapFileIndex)->getSelectedFile(); + if (threshFile != NULL) { + if (threshFile->getNumberOfMaps() > 0) { + enableSetAllMapsButtonFlag = true; + } + } + } + } + this->thresholdSetAllMapsToolButton->setEnabled(enableSetAllMapsButtonFlag); +} + +/** + * Update the labels that list values that correspond to the palette + * mapping values (percent neg/pos min/max, full, etc). + */ +void +MapSettingsPaletteColorMappingWidget::updatePaletteMappedToDataValueLabels() +{ + if (this->caretMappableDataFile == NULL) { + return; + } + else if (this->mapFileIndex < 0) { + return; + } + + FastStatistics* statistics = NULL; + switch (this->caretMappableDataFile->getPaletteNormalizationMode()) { + case PaletteNormalizationModeEnum::NORMALIZATION_ALL_MAP_DATA: + statistics = const_cast(this->caretMappableDataFile->getFileFastStatistics()); + break; + case PaletteNormalizationModeEnum::NORMALIZATION_SELECTED_MAP_DATA: + statistics = const_cast(this->caretMappableDataFile->getMapFastStatistics(this->mapFileIndex)); + break; + } + + double posMaxLabelValue = 0.0; + double posMinLabelValue = 0.0; + double negMinLabelValue = 0.0; + double negMaxLabelValue = 0.0; + + if (statistics != NULL) { + switch (this->paletteColorMapping->getScaleMode()) { + case PaletteScaleModeEnum::MODE_AUTO_SCALE: + posMaxLabelValue = statistics->getMostPositiveValue(); + posMinLabelValue = 0.0; + negMinLabelValue = 0.0; + negMaxLabelValue = statistics->getMostNegativeValue(); + break; + case PaletteScaleModeEnum::MODE_AUTO_SCALE_ABSOLUTE_PERCENTAGE: + posMaxLabelValue = statistics->getApproxPositivePercentile(this->paletteColorMapping->getAutoScaleAbsolutePercentageMaximum()); + posMinLabelValue = statistics->getApproxPositivePercentile(this->paletteColorMapping->getAutoScaleAbsolutePercentageMinimum()); + negMinLabelValue = -posMinLabelValue; + negMaxLabelValue = -posMaxLabelValue; + break; + case PaletteScaleModeEnum::MODE_AUTO_SCALE_PERCENTAGE: + posMaxLabelValue = statistics->getApproxPositivePercentile(this->paletteColorMapping->getAutoScalePercentagePositiveMaximum()); + posMinLabelValue = statistics->getApproxPositivePercentile(this->paletteColorMapping->getAutoScalePercentagePositiveMinimum()); + negMinLabelValue = statistics->getApproxNegativePercentile(this->paletteColorMapping->getAutoScalePercentageNegativeMinimum()); + negMaxLabelValue = statistics->getApproxNegativePercentile(this->paletteColorMapping->getAutoScalePercentageNegativeMaximum()); + break; + case PaletteScaleModeEnum::MODE_USER_SCALE: + posMaxLabelValue = this->paletteColorMapping->getUserScalePositiveMaximum(); + posMinLabelValue = this->paletteColorMapping->getUserScalePositiveMinimum(); + negMinLabelValue = this->paletteColorMapping->getUserScaleNegativeMinimum(); + negMaxLabelValue = this->paletteColorMapping->getUserScaleNegativeMaximum(); + break; + } + } + + this->scalePositiveMaximumValueLabel->setText(QString::number(posMaxLabelValue, 'f', 2)); + this->scalePositiveMinimumValueLabel->setText(QString::number(posMinLabelValue, 'f', 2)); + this->scaleNegativeMinimumValueLabel->setText(QString::number(negMinLabelValue, 'f', 2)); + this->scaleNegativeMaximumValueLabel->setText(QString::number(negMaxLabelValue, 'f', 2)); } /** @@ -1621,12 +1857,33 @@ MapSettingsPaletteColorMappingWidget::updateEditorInternal(CaretMappableDataFile this->displayModeNegativeCheckBox->setChecked(this->paletteColorMapping->isDisplayNegativeDataFlag()); this->interpolateColorsCheckBox->setChecked(this->paletteColorMapping->isInterpolatePaletteFlag()); + this->invertPaletteModeComboBox->setSelectedItem(this->paletteColorMapping->getInvertedMode()); + m_histogramBarsColorComboBox->setSelectedColor(this->paletteColorMapping->getHistogramBarsColor()); + m_histogramEnvelopeColorComboBox->setSelectedColor(this->paletteColorMapping->getHistogramEnvelopeColor()); + m_histogramEnvelopeLineWidthPercentageSpinBox->setValue(this->paletteColorMapping->getHistogramEnvelopeLineWidthPercentage()); + m_histogramBarsVisibleCheckBox->setChecked(this->paletteColorMapping->isHistogramBarsVisible()); + m_histogramEnvelopeVisibleCheckBox->setChecked(this->paletteColorMapping->isHistogramEnvelopeVisible()); + + m_histogramHorizontalRangeComboBox->setSelectedItem(this->paletteColorMapping->getHistogramRangeMode()); + + updatePaletteMappedToDataValueLabels(); updateThresholdSection(); float minValue = 0.0; float maxValue = 0.0; + m_histogramBucketsSpinBox->blockSignals(true); + switch (this->caretMappableDataFile->getPaletteNormalizationMode()) { + case PaletteNormalizationModeEnum::NORMALIZATION_ALL_MAP_DATA: + m_histogramBucketsSpinBox->setValue(this->caretMappableDataFile->getFileHistogramNumberOfBuckets()); + break; + case PaletteNormalizationModeEnum::NORMALIZATION_SELECTED_MAP_DATA: + m_histogramBucketsSpinBox->setValue(this->paletteColorMapping->getHistogramNumberOfBuckets()); + break; + } + m_histogramBucketsSpinBox->blockSignals(false); + FastStatistics* statistics = NULL; switch (this->caretMappableDataFile->getPaletteNormalizationMode()) { case PaletteNormalizationModeEnum::NORMALIZATION_ALL_MAP_DATA: @@ -1656,6 +1913,9 @@ MapSettingsPaletteColorMappingWidget::updateEditorInternal(CaretMappableDataFile this->scaleFixedPositiveMaximumSpinBox->setSingleStep(stepValue); } + m_paletteOptionsWidget->updateEditor(this->caretMappableDataFile, + this->mapFileIndex); + this->updateHistogramPlot(); this->updateNormalizationControlSection(); @@ -1678,41 +1938,45 @@ MapSettingsPaletteColorMappingWidget::getHistogram(const FastStatistics* statist float leastNeg = 0.0; float mostNeg = 0.0; bool matchFlag = false; - if (this->histogramAllRadioButton->isChecked()) { - float dummy; - statisticsForAll->getNonzeroRanges(mostNeg, dummy, dummy, mostPos); - } - else if (this->histogramMatchPaletteRadioButton->isChecked()) { - matchFlag = true; - switch (this->paletteColorMapping->getScaleMode()) { - case PaletteScaleModeEnum::MODE_AUTO_SCALE: - mostPos = statisticsForAll->getMax(); - leastPos = 0.0; - leastNeg = 0.0; - mostNeg = statisticsForAll->getMin(); - break; - case PaletteScaleModeEnum::MODE_AUTO_SCALE_ABSOLUTE_PERCENTAGE: - mostPos = statisticsForAll->getApproxAbsolutePercentile(this->scaleAutoAbsolutePercentageMaximumSpinBox->value()); - leastPos = statisticsForAll->getApproxAbsolutePercentile(this->scaleAutoAbsolutePercentageMinimumSpinBox->value()); - leastNeg = -statisticsForAll->getApproxAbsolutePercentile(this->scaleAutoAbsolutePercentageMinimumSpinBox->value()); - mostNeg = -statisticsForAll->getApproxAbsolutePercentile(this->scaleAutoAbsolutePercentageMaximumSpinBox->value()); - break; - case PaletteScaleModeEnum::MODE_AUTO_SCALE_PERCENTAGE: - mostPos = statisticsForAll->getApproxPositivePercentile(this->scaleAutoPercentagePositiveMaximumSpinBox->value()); - leastPos = statisticsForAll->getApproxPositivePercentile(this->scaleAutoPercentagePositiveMinimumSpinBox->value()); - leastNeg = statisticsForAll->getApproxNegativePercentile(this->scaleAutoPercentageNegativeMinimumSpinBox->value()); - mostNeg = statisticsForAll->getApproxNegativePercentile(this->scaleAutoPercentageNegativeMaximumSpinBox->value()); - break; - case PaletteScaleModeEnum::MODE_USER_SCALE: - mostPos = this->scaleFixedPositiveMaximumSpinBox->value(); - leastPos = this->scaleFixedPositiveMinimumSpinBox->value(); - leastNeg = this->scaleFixedNegativeMinimumSpinBox->value(); - mostNeg = this->scaleFixedNegativeMaximumSpinBox->value(); - break; + + switch (this->paletteColorMapping->getHistogramRangeMode()) { + case PaletteHistogramRangeModeEnum::PALETTE_HISTOGRAM_RANGE_ALL: + { + float dummy; + statisticsForAll->getNonzeroRanges(mostNeg, dummy, dummy, mostPos); } - } - else { - CaretAssert(0); + break; + case PaletteHistogramRangeModeEnum::PALETTE_HISTOGRAM_RANGE_MATCH_PALETTE: + { + matchFlag = true; + switch (this->paletteColorMapping->getScaleMode()) { + case PaletteScaleModeEnum::MODE_AUTO_SCALE: + mostPos = statisticsForAll->getMax(); + leastPos = 0.0; + leastNeg = 0.0; + mostNeg = statisticsForAll->getMin(); + break; + case PaletteScaleModeEnum::MODE_AUTO_SCALE_ABSOLUTE_PERCENTAGE: + mostPos = statisticsForAll->getApproxAbsolutePercentile(this->scaleAutoAbsolutePercentageMaximumSpinBox->value()); + leastPos = statisticsForAll->getApproxAbsolutePercentile(this->scaleAutoAbsolutePercentageMinimumSpinBox->value()); + leastNeg = -statisticsForAll->getApproxAbsolutePercentile(this->scaleAutoAbsolutePercentageMinimumSpinBox->value()); + mostNeg = -statisticsForAll->getApproxAbsolutePercentile(this->scaleAutoAbsolutePercentageMaximumSpinBox->value()); + break; + case PaletteScaleModeEnum::MODE_AUTO_SCALE_PERCENTAGE: + mostPos = statisticsForAll->getApproxPositivePercentile(this->scaleAutoPercentagePositiveMaximumSpinBox->value()); + leastPos = statisticsForAll->getApproxPositivePercentile(this->scaleAutoPercentagePositiveMinimumSpinBox->value()); + leastNeg = statisticsForAll->getApproxNegativePercentile(this->scaleAutoPercentageNegativeMinimumSpinBox->value()); + mostNeg = statisticsForAll->getApproxNegativePercentile(this->scaleAutoPercentageNegativeMaximumSpinBox->value()); + break; + case PaletteScaleModeEnum::MODE_USER_SCALE: + mostPos = this->scaleFixedPositiveMaximumSpinBox->value(); + leastPos = this->scaleFixedPositiveMinimumSpinBox->value(); + leastNeg = this->scaleFixedNegativeMinimumSpinBox->value(); + mostNeg = this->scaleFixedNegativeMaximumSpinBox->value(); + break; + } + } + break; } const PaletteNormalizationModeEnum::Enum normMode = this->caretMappableDataFile->getPaletteNormalizationMode(); @@ -1780,6 +2044,10 @@ MapSettingsPaletteColorMappingWidget::updateHistogramPlot() this->thresholdPlot->detachItems(); + + if (this->paletteColorMapping == NULL) { + return; + } FastStatistics* statistics = NULL; switch (this->caretMappableDataFile->getPaletteNormalizationMode()) { case PaletteNormalizationModeEnum::NORMALIZATION_ALL_MAP_DATA: @@ -1789,286 +2057,377 @@ MapSettingsPaletteColorMappingWidget::updateHistogramPlot() statistics = const_cast(this->caretMappableDataFile->getMapFastStatistics(this->mapFileIndex)); break; } + if (statistics == NULL) { + return; + } + if (this->paletteColorMapping->isHistogramBarsVisible() + || this->paletteColorMapping->isHistogramEnvelopeVisible()) { + /* okay */ + } + else { + /* nothing to display */ + return; + } - if ((this->paletteColorMapping != NULL) - && (statistics != NULL)) { - PaletteFile* paletteFile = GuiManager::get()->getBrain()->getPaletteFile(); - + /* + * Data values table + */ + const float statsMean = statistics->getMean(); + const float statsStdDev = statistics->getSampleStdDev(); + const float statsMin = statistics->getMin(); + const float statsMax = statistics->getMax(); + this->statisticsMeanValueLabel->setText(QString::number(statsMean, 'f', 4)); + this->statisticsStandardDeviationLabel->setText(QString::number(statsStdDev, 'f', 4)); + this->statisticsMaximumValueLabel->setText(QString::number(statsMax, 'f', 4)); + this->statisticsMinimumValueLabel->setText(QString::number(statsMin, 'f', 4)); + + /* + * Get data for this histogram. + */ + const Histogram* myHist = getHistogram(statistics); + float minValue, maxValue; + myHist->getRange(minValue, maxValue); + const std::vector& displayDataReference = myHist->getHistogramDisplay(); + std::vector displayData = displayDataReference; + const int64_t numHistogramValues = (int64_t)(displayData.size()); + + /* + * Width of each 'bar' in the histogram + */ + float step = 1.0; + if (numHistogramValues > 1) { + step = ((maxValue - minValue) + / numHistogramValues); + } + + const CaretColorEnum::Enum histogramBarsColor = paletteColorMapping->getHistogramBarsColor(); + const CaretColorEnum::Enum histogramEnvelopeColor = paletteColorMapping->getHistogramEnvelopeColor(); + + float* dataValues = NULL; + float* dataRGBA = NULL; + if (numHistogramValues > 0) { /* - * Data values table + * Compute color for 'bar' in the histogram. + * Note that number of data values is one more + * than the number of histogram values due to that + * a data value is needed on the right of the last + * histogram bar. Otherwise, if there is an outlier + * value, the histogram will not be drawn + * correctly. */ - const float statsMean = statistics->getMean(); - const float statsStdDev = statistics->getSampleStdDev(); - const float statsMin = statistics->getMin(); - const float statsMax = statistics->getMax(); - this->statisticsMeanValueLabel->setText(QString::number(statsMean, 'f', 4)); - this->statisticsStandardDeviationLabel->setText(QString::number(statsStdDev, 'f', 4)); - this->statisticsMaximumValueLabel->setText(QString::number(statsMax, 'f', 4)); - this->statisticsMinimumValueLabel->setText(QString::number(statsMin, 'f', 4)); + const int64_t numDataValues = numHistogramValues + 1; + dataValues = new float[numDataValues]; + dataRGBA = new float[numDataValues * 4]; + for (int64_t ix = 0; ix < numDataValues; ix++) { + const float value = (minValue + + (ix * step)); + dataValues[ix] = value; + } + dataValues[0] = minValue; + dataValues[numDataValues - 1] = maxValue; /* - * Get data for this histogram. + * Color with palette so that alpha values are zero for regions not displayed */ - const Histogram* myHist = getHistogram(statistics); - //const int64_t* histogram = const_cast(statistics->getHistogram()); - float minValue, maxValue; - myHist->getRange(minValue, maxValue); - const std::vector& displayData = myHist->getHistogramDisplay(); - const int64_t numHistogramValues = (int64_t)(displayData.size()); + NodeAndVoxelColoring::colorScalarsWithPalette(statistics, + paletteColorMapping, + dataValues, + paletteColorMapping, + dataValues, + numDataValues, + dataRGBA, + true); /* ignore thresholding */ /* - * Width of each 'bar' in the histogram + * If bucket is not colored (zero alpha) set bucket height to zero */ - float step = 1.0; - if (numHistogramValues > 1) { - step = ((maxValue - minValue) - / numHistogramValues); - } - - float* dataValues = NULL; - float* dataRGBA = NULL; - if (numHistogramValues > 0) { - /* - * Compute color for 'bar' in the histogram. - * Note that number of data values is one more - * than the number of histogram values due to that - * a data value is needed on the right of the last - * histogram bar. Otherwise, if there is an outlier - * value, the histogram will not be drawn - * correctly. - */ - const int64_t numDataValues = numHistogramValues + 1; - dataValues = new float[numDataValues]; - dataRGBA = new float[numDataValues * 4]; - for (int64_t ix = 0; ix < numDataValues; ix++) { - const float value = (minValue - + (ix * step)); - dataValues[ix] = value; - } - dataValues[0] = minValue; - dataValues[numDataValues - 1] = maxValue; - - const Palette* palette = paletteFile->getPaletteByName(this->paletteColorMapping->getSelectedPaletteName()); - if (this->histogramUsePaletteColors->isChecked() - && (palette != NULL)) { - NodeAndVoxelColoring::colorScalarsWithPalette(statistics, - paletteColorMapping, - palette, - dataValues, - dataValues, - numDataValues, - dataRGBA, - true); // ignore thresholding - } - else { - for (int64_t i = 0; i < numDataValues; i++) { - const int64_t i4 = i * 4; - dataRGBA[i4] = 1.0; - dataRGBA[i4+1] = 0.0; - dataRGBA[i4+2] = 0.0; - dataRGBA[i4+3] = 1.0; - } + for (int32_t i = 0; i < numHistogramValues; i++) { + const int32_t alphaIndex = i * 4 + 3; + CaretAssertArrayIndex(dataRGBA, (numDataValues * 4), alphaIndex); + if (dataRGBA[alphaIndex] <= 0.0) { + CaretAssertVectorIndex(displayData, i); + displayData[i] = 0.0; } } + } + + float z = 0.0; + float maxDataFrequency = 0.0; + for (int32_t drawModeInt = 0; drawModeInt < 2; drawModeInt++) { + bool drawBarsFlag = false; + bool drawEnvelopeFlag = false; + if (drawModeInt == 0) { + drawBarsFlag = this->paletteColorMapping->isHistogramBarsVisible(); + } + else if (drawModeInt == 1) { + drawEnvelopeFlag = this->paletteColorMapping->isHistogramEnvelopeVisible(); + } - const bool displayZeros = paletteColorMapping->isDisplayZeroDataFlag(); - - float z = 0.0; - float maxDataFrequency = 0.0; - for (int64_t ix = 0; ix < numHistogramValues; ix++) { - QColor color; - const int64_t ix4 = ix * 4; - color.setRedF(dataRGBA[ix4]); - color.setGreenF(dataRGBA[ix4+1]); - color.setBlueF(dataRGBA[ix4+2]); - color.setAlphaF(1.0); - - const float startValue = dataValues[ix]; - const float stopValue = dataValues[ix + 1]; - float dataFrequency = displayData[ix]; - - bool displayIt = true; + if (drawBarsFlag + || drawEnvelopeFlag) { + float barsSolidRGBA[4]; + CaretColorEnum::toRGBAFloat(histogramBarsColor, + barsSolidRGBA); + float envelopeSolidRGBA[4]; + CaretColorEnum::toRGBAFloat(histogramEnvelopeColor, + envelopeSolidRGBA); - if (displayZeros == false) { - if ((startValue <= 0.0) && (stopValue >= 0.0)) { - dataFrequency = 0.0; + const int32_t lastIndex = numHistogramValues - 1; + for (int64_t ix = 0; ix < numHistogramValues; ix++) { + QColor color; + const int64_t ix4 = ix * 4; + if (drawBarsFlag) { + if (histogramBarsColor == CaretColorEnum::CUSTOM) { + color.setRedF(dataRGBA[ix4]); + color.setGreenF(dataRGBA[ix4+1]); + color.setBlueF(dataRGBA[ix4+2]); + color.setAlphaF(1.0); + } + else { + color.setRgbF(barsSolidRGBA[0], barsSolidRGBA[1], barsSolidRGBA[2]); + } + } + else if (drawEnvelopeFlag) { + if (histogramEnvelopeColor == CaretColorEnum::CUSTOM) { + color.setRedF(dataRGBA[ix4]); + color.setGreenF(dataRGBA[ix4+1]); + color.setBlueF(dataRGBA[ix4+2]); + color.setAlphaF(1.0); + } + else { + color.setRgbF(envelopeSolidRGBA[0], envelopeSolidRGBA[1], envelopeSolidRGBA[2]); + } + } + else { + CaretAssert(0); + } + + const float startValue = dataValues[ix]; + const float stopValue = dataValues[ix + 1]; + float dataFrequency = displayData[ix]; + + bool displayIt = true; + + if (dataFrequency > maxDataFrequency) { + maxDataFrequency = dataFrequency; + } + + /* + * If color is not displayed ('none' or thresholded), + * set its frequncey value to a small value so that the plot + * retains its shape and color is still slightly visible + */ + if (dataRGBA[ix4+3] <= 0.0) { displayIt = false; } - } - - if (dataFrequency > maxDataFrequency) { - maxDataFrequency = dataFrequency; - } - - /* - * If color is not displayed ('none' or thresholded), - * set its frequncey value to a small value so that the plot - * retains its shape and color is still slightly visible - */ - //Qt::BrushStyle brushStyle = Qt::SolidPattern; - if (dataRGBA[ix4+3] <= 0.0) { - displayIt = false; - } - - if (displayIt == false) { - color.setAlpha(0); - } - - QVector samples; - samples.push_back(QPointF(startValue, dataFrequency)); - samples.push_back(QPointF(stopValue, dataFrequency)); - - QwtPlotCurve* curve = new QwtPlotCurve(); - curve->setRenderHint(QwtPlotItem::RenderAntialiased); - curve->setVisible(true); - curve->setStyle(QwtPlotCurve::Steps); - - curve->setBrush(QBrush(color)); //, brushStyle)); - curve->setPen(QPen(color)); - curve->setSamples(samples); - - curve->attach(this->thresholdPlot); - - if (ix == 0) { - z = curve->z(); - } - } - - z = z - 1; - - if ((numHistogramValues > 2) - && (this->paletteColorMapping->getThresholdType() != PaletteThresholdTypeEnum::THRESHOLD_TYPE_OFF)) { - float threshMinValue = this->paletteColorMapping->getThresholdNormalMinimum(); - float threshMaxValue = this->paletteColorMapping->getThresholdNormalMaximum(); - - maxDataFrequency *= 1.05; - - switch (this->paletteColorMapping->getThresholdTest()) { - case PaletteThresholdTestEnum::THRESHOLD_TEST_SHOW_INSIDE: - { - const float plotMinValue = this->thresholdPlot->axisScaleDiv(QwtPlot::xBottom).lowerBound(); - const float plotMaxValue = this->thresholdPlot->axisScaleDiv(QwtPlot::xBottom).upperBound(); - - /* - * Draw shaded region to left of minimum threshold - */ - QVector minSamples; - minSamples.push_back(QPointF(plotMinValue, maxDataFrequency)); - minSamples.push_back(QPointF(threshMinValue, maxDataFrequency)); - - QwtPlotCurve* minBox = new QwtPlotCurve(); - minBox->setRenderHint(QwtPlotItem::RenderAntialiased); - minBox->setVisible(true); - minBox->setStyle(QwtPlotCurve::Dots); - - QColor minColor(100, 100, 255, 160); - minBox->setBrush(QBrush(minColor, Qt::Dense4Pattern)); - minBox->setPen(QPen(minColor)); - minBox->setSamples(minSamples); + + if ( ! displayIt) { + if (drawBarsFlag) { + color.setAlpha(0); + } + if (drawEnvelopeFlag) { + dataFrequency = 0.0f; + } + } + + QVector samples; + + QwtPlotCurve* curve = new QwtPlotCurve(); + curve->setRenderHint(QwtPlotItem::RenderAntialiased); + curve->setVisible(true); + + curve->setPen(QPen(color)); + + if (drawBarsFlag) { + curve->setStyle(QwtPlotCurve::Steps); + curve->setBrush(QBrush(color)); + samples.push_back(QPointF(startValue, dataFrequency)); + samples.push_back(QPointF(stopValue, dataFrequency)); + } + if (drawEnvelopeFlag) { + curve->setStyle(QwtPlotCurve::Lines); - minBox->setZ(z); - minBox->attach(this->thresholdPlot); + static float lastX = 0.0f; + static float lastY = 0.0f; - /* - * Draw shaded region to right of maximum threshold + /* + * Left side */ - QVector maxSamples; - maxSamples.push_back(QPointF(threshMaxValue, maxDataFrequency)); - maxSamples.push_back(QPointF(plotMaxValue, maxDataFrequency)); - - QwtPlotCurve* maxBox = new QwtPlotCurve(); - maxBox->setRenderHint(QwtPlotItem::RenderAntialiased); - maxBox->setVisible(true); - maxBox->setStyle(QwtPlotCurve::Dots); - - QColor maxColor(100, 100, 255, 160); - maxBox->setBrush(QBrush(maxColor, Qt::Dense4Pattern)); - maxBox->setPen(QPen(maxColor)); - maxBox->setSamples(maxSamples); + if (ix == 0) { + samples.push_back(QPointF(startValue, 0.0)); + samples.push_back(QPointF(startValue, dataFrequency)); + lastX = startValue; + lastY = dataFrequency; + } + else { + CaretAssertVectorIndex(displayData, ix - 1); + const float lastValue = dataValues[ix - 1]; + const float halfX = (startValue - lastValue) / 2.0f; + const float x = startValue + halfX; + const float y = dataFrequency; + samples.push_back(QPointF(lastX, lastY)); + samples.push_back(QPointF(x, y)); + lastX = x; + lastY = y; + } - maxBox->setZ(z); - maxBox->attach(this->thresholdPlot); - } - break; - case PaletteThresholdTestEnum::THRESHOLD_TEST_SHOW_OUTSIDE: - { - /* - * Draw shaded region between minimum and maximum threshold + /* + * Right side */ - QVector minSamples; - minSamples.push_back(QPointF(threshMinValue, maxDataFrequency)); - minSamples.push_back(QPointF(threshMaxValue, maxDataFrequency)); - - QwtPlotCurve* minBox = new QwtPlotCurve(); - minBox->setRenderHint(QwtPlotItem::RenderAntialiased); - minBox->setVisible(true); - - QColor minColor(100, 100, 255, 160); - minBox->setBrush(QBrush(minColor)); //, Qt::Dense4Pattern)); - minBox->setPen(QPen(minColor)); - minBox->setSamples(minSamples); - - minBox->setZ(z); - - minBox->attach(this->thresholdPlot); + if (ix == lastIndex) { + samples.push_back(QPointF(stopValue, dataFrequency)); + samples.push_back(QPointF(stopValue, 0.0)); + lastX = stopValue; + lastY = 0.0f; + } + } + + curve->setSamples(samples); + curve->attach(this->thresholdPlot); + + if (ix == 0) { + z = curve->z(); } - break; } - - const bool showLinesFlag = false; - if (showLinesFlag) { + } + + z--; + } + + z--; + + if ((numHistogramValues > 2) + && (this->paletteColorMapping->getThresholdType() == PaletteThresholdTypeEnum::THRESHOLD_TYPE_NORMAL)) { + float threshMinValue = this->paletteColorMapping->getThresholdNormalMinimum(); + float threshMaxValue = this->paletteColorMapping->getThresholdNormalMaximum(); + + maxDataFrequency *= 1.05; + + switch (this->paletteColorMapping->getThresholdTest()) { + case PaletteThresholdTestEnum::THRESHOLD_TEST_SHOW_INSIDE: + { + const float plotMinValue = this->thresholdPlot->axisScaleDiv(QwtPlot::xBottom).lowerBound(); + const float plotMaxValue = this->thresholdPlot->axisScaleDiv(QwtPlot::xBottom).upperBound(); + /* - * Line for high threshold + * Draw shaded region to left of minimum threshold */ QVector minSamples; - minSamples.push_back(QPointF(threshMinValue, 0)); + minSamples.push_back(QPointF(plotMinValue, maxDataFrequency)); minSamples.push_back(QPointF(threshMinValue, maxDataFrequency)); - QwtPlotCurve* minLine = new QwtPlotCurve(); - minLine->setRenderHint(QwtPlotItem::RenderAntialiased); - minLine->setVisible(true); - minLine->setStyle(QwtPlotCurve::Lines); + QwtPlotCurve* minBox = new QwtPlotCurve(); + minBox->setRenderHint(QwtPlotItem::RenderAntialiased); + minBox->setVisible(true); + minBox->setStyle(QwtPlotCurve::Dots); - QColor minColor(0.0, 0.0, 1.0); - minLine->setPen(QPen(minColor)); - minLine->setSamples(minSamples); + QColor minColor(100, 100, 255, 160); + minBox->setBrush(QBrush(minColor, Qt::Dense4Pattern)); + minBox->setPen(QPen(minColor)); + minBox->setSamples(minSamples); - minLine->setZ(1.0); - minLine->attach(this->thresholdPlot); + minBox->setZ(z); + minBox->attach(this->thresholdPlot); /* - * Line for high threshold + * Draw shaded region to right of maximum threshold */ QVector maxSamples; - maxSamples.push_back(QPointF(threshMaxValue, 0)); maxSamples.push_back(QPointF(threshMaxValue, maxDataFrequency)); + maxSamples.push_back(QPointF(plotMaxValue, maxDataFrequency)); - QwtPlotCurve* maxLine = new QwtPlotCurve(); - maxLine->setRenderHint(QwtPlotItem::RenderAntialiased); - maxLine->setVisible(true); - maxLine->setStyle(QwtPlotCurve::Lines); + QwtPlotCurve* maxBox = new QwtPlotCurve(); + maxBox->setRenderHint(QwtPlotItem::RenderAntialiased); + maxBox->setVisible(true); + maxBox->setStyle(QwtPlotCurve::Dots); - QColor maxColor(1.0, 0.0, 0.0); - maxLine->setPen(QPen(maxColor)); - maxLine->setSamples(maxSamples); + QColor maxColor(100, 100, 255, 160); + maxBox->setBrush(QBrush(maxColor, Qt::Dense4Pattern)); + maxBox->setPen(QPen(maxColor)); + maxBox->setSamples(maxSamples); - maxLine->setZ(1.0); - maxLine->attach(this->thresholdPlot); + maxBox->setZ(z); + maxBox->attach(this->thresholdPlot); } + break; + case PaletteThresholdTestEnum::THRESHOLD_TEST_SHOW_OUTSIDE: + { + /* + * Draw shaded region between minimum and maximum threshold + */ + QVector minSamples; + minSamples.push_back(QPointF(threshMinValue, maxDataFrequency)); + minSamples.push_back(QPointF(threshMaxValue, maxDataFrequency)); + + QwtPlotCurve* minBox = new QwtPlotCurve(); + minBox->setRenderHint(QwtPlotItem::RenderAntialiased); + minBox->setVisible(true); + + QColor minColor(100, 100, 255, 160); + minBox->setBrush(QBrush(minColor)); + minBox->setPen(QPen(minColor)); + minBox->setSamples(minSamples); + + minBox->setZ(z); + + minBox->attach(this->thresholdPlot); + } + break; } - if (dataValues != NULL) { - delete[] dataValues; - } - if (dataRGBA != NULL) { - delete[] dataRGBA; + const bool showLinesFlag = false; + if (showLinesFlag) { + /* + * Line for high threshold + */ + QVector minSamples; + minSamples.push_back(QPointF(threshMinValue, 0)); + minSamples.push_back(QPointF(threshMinValue, maxDataFrequency)); + + QwtPlotCurve* minLine = new QwtPlotCurve(); + minLine->setRenderHint(QwtPlotItem::RenderAntialiased); + minLine->setVisible(true); + minLine->setStyle(QwtPlotCurve::Lines); + + QColor minColor(0.0, 0.0, 1.0); + minLine->setPen(QPen(minColor)); + minLine->setSamples(minSamples); + + minLine->setZ(1.0); + minLine->attach(this->thresholdPlot); + + /* + * Line for high threshold + */ + QVector maxSamples; + maxSamples.push_back(QPointF(threshMaxValue, 0)); + maxSamples.push_back(QPointF(threshMaxValue, maxDataFrequency)); + + QwtPlotCurve* maxLine = new QwtPlotCurve(); + maxLine->setRenderHint(QwtPlotItem::RenderAntialiased); + maxLine->setVisible(true); + maxLine->setStyle(QwtPlotCurve::Lines); + + QColor maxColor(1.0, 0.0, 0.0); + maxLine->setPen(QPen(maxColor)); + maxLine->setSamples(maxSamples); + + maxLine->setZ(1.0); + maxLine->attach(this->thresholdPlot); } - - /* - * Causes updates of plots. - */ - this->thresholdPlot->replot(); } + + if (dataValues != NULL) { + delete[] dataValues; + } + if (dataRGBA != NULL) { + delete[] dataRGBA; + } + + /* + * Causes updates of plots. + */ + this->thresholdPlot->replot(); } /** @@ -2122,6 +2481,22 @@ void MapSettingsPaletteColorMappingWidget::applySelections() this->paletteColorMapping->setDisplayZeroDataFlag(this->displayModeZeroCheckBox->isChecked()); this->paletteColorMapping->setInterpolatePaletteFlag(this->interpolateColorsCheckBox->isChecked()); + this->paletteColorMapping->setInvertedMode(this->invertPaletteModeComboBox->getSelectedItem()); + + this->paletteColorMapping->setHistogramBarsColor(m_histogramBarsColorComboBox->getSelectedColor()); + this->paletteColorMapping->setHistogramEnvelopeColor(m_histogramEnvelopeColorComboBox->getSelectedColor()); + this->paletteColorMapping->setHistogramEnvelopeLineWidthPercentage(m_histogramEnvelopeLineWidthPercentageSpinBox->value()); + this->paletteColorMapping->setHistogramBarsVisible(m_histogramBarsVisibleCheckBox->isChecked()); + this->paletteColorMapping->setHistogramEnvelopeVisible(m_histogramEnvelopeVisibleCheckBox->isChecked()); + this->paletteColorMapping->setHistogramRangeMode(m_histogramHorizontalRangeComboBox->getSelectedItem()); + switch (this->caretMappableDataFile->getPaletteNormalizationMode()) { + case PaletteNormalizationModeEnum::NORMALIZATION_ALL_MAP_DATA: + this->caretMappableDataFile->setFileHistogramNumberOfBuckets(m_histogramBucketsSpinBox->value()); + break; + case PaletteNormalizationModeEnum::NORMALIZATION_SELECTED_MAP_DATA: + this->paletteColorMapping->setHistogramNumberOfBuckets(m_histogramBucketsSpinBox->value()); + break; + } float lowValue = this->thresholdLowSpinBox->value(); float highValue = this->thresholdHighSpinBox->value(); @@ -2140,19 +2515,9 @@ void MapSettingsPaletteColorMappingWidget::applySelections() this->paletteColorMapping->setThresholdTest(PaletteThresholdTestEnum::THRESHOLD_TEST_SHOW_OUTSIDE); } - if (this->applyAllMapsCheckBox->checkState() == Qt::Checked) { - const int numMaps = this->caretMappableDataFile->getNumberOfMaps(); - for (int32_t i = 0; i < numMaps; i++) { - PaletteColorMapping* pcm = this->caretMappableDataFile->getMapPaletteColorMapping(i); - if (pcm != this->paletteColorMapping) { - pcm->copy(*this->paletteColorMapping); - } - } - } - this->updateHistogramPlot(); - updateColoringAndGraphics(); + m_paletteOptionsWidget->applyOptions(); } /** @@ -2269,12 +2634,12 @@ MapSettingsPaletteColorMappingWidget::normalizationModeComboBoxActivated(int) * When files are "large", using all file data may take * a very long time so allow the user to cancel. */ - const int64_t megabyte = 1000000; // 10e6 + const int64_t megabyte = 1000000; /* 10e6 */ const int64_t warningDataSize = 100 * megabyte; const int64_t dataSize = this->caretMappableDataFile->getDataSizeUncompressedInBytes(); if (dataSize > warningDataSize) { - const int64_t gigabyte = 1000000000; // 10e9 + const int64_t gigabyte = 1000000000; /* 10e9 */ const int64_t numReallys = std::min(dataSize / gigabyte, (int64_t)10); AString veryString; @@ -2308,7 +2673,7 @@ MapSettingsPaletteColorMappingWidget::normalizationModeComboBoxActivated(int) this->caretMappableDataFile->setPaletteNormalizationMode(mode); this->updateEditorInternal(this->caretMappableDataFile, this->mapFileIndex); - this->updateColoringAndGraphics(); + m_paletteOptionsWidget->applyOptions(); } else { this->updateNormalizationControlSection(); @@ -2318,118 +2683,3 @@ MapSettingsPaletteColorMappingWidget::normalizationModeComboBoxActivated(int) } } } - - -/** - * Called when the state of the apply all maps checkbox is changed. - * @param checked - * New status of checkbox. - */ -void -MapSettingsPaletteColorMappingWidget::applyAllMapsCheckBoxStateChanged(bool checked) -{ - if (checked) { - this->applySelections(); - } -} - -/** - * @return A widget containing the data options. - */ -QWidget* -MapSettingsPaletteColorMappingWidget::createDataOptionsSection() -{ - this->applyAllMapsCheckBox = new QCheckBox("Apply to All Maps"); - this->applyAllMapsCheckBox->setCheckState(Qt::Checked); - QObject::connect(this->applyAllMapsCheckBox, SIGNAL(clicked(bool)), - this, SLOT(applyAllMapsCheckBoxStateChanged(bool))); - this->applyAllMapsCheckBox->setToolTip("If checked, settings are applied to all maps\n" - "in the file containing the selected map"); - - this->applyToMultipleFilesPushButton = new QPushButton("Apply to Files..."); - const QString tt("Displays a dialog that allows selection of data files to which the " - "palette settings are applied."); - this->applyToMultipleFilesPushButton->setToolTip(WuQtUtilities::createWordWrappedToolTipText(tt)); - QObject::connect(this->applyToMultipleFilesPushButton, SIGNAL(clicked()), - this, SLOT(applyToMultipleFilesPushbuttonClicked())); - - QGroupBox* optionsGroupBox = new QGroupBox("Data Options"); - QVBoxLayout* optionsLayout = new QVBoxLayout(optionsGroupBox); - this->setLayoutSpacingAndMargins(optionsLayout); - optionsLayout->addWidget(this->applyAllMapsCheckBox); - optionsLayout->addWidget(this->applyToMultipleFilesPushButton); - optionsGroupBox->setSizePolicy(QSizePolicy(QSizePolicy::MinimumExpanding, - QSizePolicy::Fixed)); - - return optionsGroupBox; -} - -/** - * Allows user to select files to which palette settings are applied. - */ -void -MapSettingsPaletteColorMappingWidget::applyToMultipleFilesPushbuttonClicked() -{ - EventCaretMappableDataFilesGet mapFilesGet; - EventManager::get()->sendEvent(mapFilesGet.getPointer()); - - std::vector mappableFiles; - mapFilesGet.getAllFiles(mappableFiles); - - const QString filePointerPropertyName("filePointer"); - - WuQDataEntryDialog ded("Apply Palettes Settings", - this->applyToMultipleFilesPushButton, - true); - ded.setTextAtTop("Palette settings will be applied to all maps in the selected files.", - true); - - std::vector mapFileCheckBoxes; - for (std::vector::iterator iter = mappableFiles.begin(); - iter != mappableFiles.end(); - iter++) { - CaretMappableDataFile* cmdf = *iter; - if (cmdf->isMappedWithPalette()) { - QCheckBox* cb = ded.addCheckBox(cmdf->getFileNameNoPath()); - cb->setProperty(filePointerPropertyName.toAscii().constData(), - qVariantFromValue((void*)cmdf)); - mapFileCheckBoxes.push_back(cb); - - if (previousApplyPaletteToMapFilesSelected.find(cmdf) != previousApplyPaletteToMapFilesSelected.end()) { - cb->setChecked(true); - } - } - } - - previousApplyPaletteToMapFilesSelected.clear(); - - if (ded.exec() == WuQDataEntryDialog::Accepted) { - PaletteFile* paletteFile = GuiManager::get()->getBrain()->getPaletteFile(); - - for (std::vector::iterator iter = mapFileCheckBoxes.begin(); - iter != mapFileCheckBoxes.end(); - iter++) { - QCheckBox* cb = *iter; - if (cb->isChecked()) { - void* pointer = cb->property(filePointerPropertyName.toAscii().constData()).value(); - CaretMappableDataFile* cmdf = (CaretMappableDataFile*)pointer; - - const int32_t numMaps = cmdf->getNumberOfMaps(); - for (int32_t iMap = 0; iMap < numMaps; iMap++) { - PaletteColorMapping* pcm = cmdf->getMapPaletteColorMapping(iMap); - if (pcm != this->paletteColorMapping) { - pcm->copy(*this->paletteColorMapping); - } - } - - cmdf->updateScalarColoringForAllMaps(paletteFile); - - previousApplyPaletteToMapFilesSelected.insert(cmdf); - } - } - - updateColoringAndGraphics(); - } -} - - diff --git a/src/GuiQt/MapSettingsPaletteColorMappingWidget.h b/src/GuiQt/MapSettingsPaletteColorMappingWidget.h index 37aae1308e56b7b883225a6e33d8f85d54cc7675..6dff272d8158722e44e009a6873bf48fd890e9ed 100644 --- a/src/GuiQt/MapSettingsPaletteColorMappingWidget.h +++ b/src/GuiQt/MapSettingsPaletteColorMappingWidget.h @@ -34,17 +34,22 @@ class QLayout; class QPushButton; class QRadioButton; class QSpinBox; +class QToolButton; class QwtPlot; namespace caret { + class CaretColorEnumComboBox; + class CaretMappableDataFileAndMapSelectorObject; class CaretMappableDataFile; class EnumComboBoxTemplate; class FastStatistics; class Histogram; + class MapSettingsColorBarPaletteOptionsWidget; class PaletteColorMapping; class WuQDoubleSlider; + class WuQDoubleSpinBox; class WuQWidgetObjectGroup; class WuQwtPlot; @@ -74,6 +79,8 @@ namespace caret { void thresholdTypeChanged(int); void thresholdRangeModeChanged(); void thresholdLinkCheckBoxToggled(bool); + void thresholdMapFileIndexSelectorChanged(); + void thresholdSetAllMapsToolButtonClicked(); void scaleAutoPercentageNegativeMaximumValueChanged(double value); void scaleAutoPercentageNegativeMinimumValueChanged(double value); @@ -88,15 +95,10 @@ namespace caret { void scaleFixedPositiveMinimumValueChanged(double value); void scaleFixedPositiveMaximumValueChanged(double value); - void histogramControlChanged(); void histogramResetViewButtonClicked(); void applyAndUpdate(); void applySelections(); - void applyAllMapsCheckBoxStateChanged(bool); - - void applyToMultipleFilesPushbuttonClicked(); - void contextMenuDisplayRequested(QContextMenuEvent* event, float graphX, float graphY); @@ -114,7 +116,6 @@ namespace caret { QWidget* createHistogramSection(); QWidget* createHistogramControlSection(); QWidget* createNormalizationControlSection(); - QWidget* createDataOptionsSection(); void updateNormalizationControlSection(); @@ -123,10 +124,10 @@ namespace caret { void updateHistogramPlot(); - void updateColoringAndGraphics(); - void updateThresholdControlsMinimumMaximumRangeValues(); + void updatePaletteMappedToDataValueLabels(); + void setLayoutSpacingAndMargins(QLayout* layout); const Histogram* getHistogram(const FastStatistics* statisticsForAll) const; @@ -135,9 +136,6 @@ namespace caret { QComboBox* paletteNameComboBox; - QCheckBox* applyAllMapsCheckBox; - QPushButton* applyToMultipleFilesPushButton; - QRadioButton* scaleAutoRadioButton; QRadioButton* scaleAutoAbsolutePercentageRadioButton; QRadioButton* scaleAutoPercentageRadioButton; @@ -156,13 +154,23 @@ namespace caret { QDoubleSpinBox* scaleFixedPositiveMinimumSpinBox; QDoubleSpinBox* scaleFixedPositiveMaximumSpinBox; + QLabel* scaleNegativeMaximumValueLabel; + QLabel* scaleNegativeMinimumValueLabel; + QLabel* scalePositiveMinimumValueLabel; + QLabel* scalePositiveMaximumValueLabel; + QCheckBox* displayModePositiveCheckBox; QCheckBox* displayModeZeroCheckBox; QCheckBox* displayModeNegativeCheckBox; QCheckBox* interpolateColorsCheckBox; + EnumComboBoxTemplate* invertPaletteModeComboBox; QComboBox* thresholdTypeComboBox; + QToolButton* thresholdSetAllMapsToolButton; + + CaretMappableDataFileAndMapSelectorObject* thresholdMapFileIndexSelector; + QWidget* thresholdFileWidget; WuQDoubleSlider* thresholdLowSlider; WuQDoubleSlider* thresholdHighSlider; @@ -176,7 +184,7 @@ namespace caret { QRadioButton* thresholdShowInsideRadioButton; QRadioButton* thresholdShowOutsideRadioButton; - + QWidget* thresholdAdjustmentWidget; EnumComboBoxTemplate* thresholdRangeModeComboBox; QCheckBox* thresholdLinkCheckBox; @@ -190,15 +198,21 @@ namespace caret { QComboBox* m_normalizationModeComboBox; - QRadioButton* histogramAllRadioButton; - QRadioButton* histogramMatchPaletteRadioButton; + EnumComboBoxTemplate* m_histogramHorizontalRangeComboBox; + + CaretColorEnumComboBox* m_histogramBarsColorComboBox; - QCheckBox* histogramUsePaletteColors; + CaretColorEnumComboBox* m_histogramEnvelopeColorComboBox; - bool isHistogramColored; + WuQDoubleSpinBox* m_histogramEnvelopeLineWidthPercentageSpinBox; + + QCheckBox* m_histogramBarsVisibleCheckBox; + + QCheckBox* m_histogramEnvelopeVisibleCheckBox; + + QSpinBox* m_histogramBucketsSpinBox; CaretMappableDataFile* caretMappableDataFile; - CaretMappableDataFile* m_previousCaretMappableDataFile; int32_t mapFileIndex; @@ -207,6 +221,7 @@ namespace caret { WuQWidgetObjectGroup* paletteWidgetGroup; WuQWidgetObjectGroup* thresholdWidgetGroup; + MapSettingsColorBarPaletteOptionsWidget* m_paletteOptionsWidget; }; diff --git a/src/GuiQt/MapYokingGroupComboBox.cxx b/src/GuiQt/MapYokingGroupComboBox.cxx index 6c7159992c9f5bfa7b5ca3f5c5ff4fa1e6395ff4..1e8c58059aaa34cbdb43225a7df05a5cb0b44469 100644 --- a/src/GuiQt/MapYokingGroupComboBox.cxx +++ b/src/GuiQt/MapYokingGroupComboBox.cxx @@ -25,6 +25,7 @@ #include "CaretAssert.h" #include "CaretMappableDataFile.h" +#include "ChartTwoOverlay.h" #include "ChartableMatrixSeriesInterface.h" #include "CiftiScalarDataSeriesFile.h" #include "EnumComboBoxTemplate.h" @@ -195,6 +196,55 @@ MapYokingGroupComboBox::validateYokingChange(Overlay* overlay) } } +/** + * Validate a change in yoking for a chart overlay. + * + * @param chartOverlay + * Chart overlay whose yoking changes. + */ +void +MapYokingGroupComboBox::validateYokingChange(ChartTwoOverlay* chartOverlay) +{ + const MapYokingGroupEnum::Enum previousMapYokingGroup = chartOverlay->getMapYokingGroup(); + const MapYokingGroupEnum::Enum newYokingGroup = getMapYokingGroup(); + CaretMappableDataFile* mapFile = NULL; + ChartTwoOverlay::SelectedIndexType selectedIndexType = ChartTwoOverlay::SelectedIndexType::INVALID; + int32_t selectedIndex = -1; + chartOverlay->getSelectionData(mapFile, + selectedIndexType, + selectedIndex); + bool selectionStatus = chartOverlay->isEnabled(); + + if ((mapFile != NULL) + && chartOverlay->isMapYokingSupported()) { + if (mapFile->getNumberOfMaps() > 0) { + if (selectedIndex < 0) { + selectedIndex = 0; + } + } + const YokeValidationResult result = validateYoking(mapFile, + selectedIndex, + selectionStatus); + + switch (result) { + case YOKE_VALIDATE_RESULT_ACCEPT: + chartOverlay->setEnabled(selectionStatus); + chartOverlay->setSelectionData(mapFile, + selectedIndex); + chartOverlay->setMapYokingGroup(newYokingGroup); + break; + case YOKE_VALIDATE_RESULT_OFF: + chartOverlay->setMapYokingGroup(MapYokingGroupEnum::MAP_YOKING_GROUP_OFF); + break; + case YOKE_VALIDATE_RESULT_PREVIOUS: + chartOverlay->setMapYokingGroup(previousMapYokingGroup); + break; + } + + setMapYokingGroup(chartOverlay->getMapYokingGroup()); + } +} + /** * Validate yoking when a new file is added to a yoking group. * @@ -267,7 +317,6 @@ MapYokingGroupComboBox::validateYoking(CaretMappableDataFile* selectedFile, * the map index and status from the yoking group. */ selectedMapIndexInOut = MapYokingGroupEnum::getSelectedMapIndex(newYokingGroup); - //selectionStatusInOut = MapYokingGroupEnum::isEnabled(newYokingGroup); } else { /* @@ -277,8 +326,6 @@ MapYokingGroupComboBox::validateYoking(CaretMappableDataFile* selectedFile, */ MapYokingGroupEnum::setSelectedMapIndex(newYokingGroup, selectedMapIndexInOut); - //MapYokingGroupEnum::setEnabled(newYokingGroup, - // selectionStatusInOut); } } } @@ -287,78 +334,3 @@ MapYokingGroupComboBox::validateYoking(CaretMappableDataFile* selectedFile, return yokeResult; } - -//void -//MapYokingGroupComboBox::validateYokingChange(const MapYokingGroupEnum::Enum previousMapYokingGroup, -// CaretMappableDataFile* selectedFile, -// const int32_t selectedMapIndex, -// ChartableMatrixSeriesInterface* chartableMatrixSeriesInterface, -// Overlay* overlay) -//{ -// MapYokingGroupEnum::Enum yokingGroup = getMapYokingGroup(); -// if (yokingGroup != MapYokingGroupEnum::MAP_YOKING_GROUP_OFF) { -// CaretMappableDataFile* selectedFile = NULL; -// if ((selectedFile != NULL) -// && (selectedMapIndex >= 0)) { -// /* -// * Get info on yoking selections -// */ -// EventMapYokingValidation validateEvent(yokingGroup); -// EventManager::get()->sendEvent(validateEvent.getPointer()); -// -// const int32_t numOverlaysYoked = yokedOverlaysEvent.getNumberOfYokedOverlays(); -// -// /* -// * Check compatibility based (number of maps in yoked files) -// * and warn use if there is an incompatibility. -// */ -// AString message; -// if ( ! validateEvent.validateCompatibility(selectedFile, -// message)) { -// message.appendWithNewLine(""); -// message.appendWithNewLine("Allow yoking?"); -// -// message = WuQtUtilities::createWordWrappedToolTipText(message); -// -// WuQMessageBox::YesNoCancelResult result = -// WuQMessageBox::warningYesNoCancel(m_comboBox->getWidget(), -// message, -// ""); -// switch (result) { -// case WuQMessageBox::RESULT_YES: -// break; -// case WuQMessageBox::RESULT_NO: -// yokingGroup = MapYokingGroupEnum::MAP_YOKING_GROUP_OFF; -// break; -// case WuQMessageBox::RESULT_CANCEL: -// yokingGroup = previousMapYokingGroup; -// break; -// } -// } -// -// if (overlay != NULL) { -// -// } -// overlay->setYokingGroup(yokingGroup); -// if (yokingGroup != MapYokingGroupEnum::MAP_YOKING_GROUP_OFF) { -// if (numOverlaysYoked <= 0) { -// OverlayYokingGroupEnum::setSelectedMapIndex(yokingGroup, -// selectedMapIndex); -// const bool enabledStatus = overlay->isEnabled(); -// OverlayYokingGroupEnum::setEnabled(yokingGroup, -// enabledStatus); -// } -// } -// } -// } -// else { -// overlay->setYokingGroup(yokingGroup); -// } -// -// updateViewController(overlay); -// -// this->updateUserInterfaceAndGraphicsWindow(); -// -//} -// - diff --git a/src/GuiQt/MapYokingGroupComboBox.h b/src/GuiQt/MapYokingGroupComboBox.h index 1fcb9345ff8fe3e205248010412a42a824773c8b..b2dffe916b4e3df6a523e88a9224c1372b8dae23 100644 --- a/src/GuiQt/MapYokingGroupComboBox.h +++ b/src/GuiQt/MapYokingGroupComboBox.h @@ -27,6 +27,7 @@ namespace caret { class CaretMappableDataFile; + class ChartTwoOverlay; class ChartableMatrixSeriesInterface; class EnumComboBoxTemplate; class Overlay; @@ -50,6 +51,8 @@ namespace caret { void validateYokingChange(Overlay* overlay); + void validateYokingChange(ChartTwoOverlay* chartOverlay); + // ADD_NEW_METHODS_HERE signals: diff --git a/src/GuiQt/MovieDialog.cxx b/src/GuiQt/MovieDialog.cxx index f3fe30390fc443fcab3dbdc5d161f23132e40081..cd96c99067cc549c26b3ed6d7a0685a10ee2f8f2 100644 --- a/src/GuiQt/MovieDialog.cxx +++ b/src/GuiQt/MovieDialog.cxx @@ -20,7 +20,11 @@ #include #include +#ifdef CARET_OS_WINDOWS +#include +#else #include +#endif #include "MovieDialog.h" #include "ui_MovieDialog.h" @@ -252,7 +256,7 @@ void MovieDialog::on_recordButton_toggled(bool checked) + fileName); CaretLogFine("running " + command); - system(command.toAscii().data()); + system(command.toLatin1().data()); CaretLogFine("Finished rendering " + fileName); } diff --git a/src/GuiQt/OffScreenOpenGLRenderer.cxx b/src/GuiQt/OffScreenOpenGLRenderer.cxx new file mode 100644 index 0000000000000000000000000000000000000000..d2a4d891d7a120b6b2d7d6813efd5be9b2b288d3 --- /dev/null +++ b/src/GuiQt/OffScreenOpenGLRenderer.cxx @@ -0,0 +1,171 @@ + +/*LICENSE_START*/ +/* + * Copyright (C) 2017 Washington University School of Medicine + * + * 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 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ +/*LICENSE_END*/ + +#include "CaretOpenGLInclude.h" + +#define __OFF_SCREEN_OPEN_G_L_RENDERER_DECLARE__ +#include "OffScreenOpenGLRenderer.h" +#undef __OFF_SCREEN_OPEN_G_L_RENDERER_DECLARE__ + +#include +#ifdef WORKBENCH_USE_QT5_QOPENGL_WIDGET +#include +#else +#include +#include +#endif +#include +#include +#include + +#include "BrainOpenGLWidget.h" +#include "CaretAssert.h" + +using namespace caret; + + + +/** + * \class caret::OffScreenOpenGLRenderer + * \brief Performs offscreen rendering of a window. + * \ingroup GuiQt + */ + +/** + * Constructor. + * + * @param openGLWidgetInstance + * An instance of either QGLWidget or QOpenGLWidget + * @param width + * Width for image (may be different, perhaps larger, than width of 'openglWidget' + * @param height + * Height for image (may be different, perhaps larger, than hight of 'openglWidget' + */ +#ifdef WORKBENCH_USE_QT5_QOPENGL_WIDGET +OffScreenOpenGLRenderer::OffScreenOpenGLRenderer(QOpenGLWidget* openglWidget, +#else +OffScreenOpenGLRenderer::OffScreenOpenGLRenderer(QGLWidget* openglWidget, +#endif + const int32_t width, + const int32_t height) +: QObject() +{ + CaretAssert(openglWidget); + + m_openglContext = NULL;; + QSurfaceFormat surfaceFormat; + + /* + * Copy the surface format + */ +#ifdef WORKBENCH_USE_QT5_QOPENGL_WIDGET + surfaceFormat = openglWidget->format(); + m_openglContext = openglWidget->context(); +#else + surfaceFormat = QGLFormat::toSurfaceFormat(openglWidget->format()); + CaretAssert(openglWidget->context()); + m_openglContext = openglWidget->context()->contextHandle(); +#endif +// QGLWidget* glWidget = dynamic_cast(openGLWidgetInstance); +// QOpenGLWidget* openglWidget = dynamic_cast(openGLWidgetInstance); +// if (glWidget != NULL) { +// } +// else if (openglWidget != NULL) { +// } +// else { +// m_errorMessage = "Unable to cast openglQtWidget to QGLWidget nor QOpenGLWidget"; +// return; +// } + + CaretAssert(m_openglContext); + if ( ! m_openglContext->isValid()) { + m_errorMessage = "The OpenGL Context is not valid"; + return; + } + + // since context is already valid, do not need to do this: openglContext->setFormat(surfaceFormat); + + m_offScreenSurface.reset(new QOffscreenSurface()); + m_offScreenSurface->setFormat(surfaceFormat); + m_offScreenSurface->create(); + if ( ! m_offScreenSurface->isValid()) { + m_errorMessage = "Offscreen surface is not valid after setFormat() and create()"; + return; + } + + if ( ! m_openglContext->makeCurrent(m_offScreenSurface.get())) { + m_errorMessage = "Unable to makeCurrent() using Off Screen Surface"; + return; + } + + QOpenGLFramebufferObjectFormat frameBufferFormat; + frameBufferFormat.setSamples(2); + frameBufferFormat.setAttachment(QOpenGLFramebufferObject::Depth); + frameBufferFormat.setTextureTarget(GL_TEXTURE_2D); + + m_frameBuffer.reset(new QOpenGLFramebufferObject(width, + height, + frameBufferFormat)); + if ( ! m_frameBuffer->isValid()) { + m_errorMessage = "Unable to create frame buffer object for Off Screen Rendering"; + m_openglContext->doneCurrent(); + return; + } + +} + +/** + * Destructor. + */ +OffScreenOpenGLRenderer::~OffScreenOpenGLRenderer() +{ + m_openglContext->doneCurrent(); +} + +bool +OffScreenOpenGLRenderer::isError() const +{ + return ( ! m_errorMessage.isEmpty()); +} + +QString +OffScreenOpenGLRenderer::getErrorMessage() const +{ + return m_errorMessage; +} + +QImage +OffScreenOpenGLRenderer::getImage() +{ + QImage image; + + if ( ! m_frameBuffer) { + m_errorMessage = "Frame buffer is invalid while trying to get image"; + return image; + } + + image = m_frameBuffer->toImage(); + + return image; +} + + + diff --git a/src/GuiQt/OffScreenOpenGLRenderer.h b/src/GuiQt/OffScreenOpenGLRenderer.h new file mode 100644 index 0000000000000000000000000000000000000000..2a4441ac7db1210539e8c7a81d50ac4c51bcd39a --- /dev/null +++ b/src/GuiQt/OffScreenOpenGLRenderer.h @@ -0,0 +1,85 @@ +#ifndef __OFF_SCREEN_OPEN_G_L_RENDERER_H__ +#define __OFF_SCREEN_OPEN_G_L_RENDERER_H__ + +/*LICENSE_START*/ +/* + * Copyright (C) 2017 Washington University School of Medicine + * + * 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 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ +/*LICENSE_END*/ + + + +#include + +#include + +class QGLWidget; +class QOpenGLFramebufferObject; +class QOffscreenSurface; +class QOpenGLContext; +class QOpenGLWidget; +class QSurfaceFormat; +class QWidget; + +namespace caret { + + class OffScreenOpenGLRenderer : public QObject { + + Q_OBJECT + + public: +#ifdef WORKBENCH_USE_QT5_QOPENGL_WIDGET + OffScreenOpenGLRenderer(QOpenGLWidget* openglWidget, +#else + OffScreenOpenGLRenderer(QGLWidget* openglWidget, +#endif + const int32_t width, + const int32_t height); + + virtual ~OffScreenOpenGLRenderer(); + + bool isError() const; + + QString getErrorMessage() const; + + QImage getImage(); + + // ADD_NEW_METHODS_HERE + + private: + OffScreenOpenGLRenderer(const OffScreenOpenGLRenderer&); + + OffScreenOpenGLRenderer& operator=(const OffScreenOpenGLRenderer&); + + std::unique_ptr m_offScreenSurface; + + std::unique_ptr m_frameBuffer; + + QOpenGLContext* m_openglContext; + + QString m_errorMessage; + + // ADD_NEW_MEMBERS_HERE + + }; + +#ifdef __OFF_SCREEN_OPEN_G_L_RENDERER_DECLARE__ + // +#endif // __OFF_SCREEN_OPEN_G_L_RENDERER_DECLARE__ + +} // namespace +#endif //__OFF_SCREEN_OPEN_G_L_RENDERER_H__ diff --git a/src/GuiQt/OverlaySetViewController.cxx b/src/GuiQt/OverlaySetViewController.cxx index 3d1bbf618ac465c10d2ad7b1b1259e9519b1b995..fb090d8eebe1835c0df0bd46517edb42de16352d 100644 --- a/src/GuiQt/OverlaySetViewController.cxx +++ b/src/GuiQt/OverlaySetViewController.cxx @@ -81,6 +81,7 @@ OverlaySetViewController::OverlaySetViewController(const Qt::Orientation orienta gridLayout->setColumnStretch(4, 0); gridLayout->setColumnStretch(5, 100); gridLayout->setColumnStretch(6, 0); + gridLayout->setColumnStretch(7, 0); gridLayout->setColumnStretch(8, 100); QLabel* onLabel = new QLabel("On"); @@ -135,15 +136,24 @@ OverlaySetViewController::OverlaySetViewController(const Qt::Orientation orienta verticalLayout->addStretch(); } else { - QVBoxLayout* verticalLayout = new QVBoxLayout(); + /* + * Resolve WB-649 + */ + QVBoxLayout* verticalLayout = new QVBoxLayout(this); WuQtUtilities::setLayoutSpacingAndMargins(verticalLayout, 1, 1); verticalLayout->addWidget(gridWidget); verticalLayout->addStretch(); - QHBoxLayout* horizontalLayout = new QHBoxLayout(this); - WuQtUtilities::setLayoutSpacingAndMargins(horizontalLayout, 1, 1); - horizontalLayout->addLayout(verticalLayout); - horizontalLayout->addStretch(); + +// QVBoxLayout* verticalLayout = new QVBoxLayout(); +// WuQtUtilities::setLayoutSpacingAndMargins(verticalLayout, 1, 1); +// verticalLayout->addWidget(gridWidget); +// verticalLayout->addStretch(); +// +// QHBoxLayout* horizontalLayout = new QHBoxLayout(this); +// WuQtUtilities::setLayoutSpacingAndMargins(horizontalLayout, 1, 1); +// horizontalLayout->addLayout(verticalLayout); +// horizontalLayout->addStretch(); } EventManager::get()->addEventListener(this, EventTypeEnum::EVENT_USER_INTERFACE_UPDATE); diff --git a/src/GuiQt/OverlaySettingsEditorDialog.cxx b/src/GuiQt/OverlaySettingsEditorDialog.cxx index e86650cc8bbfbede789ecc617262667330fcff0a..5aeff42f904d422a70163d6ad7972b863fbb02a2 100644 --- a/src/GuiQt/OverlaySettingsEditorDialog.cxx +++ b/src/GuiQt/OverlaySettingsEditorDialog.cxx @@ -32,8 +32,10 @@ #undef __OVERLAY_SETTINGS_EDITOR_DIALOG_DECLARE__ #include "CaretMappableDataFile.h" +#include "ChartTwoOverlay.h" #include "CiftiFiberTrajectoryFile.h" #include "CiftiConnectivityMatrixParcelFile.h" +#include "EventChartOverlayValidate.h" #include "EventDataFileDelete.h" #include "EventGraphicsUpdateAllWindows.h" #include "EventManager.h" @@ -41,6 +43,7 @@ #include "EventSurfaceColoringInvalidate.h" #include "EventUserInterfaceUpdate.h" #include "GiftiLabelTableEditor.h" +#include "MapSettingsChartTwoLineHistoryWidget.h" #include "MapSettingsColorBarWidget.h" #include "MapSettingsFiberTrajectoryWidget.h" #include "MapSettingsLabelsWidget.h" @@ -77,7 +80,10 @@ OverlaySettingsEditorDialog::OverlaySettingsEditorDialog(QWidget* parent) this->setDeleteWhenClosed(false); m_caretMappableDataFile = NULL; - m_mapIndex = -1; + m_selectedMapFileIndex = -1; + m_brainordinateOverlay = NULL; + m_chartOverlay = NULL; + m_chartOverlaySelectedIndexType = ChartTwoOverlay::SelectedIndexType::INVALID; /* * No apply button @@ -100,6 +106,8 @@ OverlaySettingsEditorDialog::OverlaySettingsEditorDialog(QWidget* parent) m_labelsWidget = new MapSettingsLabelsWidget(); + m_lineHistoryWidget = new MapSettingsChartTwoLineHistoryWidget(); + m_tabWidget = new QTabWidget(); m_colorBarWidgetTabIndex = m_tabWidget->addTab(m_colorBarWidget, @@ -112,6 +120,8 @@ OverlaySettingsEditorDialog::OverlaySettingsEditorDialog(QWidget* parent) m_layersWidgetTabIndex = m_tabWidget->addTab(m_layerWidget, "Layer"); + m_lineHistoryWidgetTabIndex = m_tabWidget->addTab(m_lineHistoryWidget, + "Lines"); m_metadataWidgetTabIndex = m_tabWidget->addTab(new QWidget(), "Metadata"); m_tabWidget->setTabEnabled(m_tabWidget->count() - 1, false); @@ -201,44 +211,94 @@ OverlaySettingsEditorDialog::createMapFileAndNameSection() * The focus event. */ void -OverlaySettingsEditorDialog::focusInEvent(QFocusEvent* /*event*/) +OverlaySettingsEditorDialog::focusInEvent(QFocusEvent* event) { + if (event->reason() == Qt::PopupFocusReason) { + /* + * This occurs when a combo box is popped up Mac, + * causes a problem, and can be ignored. + */ + return; + } + updateDialog(); } /** * Update if the given overlay is displayed in the dialog. * - * @param overlay - * The overlay. + * @param brainordinateOverlay + * Brainordinate overlay for the dialog. + * @param chartOverlay + * Chart overlay for the dialog. */ void -OverlaySettingsEditorDialog::updateIfThisOverlayIsInDialog(Overlay* overlay) +OverlaySettingsEditorDialog::updateIfThisOverlayIsInDialog(Overlay* brainordinateOverlay, + ChartTwoOverlay* chartOverlay) { - if (m_overlay == overlay) { - updateDialogContent(m_overlay); + if (m_brainordinateOverlay != NULL) { + if (m_brainordinateOverlay == brainordinateOverlay) { + updateDialogContent(m_brainordinateOverlay, + NULL); + } + } + else if (m_chartOverlay != NULL) { + if (m_chartOverlay == chartOverlay) { + updateDialogContent(NULL, + m_chartOverlay); + } } } /** * May be called to update the dialog's content. * - * @param overlay - * Overlay for the dialog. + * @param brainordinateOverlay + * Brainordinate overlay for the dialog. + * @param chartOverlay + * Chart overlay for the dialog. */ void -OverlaySettingsEditorDialog::updateDialogContent(Overlay* overlay) +OverlaySettingsEditorDialog::updateDialogContent(Overlay* brainordinateOverlay, + ChartTwoOverlay* chartOverlay) +{ + updateDialogContentPrivate(brainordinateOverlay, + chartOverlay); +} + +/** + * Update the dialog's content. Only one of the parameters + * should be non-NULL. + * + * @param brainordinateOverlay + * Brainordinate overlay for the dialog. + * @param chartOverlay + * Chart overlay for the dialog. + */ +void +OverlaySettingsEditorDialog::updateDialogContentPrivate(Overlay* brainordinateOverlay, + ChartTwoOverlay* chartOverlay) { + if ((brainordinateOverlay != NULL) + && (chartOverlay != NULL)) { + CaretAssert(0); // only one should be non-NULL + } + const int32_t selectedTabIndex = m_tabWidget->currentIndex(); - m_overlay = overlay; -// m_caretMappableDataFile = caretMappableDataFile; -// m_mapFileIndex = mapFileIndex; + m_brainordinateOverlay = brainordinateOverlay; + m_chartOverlay = chartOverlay; m_caretMappableDataFile = NULL; - m_mapIndex = -1; - if (m_overlay != NULL) { - overlay->getSelectionData(m_caretMappableDataFile, - m_mapIndex); + m_chartOverlaySelectedIndexType = ChartTwoOverlay::SelectedIndexType::INVALID; + m_selectedMapFileIndex = -1; + if (m_brainordinateOverlay != NULL) { + m_brainordinateOverlay->getSelectionData(m_caretMappableDataFile, + m_selectedMapFileIndex); + } + if (m_chartOverlay != NULL) { + m_chartOverlay->getSelectionData(m_caretMappableDataFile, + m_chartOverlaySelectedIndexType, + m_selectedMapFileIndex); } bool isLabelsValid = false; @@ -248,92 +308,204 @@ OverlaySettingsEditorDialog::updateDialogContent(Overlay* overlay) bool isParcelsValid = false; bool isFiberTrajectoryValid = false; bool isVolumeLayer = false; + bool isLinesValid = false; QString mapFileName = ""; QString mapName = ""; if (m_caretMappableDataFile != NULL) { - if ((m_mapIndex >= 0) - && (m_mapIndex < m_caretMappableDataFile->getNumberOfMaps())) { - /* - * Get name of file and map - */ - mapFileName = m_caretMappableDataFile->getFileNameNoPath(); - if (m_mapIndex >= 0) { - mapName = m_caretMappableDataFile->getMapName(m_mapIndex); - } - - /* - * Update layer widget - */ - m_layerWidget->updateContent(m_overlay); - - if (m_caretMappableDataFile->isMappedWithLabelTable()) { - if (m_caretMappableDataFile->getMapLabelTable(m_mapIndex) != NULL) { - /* - * Update label editor - */ - isLabelsValid = true; - m_labelsWidget->updateContent(overlay); + if (m_brainordinateOverlay != NULL) { + if ((m_selectedMapFileIndex >= 0) + && (m_selectedMapFileIndex < m_caretMappableDataFile->getNumberOfMaps())) { + /* + * Get name of file and map + */ + mapFileName = m_caretMappableDataFile->getFileNameNoPath(); + if (m_selectedMapFileIndex >= 0) { + mapName = m_caretMappableDataFile->getMapName(m_selectedMapFileIndex); } - } - - metadata = m_caretMappableDataFile->getMapMetaData(m_mapIndex); - if (metadata != NULL) { + /* - * TODO: Update metadata widget + * Update layer widget */ - //isMetadataValid = true; - } - - if (m_caretMappableDataFile->isMappedWithPalette()) { - PaletteColorMapping* paletteColorMapping = m_caretMappableDataFile->getMapPaletteColorMapping(m_mapIndex); - if (paletteColorMapping != NULL) { + m_layerWidget->updateContent(m_brainordinateOverlay); + + if (m_caretMappableDataFile->isMappedWithLabelTable()) { + if (m_caretMappableDataFile->getMapLabelTable(m_selectedMapFileIndex) != NULL) { + /* + * Update label editor + */ + isLabelsValid = true; + m_labelsWidget->updateContent(m_brainordinateOverlay); + } + } + + metadata = m_caretMappableDataFile->getMapMetaData(m_selectedMapFileIndex); + if (metadata != NULL) { /* - * Update palette settings + * TODO: Update metadata widget */ - isPaletteValid = true; - m_paletteColorMappingWidget->updateEditor(m_caretMappableDataFile, - m_mapIndex); - - m_colorBarWidget->updateContent(m_overlay->getColorBar(), - paletteColorMapping); + //isMetadataValid = true; } - } - - CiftiFiberTrajectoryFile* trajFile = dynamic_cast(m_caretMappableDataFile); - if (trajFile != NULL) { + + if (m_caretMappableDataFile->isMappedWithPalette()) { + PaletteColorMapping* paletteColorMapping = m_caretMappableDataFile->getMapPaletteColorMapping(m_selectedMapFileIndex); + if (paletteColorMapping != NULL) { + /* + * Update palette settings + */ + isPaletteValid = true; + m_paletteColorMappingWidget->updateEditor(m_caretMappableDataFile, + m_selectedMapFileIndex); + AnnotationColorBar* colorBar = NULL; + if (m_brainordinateOverlay != NULL) { + colorBar = m_brainordinateOverlay->getColorBar(); + } + else if (m_chartOverlay != NULL) { + colorBar = m_chartOverlay->getColorBar(); + } + m_colorBarWidget->updateContent(m_caretMappableDataFile, + m_selectedMapFileIndex, + colorBar, + paletteColorMapping); + } + } + + CiftiFiberTrajectoryFile* trajFile = dynamic_cast(m_caretMappableDataFile); + if (trajFile != NULL) { + /* + * Update trajectory + */ + isFiberTrajectoryValid = true; + m_fiberTrajectoryWidget->updateEditor(trajFile); + } + + CiftiConnectivityMatrixParcelFile* parcelsFile = dynamic_cast(m_caretMappableDataFile); + if (parcelsFile != NULL) { + /* + * Update parcels + */ + isParcelsValid = true; + m_parcelsWidget->updateEditor(parcelsFile); + } + /* - * Update trajectory + * Is volume mappable */ - isFiberTrajectoryValid = true; - m_fiberTrajectoryWidget->updateEditor(trajFile); + if (m_caretMappableDataFile->isVolumeMappable()) { + if (isFiberTrajectoryValid) { + /* nothing */ + } + else { + isVolumeLayer = true; + } + } } - - CiftiConnectivityMatrixParcelFile* parcelsFile = dynamic_cast(m_caretMappableDataFile); - if (parcelsFile != NULL) { - /* - * Update parcels - */ - isParcelsValid = true; - m_parcelsWidget->updateEditor(parcelsFile); + } + else if (m_chartOverlay != NULL) { + bool hasMapsFlag = false; + bool hasHistoryFlag = false; + switch (m_chartOverlay->getChartTwoDataType()) { + case ChartTwoDataTypeEnum::CHART_DATA_TYPE_INVALID: + break; + case ChartTwoDataTypeEnum::CHART_DATA_TYPE_HISTOGRAM: + hasMapsFlag = true; + break; + case ChartTwoDataTypeEnum::CHART_DATA_TYPE_LINE_SERIES: + hasHistoryFlag = true; + break; + case ChartTwoDataTypeEnum::CHART_DATA_TYPE_MATRIX: + hasMapsFlag = true; + break; } - /* - * Is volume mappable - */ - if (m_caretMappableDataFile->isVolumeMappable()) { - if (isFiberTrajectoryValid) { - /* nothing */ + int32_t selectedMapIndex = -1; + bool mapTypeFlag = false; + if (hasMapsFlag) { + switch (m_chartOverlaySelectedIndexType) { + case ChartTwoOverlay::SelectedIndexType::INVALID: + CaretAssert(0); + break; + case ChartTwoOverlay::SelectedIndexType::MAP: + if ((m_selectedMapFileIndex >= 0) + && (m_selectedMapFileIndex < m_caretMappableDataFile->getNumberOfMaps())) { + selectedMapIndex = m_selectedMapFileIndex; + mapTypeFlag = true; + } + break; + case ChartTwoOverlay::SelectedIndexType::COLUMN: + case ChartTwoOverlay::SelectedIndexType::ROW: + if (m_selectedMapFileIndex >= 0) { + if (m_caretMappableDataFile->getNumberOfMaps() == 1) { + m_selectedMapFileIndex = 0; + } + selectedMapIndex = m_selectedMapFileIndex; + } + break; } - else { - isVolumeLayer = true; + if ((selectedMapIndex >= 0) + && (selectedMapIndex < m_caretMappableDataFile->getNumberOfMaps())) { + /* + * Get name of file and map + */ + mapFileName = m_caretMappableDataFile->getFileNameNoPath(); + if (m_selectedMapFileIndex >= 0) { + mapName = m_caretMappableDataFile->getMapName(selectedMapIndex); + } + + if (mapTypeFlag) { + metadata = m_caretMappableDataFile->getMapMetaData(selectedMapIndex); + if (metadata != NULL) { + /* + * TODO: Update metadata widget + */ + //isMetadataValid = true; + } + } + + if (m_caretMappableDataFile->isMappedWithPalette()) { + PaletteColorMapping* paletteColorMapping = m_caretMappableDataFile->getMapPaletteColorMapping(selectedMapIndex); + if (paletteColorMapping != NULL) { + /* + * Update palette settings + */ + isPaletteValid = true; + m_paletteColorMappingWidget->updateEditor(m_caretMappableDataFile, + selectedMapIndex); + AnnotationColorBar* colorBar = NULL; + if (m_brainordinateOverlay != NULL) { + colorBar = m_brainordinateOverlay->getColorBar(); + } + else if (m_chartOverlay != NULL) { + colorBar = m_chartOverlay->getColorBar(); + } + m_colorBarWidget->updateContent(m_caretMappableDataFile, + selectedMapIndex, + colorBar, + paletteColorMapping); + } + } + + CiftiConnectivityMatrixParcelFile* parcelsFile = dynamic_cast(m_caretMappableDataFile); + if (parcelsFile != NULL) { + /* + * Update parcels + */ + isParcelsValid = true; + m_parcelsWidget->updateEditor(parcelsFile); + } } } - + else if (hasHistoryFlag) { + isLinesValid = true; + m_lineHistoryWidget->updateContent(m_chartOverlay); + } + } + else { + CaretAssert(0); } } - + /* * Set enabled status of tabs @@ -352,6 +524,8 @@ OverlaySettingsEditorDialog::updateDialogContent(Overlay* overlay) isParcelsValid); m_tabWidget->setTabEnabled(m_trajectoryWidgetTabIndex, isFiberTrajectoryValid); + m_tabWidget->setTabEnabled(m_lineHistoryWidgetTabIndex, + isLinesValid); /* * When the selected tab is invalid, we want to select the @@ -360,6 +534,7 @@ OverlaySettingsEditorDialog::updateDialogContent(Overlay* overlay) */ std::vector priorityTabIndices; priorityTabIndices.push_back(m_paletteWidgetTabIndex); + priorityTabIndices.push_back(m_lineHistoryWidgetTabIndex); priorityTabIndices.push_back(m_colorBarWidgetTabIndex); priorityTabIndices.push_back(m_labelsWidgetTabIndex); priorityTabIndices.push_back(m_parcelsWidgetTabIndex); @@ -400,19 +575,52 @@ OverlaySettingsEditorDialog::updateDialog() /* * Validate overlay to prevent crash */ - EventOverlayValidate validateOverlayEvent(m_overlay); - EventManager::get()->sendEvent(validateOverlayEvent.getPointer()); - if (validateOverlayEvent.isValidOverlay() == false) { - m_overlay = NULL; + + if (m_brainordinateOverlay != NULL) { + EventOverlayValidate validateOverlayEvent(m_brainordinateOverlay); + EventManager::get()->sendEvent(validateOverlayEvent.getPointer()); + if ( ! validateOverlayEvent.isValidOverlay()) { + m_brainordinateOverlay = NULL; + } } - updateDialogContent(m_overlay); + if (m_chartOverlay != NULL) { + EventChartOverlayValidate validateChartOverlayEvent(m_chartOverlay); + EventManager::get()->sendEvent(validateChartOverlayEvent.getPointer()); + if ( ! validateChartOverlayEvent.isValidChartOverlay()) { + m_chartOverlay = NULL; + } + } + + updateDialogContentPrivate(m_brainordinateOverlay, + m_chartOverlay); - if (m_overlay == NULL) { + if ((m_brainordinateOverlay == NULL) + && (m_chartOverlay == NULL)) { close(); } } +/** + * Update chart lines in the dialog + */ +void +OverlaySettingsEditorDialog::updateChartLinesInDialog() +{ + if (m_chartOverlay != NULL) { + EventChartOverlayValidate validateChartOverlayEvent(m_chartOverlay); + EventManager::get()->sendEvent(validateChartOverlayEvent.getPointer()); + if ( ! validateChartOverlayEvent.isValidChartOverlay()) { + m_chartOverlay = NULL; + } + + if (m_chartOverlay != NULL) { + updateDialogContentPrivate(NULL, + m_chartOverlay); + } + } +} + /** * Called when close button pressed. @@ -425,7 +633,8 @@ OverlaySettingsEditorDialog::closeButtonPressed() * and we don't want the dialog updating if it is not * visible. */ - updateDialogContent(NULL); + updateDialogContent(NULL, + NULL); /* * Allow this dialog to be reused (checked means DO NOT reuse) @@ -503,9 +712,9 @@ OverlaySettingsEditorDialog::editLabelTablePushButtonClicked() { if (m_caretMappableDataFile != NULL) { if (m_caretMappableDataFile->isMappedWithLabelTable()) { - if (m_mapIndex >= 0) { + if (m_selectedMapFileIndex >= 0) { GiftiLabelTableEditor labelTableEditor(m_caretMappableDataFile, - m_mapIndex, + m_selectedMapFileIndex, "Edit Labels", GiftiLabelTableEditor::OPTION_ADD_APPLY_BUTTON, m_editLabelTablePushButton); diff --git a/src/GuiQt/OverlaySettingsEditorDialog.h b/src/GuiQt/OverlaySettingsEditorDialog.h index d81e275c17b767a362f7e7d91eac7ed602c300f9..955f74b9a8363a584ad009d0db622f390f81b033 100644 --- a/src/GuiQt/OverlaySettingsEditorDialog.h +++ b/src/GuiQt/OverlaySettingsEditorDialog.h @@ -21,6 +21,7 @@ */ /*LICENSE_END*/ +#include "ChartTwoOverlay.h" #include "EventListenerInterface.h" #include "WuQDialogNonModal.h" @@ -33,7 +34,9 @@ class QTabWidget; namespace caret { class CaretMappableDataFile; + class ChartTwoOverlay; class LabelTableEditorWidget; + class MapSettingsChartTwoLineHistoryWidget; class MapSettingsColorBarWidget; class MapSettingsFiberTrajectoryWidget; class MapSettingsLabelsWidget; @@ -48,12 +51,16 @@ namespace caret { public: OverlaySettingsEditorDialog(QWidget* parent); - void updateDialogContent(Overlay* overlay); + void updateDialogContent(Overlay* brainordinateOverlay, + ChartTwoOverlay* chartOverlay); - void updateIfThisOverlayIsInDialog(Overlay* overlay); + void updateIfThisOverlayIsInDialog(Overlay* brainordinateOverlay, + ChartTwoOverlay* chartOverlay); void updateDialog(); + void updateChartLinesInDialog(); + virtual ~OverlaySettingsEditorDialog(); bool isDoNotReplaceSelected() const; @@ -76,6 +83,9 @@ namespace caret { void editLabelTablePushButtonClicked(); private: + void updateDialogContentPrivate(Overlay* brainordinateOverlay, + ChartTwoOverlay* chartOverlay); + QWidget* createWindowOptionsSection(); QWidget* createMapFileAndNameSection(); @@ -90,9 +100,13 @@ namespace caret { CaretMappableDataFile* m_caretMappableDataFile; - Overlay* m_overlay; + Overlay* m_brainordinateOverlay; + + ChartTwoOverlay* m_chartOverlay; - int32_t m_mapIndex; + ChartTwoOverlay::SelectedIndexType m_chartOverlaySelectedIndexType; + + int32_t m_selectedMapFileIndex; MapSettingsPaletteColorMappingWidget* m_paletteColorMappingWidget; @@ -108,6 +122,8 @@ namespace caret { MapSettingsLabelsWidget* m_labelsWidget; + MapSettingsChartTwoLineHistoryWidget* m_lineHistoryWidget; + QPushButton* m_editLabelTablePushButton; QLabel* m_selectedMapFileNameLabel; @@ -128,6 +144,8 @@ namespace caret { int32_t m_trajectoryWidgetTabIndex; + int32_t m_lineHistoryWidgetTabIndex; + }; #ifdef __OVERLAY_SETTINGS_EDITOR_DIALOG_DECLARE__ diff --git a/src/GuiQt/OverlayViewController.cxx b/src/GuiQt/OverlayViewController.cxx index 22e813395676a115694e8e84ec2ab129444d869e..1c6747bcbb032cf89f1f9d0f49ded5308c281123 100644 --- a/src/GuiQt/OverlayViewController.cxx +++ b/src/GuiQt/OverlayViewController.cxx @@ -95,6 +95,7 @@ OverlayViewController::OverlayViewController(const Qt::Orientation orientation, minComboBoxWidth = 50; maxComboBoxWidth = 100000; } + const QComboBox::SizeAdjustPolicy comboSizePolicy = QComboBox::AdjustToContentsOnFirstShow; //QComboBox::AdjustToContents; /* * Enabled Check Box @@ -114,6 +115,7 @@ OverlayViewController::OverlayViewController(const Qt::Orientation orientation, QObject::connect(this->fileComboBox, SIGNAL(activated(int)), this, SLOT(fileComboBoxSelected(int))); this->fileComboBox->setToolTip("Selects file for this overlay"); + this->fileComboBox->setSizeAdjustPolicy(comboSizePolicy); /* * Map Index Spin Box @@ -132,6 +134,7 @@ OverlayViewController::OverlayViewController(const Qt::Orientation orientation, QObject::connect(this->mapNameComboBox, SIGNAL(activated(int)), this, SLOT(mapNameComboBoxSelected(int))); this->mapNameComboBox->setToolTip("Select map by its name"); + this->mapNameComboBox->setSizeAdjustPolicy(comboSizePolicy); /* * Opacity double spin box @@ -363,7 +366,6 @@ OverlayViewController::fileComboBoxSelected(int indx) updateOverlaySettingsEditor(); updateViewController(this->overlay); - updateGraphicsWindow(); if (file != NULL) { if (file->isVolumeMappable()) { @@ -373,6 +375,8 @@ OverlayViewController::fileComboBoxSelected(int indx) EventManager::get()->sendEvent(EventUserInterfaceUpdate().setWindowIndex(browserWindowIndex).addToolBar().getPointer()); } } + + updateGraphicsWindow(); } /** @@ -838,9 +842,9 @@ OverlayViewController::createConstructionMenu(QWidget* parent) menu->addSeparator(); - menu->addAction("Copy Path and File Name to Clipboard", - this, - SLOT(menuCopyFileNameToClipBoard())); + m_copyPathAndFileNameToClipboardAction = menu->addAction("Copy Path and File Name to Clipboard", + this, + SLOT(menuCopyFileNameToClipBoard())); menu->addAction("Copy Map Name to Clipboard", this, @@ -861,7 +865,7 @@ OverlayViewController::menuConstructionAboutToShow() int32_t mapIndex = -1; this->overlay->getSelectionData(caretDataFile, mapIndex); - + QString menuText = "Reload Selected File"; if (caretDataFile != NULL) { if (caretDataFile->isModified()) { @@ -873,7 +877,12 @@ OverlayViewController::menuConstructionAboutToShow() } menuText += suffix; } + + const bool notDynConnFileFlag = (caretDataFile->getDataFileType() != DataFileTypeEnum::CONNECTIVITY_DENSE_DYNAMIC); + m_constructionReloadFileAction->setEnabled(notDynConnFileFlag); + m_copyPathAndFileNameToClipboardAction->setEnabled(notDynConnFileFlag); } + m_constructionReloadFileAction->setText(menuText); } } diff --git a/src/GuiQt/OverlayViewController.h b/src/GuiQt/OverlayViewController.h index aa8da69440bcda8c100a7a03f1d2b7da7f21bc71..d68b2c54f080659cd1285d74efc1b83b52270ecd 100644 --- a/src/GuiQt/OverlayViewController.h +++ b/src/GuiQt/OverlayViewController.h @@ -148,6 +148,8 @@ namespace caret { QAction* m_constructionReloadFileAction; + QAction* m_copyPathAndFileNameToClipboardAction; + WuQGridLayoutGroup* gridLayoutGroup; friend class OverlaySetViewController; diff --git a/src/GuiQt/PaletteColorMappingEditorDialog.cxx b/src/GuiQt/PaletteColorMappingEditorDialog.cxx index cf377fd64710cf0facdd88ccb24c10eee6fd472b..5340498d8bb42e507193787beedba57b5751b855 100644 --- a/src/GuiQt/PaletteColorMappingEditorDialog.cxx +++ b/src/GuiQt/PaletteColorMappingEditorDialog.cxx @@ -19,6 +19,7 @@ */ /*LICENSE_END*/ +#include #include #include #include @@ -133,8 +134,16 @@ PaletteColorMappingEditorDialog::receiveEvent(Event* event) * The focus event. */ void -PaletteColorMappingEditorDialog::focusInEvent(QFocusEvent* /*event*/) +PaletteColorMappingEditorDialog::focusInEvent(QFocusEvent* event) { + if (event->reason() == Qt::PopupFocusReason) { + /* + * This occurs when a combo box is popped up Mac, + * causes a problem, and can be ignored. + */ + return; + } + updateDialog(); } @@ -180,7 +189,9 @@ PaletteColorMappingEditorDialog::updateDialogContent(CaretMappableDataFile* mapF colorBar = matrixProps->getColorBar(); } } - m_paletteColorBarWidget->updateContent(colorBar, + m_paletteColorBarWidget->updateContent(m_mapFile, + m_mapIndex, + colorBar, paletteColorMapping); m_paletteColorMappingEditor->setEnabled(true); m_paletteColorMappingEditor->updateEditor(m_mapFile, diff --git a/src/GuiQt/PreferencesDialog.cxx b/src/GuiQt/PreferencesDialog.cxx index 8ccff5d7d9c87e39ba81902038fcd0f4ab1b1364..6ed6728bef18dd7000fa4b16a3ca6a44fd705429 100644 --- a/src/GuiQt/PreferencesDialog.cxx +++ b/src/GuiQt/PreferencesDialog.cxx @@ -43,6 +43,7 @@ #include "EnumComboBoxTemplate.h" #include "EventGraphicsUpdateAllWindows.h" #include "EventManager.h" +#include "EventSurfaceColoringInvalidate.h" #include "GuiManager.h" #include "ImageCaptureMethodEnum.h" #include "OpenGLDrawingMethodEnum.h" @@ -98,10 +99,10 @@ PreferencesDialog::PreferencesDialog(QWidget* parent) "ID"); tabWidget->addTab(createMiscellaneousWidget(), "Misc"); + tabWidget->addTab(createTabDefaltsWidget(), + "New Tabs"); tabWidget->addTab(createOpenGLWidget(), "OpenGL"); - tabWidget->addTab(createVolumeWidget(), - "Volume"); setCentralWidget(tabWidget, WuQDialog::SCROLL_AREA_NEVER); @@ -171,6 +172,10 @@ PreferencesDialog::addColorButtonAndSwatch(QGridLayout* gridLayout, buttonText = "Chart Grid Lines"; m_chartMatrixGridLinesColorWidget = colorSwatchWidget; break; + case PREF_COLOR_CHART_THRESHOLD: + buttonText = "Chart Threshold"; + m_chartHistogramThresholdColorWidget = colorSwatchWidget; + break; case NUMBER_OF_PREF_COLORS: CaretAssert(0); break; @@ -218,7 +223,9 @@ PreferencesDialog::createColorsWidget() addColorButtonAndSwatch(gridLayout, PREF_COLOR_CHART_MATRIX_GRID_LINES, colorSignalMapper); - + addColorButtonAndSwatch(gridLayout, + PREF_COLOR_CHART_THRESHOLD, + colorSignalMapper); addColorButtonAndSwatch(gridLayout, PREF_COLOR_FOREGROUND_SURFACE, colorSignalMapper); @@ -300,6 +307,10 @@ PreferencesDialog::updateColorWidget(CaretPreferences* prefs) colors.getColorChartMatrixGridLines(rgb); colorSwatchWidget = m_chartMatrixGridLinesColorWidget; break; + case PREF_COLOR_CHART_THRESHOLD: + colors.getColorChartHistogramThreshold(rgb); + colorSwatchWidget = m_chartHistogramThresholdColorWidget; + break; case NUMBER_OF_PREF_COLORS: CaretAssert(0); break; @@ -330,6 +341,8 @@ PreferencesDialog::createMiscellaneousWidget() QObject::connect(m_dynamicConnectivityComboBox, SIGNAL(statusChanged(bool)), this, SLOT(miscDynamicConnectivityComboBoxChanged(bool))); m_allWidgets->add(m_dynamicConnectivityComboBox); + m_dynamicConnectivityComboBox->setToolTip("Sets default (checked or unchecked) for dynamic connectivity files " + "on the Overlay ToolBox --> Connectivity tab."); /* * Logging Level @@ -358,16 +371,6 @@ PreferencesDialog::createMiscellaneousWidget() this, SLOT(miscSplashScreenShowAtStartupComboBoxChanged(bool))); m_allWidgets->add(m_miscSplashScreenShowAtStartupComboBox); - /* - * Yoking - */ - m_yokingDefaultComboBox = new WuQTrueFalseComboBox("On", - "Off", - this); - QObject::connect(m_yokingDefaultComboBox, SIGNAL(statusChanged(bool)), - this, SLOT(yokingComboBoxToggled(bool))); - m_allWidgets->add(m_yokingDefaultComboBox); - /* * Developer Menu */ @@ -389,7 +392,7 @@ PreferencesDialog::createMiscellaneousWidget() QGridLayout* gridLayout = new QGridLayout(); addWidgetToLayout(gridLayout, - "Show Dynconn By Default: ", + "Dynconn As Layer Default: ", m_dynamicConnectivityComboBox->getWidget()); addWidgetToLayout(gridLayout, "Logging Level: ", @@ -397,9 +400,6 @@ PreferencesDialog::createMiscellaneousWidget() addWidgetToLayout(gridLayout, "Save/Manage View Files: ", m_miscSpecFileDialogViewFilesTypeEnumComboBox->getWidget()); - addWidgetToLayout(gridLayout, - "New Tabs Yoked to Group A: ", - m_yokingDefaultComboBox->getWidget()); addWidgetToLayout(gridLayout, "Show Develop Menu in Menu Bar: ", m_miscDevelopMenuEnabledComboBox->getWidget()); @@ -447,21 +447,27 @@ PreferencesDialog::updateMiscellaneousWidget(CaretPreferences* prefs) QWidget* PreferencesDialog::createIdentificationSymbolWidget() { - QLabel* surfaceLabel = new QLabel("Show Surface ID Symbols"); + QLabel* infoLabel = new QLabel("These are defaults for Information Properties"); + infoLabel->setWordWrap(true); + m_surfaceIdentificationSymbolComboBox = new WuQTrueFalseComboBox("On", "Off", this); QObject::connect(m_surfaceIdentificationSymbolComboBox, SIGNAL(statusChanged(bool)), this, SLOT(identificationSymbolToggled())); - QLabel* volumeLabel = new QLabel("Show Volume ID Symbols"); m_volumeIdentificationSymbolComboBox = new WuQTrueFalseComboBox("On", "Off", this); QObject::connect(m_volumeIdentificationSymbolComboBox, SIGNAL(statusChanged(bool)), this, SLOT(identificationSymbolToggled())); QGridLayout* gridLayout = new QGridLayout(); - gridLayout->addWidget(surfaceLabel, 0, 0); - gridLayout->addWidget(m_surfaceIdentificationSymbolComboBox->getWidget(), 0, 1); - gridLayout->addWidget(volumeLabel, 1, 0); - gridLayout->addWidget(m_volumeIdentificationSymbolComboBox->getWidget(), 1, 1); + int row = gridLayout->rowCount(); + gridLayout->addWidget(infoLabel, + row, 0, 1, 2); + addWidgetToLayout(gridLayout, + "Show Surface ID Symbols: ", + m_surfaceIdentificationSymbolComboBox->getWidget()); + addWidgetToLayout(gridLayout, + "Show Volume ID Symbols: ", + m_volumeIdentificationSymbolComboBox->getWidget()); QWidget* widget = new QWidget(); QVBoxLayout* layout = new QVBoxLayout(widget); @@ -566,8 +572,18 @@ PreferencesDialog::updateOpenGLWidget(CaretPreferences* prefs) * @return The volume widget. */ QWidget* -PreferencesDialog::createVolumeWidget() +PreferencesDialog::createTabDefaltsWidget() { + /* + * Yoking + */ + m_yokingDefaultComboBox = new WuQTrueFalseComboBox("On", + "Off", + this); + QObject::connect(m_yokingDefaultComboBox, SIGNAL(statusChanged(bool)), + this, SLOT(yokingComboBoxToggled(bool))); + m_allWidgets->add(m_yokingDefaultComboBox); + /* * Crosshairs On/Off */ @@ -600,34 +616,36 @@ PreferencesDialog::createVolumeWidget() this, SLOT(volumeAxesMontageCoordinatesComboBoxToggled(bool))); m_allWidgets->add(m_volumeAxesMontageCoordinatesComboBox); -// /* -// * Montage Slice Gap -// */ -// m_volumeMontageGapSpinBox = WuQFactory::newSpinBoxWithMinMaxStepSignalInt(0, -// 100000, -// 1, -// this, -// SLOT(volumeMontageGapValueChanged(int))); -// m_allWidgets->add(m_volumeMontageGapSpinBox); - /* * Montage Slice Coordinate Precision */ m_volumeMontageCoordinatePrecisionSpinBox = WuQFactory::newSpinBoxWithMinMaxStepSignalInt(0, - 5, - 1, - this, - SLOT(volumeMontageCoordinatePrecisionChanged(int))); + 5, + 1, + this, + SLOT(volumeMontageCoordinatePrecisionChanged(int))); m_allWidgets->add(m_volumeMontageCoordinatePrecisionSpinBox); m_allWidgets->add(m_volumeAxesCrosshairsComboBox); m_allWidgets->add(m_volumeAxesLabelsComboBox); m_allWidgets->add(m_volumeAxesMontageCoordinatesComboBox); -// m_allWidgets->add(m_volumeMontageGapSpinBox); m_allWidgets->add(m_volumeMontageCoordinatePrecisionSpinBox); QGridLayout* gridLayout = new QGridLayout(); + QLabel* infoLabel = new QLabel("" + "These are the default settings for new Tabs. If " + "Yoked to Group A is on, the volume settings are overriden " + "by the yoked Tabs." + ""); + infoLabel->setWordWrap(true); + + gridLayout->addWidget(infoLabel, + 0, 0, 1, 2); + + addWidgetToLayout(gridLayout, + "Yoked to Group A: ", + m_yokingDefaultComboBox->getWidget()); addWidgetToLayout(gridLayout, "Volume Axes Crosshairs: ", m_volumeAxesCrosshairsComboBox->getWidget()); @@ -635,14 +653,11 @@ PreferencesDialog::createVolumeWidget() "Volume Axes Labels: ", m_volumeAxesLabelsComboBox->getWidget()); addWidgetToLayout(gridLayout, - "Volume Identification For New Tabs: ", + "Volume ID Moves Slice Selection: ", m_volumeIdentificationComboBox->getWidget()); addWidgetToLayout(gridLayout, "Volume Montage Slice Coord: ", m_volumeAxesMontageCoordinatesComboBox->getWidget()); -// addWidgetToLayout(gridLayout, -// "Volume Montage Gap: ", -// m_volumeMontageGapSpinBox); addWidgetToLayout(gridLayout, "Volume Montage Precision: ", m_volumeMontageCoordinatePrecisionSpinBox); @@ -667,7 +682,6 @@ PreferencesDialog::updateVolumeWidget(CaretPreferences* prefs) m_volumeAxesLabelsComboBox->setStatus(prefs->isVolumeAxesLabelsDisplayed()); m_volumeAxesMontageCoordinatesComboBox->setStatus(prefs->isVolumeMontageAxesCoordinatesDisplayed()); m_volumeIdentificationComboBox->setStatus(prefs->isVolumeIdentificationDefaultedOn()); -// m_volumeMontageGapSpinBox->setValue(prefs->getVolumeMontageGap()); m_volumeMontageCoordinatePrecisionSpinBox->setValue(prefs->getVolumeMontageCoordinatePrecision()); } @@ -791,6 +805,10 @@ PreferencesDialog::updateColorWithDialog(const PREF_COLOR prefColor) colors.getColorChartMatrixGridLines(rgb); prefColorName = "Chart Matrix Grid Lines"; break; + case PREF_COLOR_CHART_THRESHOLD: + colors.getColorChartHistogramThreshold(rgb); + prefColorName = "Chart Histogram Threshold"; + break; case NUMBER_OF_PREF_COLORS: CaretAssert(0); break; @@ -801,9 +819,9 @@ PreferencesDialog::updateColorWithDialog(const PREF_COLOR prefColor) rgb[2]); QColorDialog colorDialog(this); - colorDialog.setCurrentColor(initialColor); colorDialog.setOption(QColorDialog::DontUseNativeDialog); colorDialog.setWindowTitle(prefColorName); + colorDialog.setCurrentColor(initialColor); if (colorDialog.exec() == QColorDialog::Accepted) { const QColor newColor = colorDialog.currentColor(); @@ -841,6 +859,9 @@ PreferencesDialog::updateColorWithDialog(const PREF_COLOR prefColor) case PREF_COLOR_CHART_MATRIX_GRID_LINES: colors.setColorChartMatrixGridLines(rgb); break; + case PREF_COLOR_CHART_THRESHOLD: + colors.setColorChartHistogramThreshold(rgb); + break; case NUMBER_OF_PREF_COLORS: CaretAssert(0); break; @@ -851,6 +872,7 @@ PreferencesDialog::updateColorWithDialog(const PREF_COLOR prefColor) updateColorWidget(prefs); + EventManager::get()->sendEvent(EventSurfaceColoringInvalidate().getPointer()); EventManager::get()->sendEvent(EventGraphicsUpdateAllWindows().getPointer()); } } @@ -951,17 +973,6 @@ PreferencesDialog::volumeAxesMontageCoordinatesComboBoxToggled(bool value) EventManager::get()->sendEvent(EventGraphicsUpdateAllWindows().getPointer()); } -///** -// * Called when volume montage gap value is changed. -// */ -//void -//PreferencesDialog::volumeMontageGapValueChanged(int value) -//{ -// CaretPreferences* prefs = SessionManager::get()->getCaretPreferences(); -// prefs->setVolumeMontageGap(value); -// EventManager::get()->sendEvent(EventGraphicsUpdateAllWindows().getPointer()); -//} - /** * Called when volume montage coordinate precision value is changed. */ diff --git a/src/GuiQt/PreferencesDialog.h b/src/GuiQt/PreferencesDialog.h index 0ccd515e5432008199ec06494861702a7fe81db9..5e915f4ff912f26b5fe7f194f64e78b6614e6def 100644 --- a/src/GuiQt/PreferencesDialog.h +++ b/src/GuiQt/PreferencesDialog.h @@ -68,7 +68,6 @@ namespace caret { void volumeAxesCrosshairsComboBoxToggled(bool value); void volumeAxesLabelsComboBoxToggled(bool value); void volumeAxesMontageCoordinatesComboBoxToggled(bool value); -// void volumeMontageGapValueChanged(int value); void volumeMontageCoordinatePrecisionChanged(int value); void volumeIdentificationComboBoxToggled(bool value); @@ -87,14 +86,15 @@ namespace caret { PREF_COLOR_FOREGROUND_SURFACE = 6, PREF_COLOR_FOREGROUND_VOLUME = 7, PREF_COLOR_CHART_MATRIX_GRID_LINES = 8, - NUMBER_OF_PREF_COLORS = 9 + PREF_COLOR_CHART_THRESHOLD = 9, + NUMBER_OF_PREF_COLORS = 10 }; QWidget* createColorsWidget(); QWidget* createIdentificationSymbolWidget(); QWidget* createMiscellaneousWidget(); QWidget* createOpenGLWidget(); - QWidget* createVolumeWidget(); + QWidget* createTabDefaltsWidget(); void updateColorWidget(CaretPreferences* prefs); void updateIdentificationWidget(CaretPreferences* prefs); @@ -129,6 +129,7 @@ namespace caret { QWidget* m_backgroundColorSurfaceWidget; QWidget* m_backgroundColorVolumeWidget; QWidget* m_chartMatrixGridLinesColorWidget; + QWidget* m_chartHistogramThresholdColorWidget; WuQTrueFalseComboBox* m_miscDevelopMenuEnabledComboBox; QComboBox* m_miscLoggingLevelComboBox; @@ -145,7 +146,6 @@ namespace caret { WuQTrueFalseComboBox* m_volumeAxesCrosshairsComboBox; WuQTrueFalseComboBox* m_volumeAxesLabelsComboBox; WuQTrueFalseComboBox* m_volumeAxesMontageCoordinatesComboBox; -// QSpinBox* m_volumeMontageGapSpinBox; QSpinBox* m_volumeMontageCoordinatePrecisionSpinBox; WuQTrueFalseComboBox* m_volumeIdentificationComboBox; diff --git a/src/GuiQt/ProgressReportingDialog.cxx b/src/GuiQt/ProgressReportingDialog.cxx index 7f94379b6a1eddd22916165e0e7bc1eaf6711da7..ef42cb968f85e1bca34d8c0526162f566a4ed62d 100644 --- a/src/GuiQt/ProgressReportingDialog.cxx +++ b/src/GuiQt/ProgressReportingDialog.cxx @@ -27,6 +27,7 @@ #include #include "CaretAssert.h" +#include "CaretLogger.h" #include "EventManager.h" #include "EventProgressUpdate.h" #include "ProgressReportingFromEvent.h" @@ -148,3 +149,22 @@ ProgressReportingDialog::runEvent(Event* event, prd.setValue(prd.maximum()); } + +/** + * Set event receiving enabled. This method is used to enable + * and disable the receiving of EventProgressUpdate events. + * The default is true. + * + * @param enabled + * New status for receiving the events. + */ +void +ProgressReportingDialog::setEventReceivingEnabled(bool status) +{ + ProgressReportingFromEvent* progFromEvent = dynamic_cast(m_progressReporter); + if (progFromEvent != NULL) { + progFromEvent->setEventReceivingEnabled(status); + } +} + + diff --git a/src/GuiQt/ProgressReportingDialog.h b/src/GuiQt/ProgressReportingDialog.h index cb640adc9cd92dcfda9a59c70d8210fb0ff37e64..9e508310d782ffb50036ac19d2a401f7e4300750 100644 --- a/src/GuiQt/ProgressReportingDialog.h +++ b/src/GuiQt/ProgressReportingDialog.h @@ -39,11 +39,13 @@ namespace caret { const AString& initialMessage, QWidget* parent, Qt::WindowFlags f = 0); - + static void runEvent(Event* event, QWidget* parent, const AString& title); + void setEventReceivingEnabled(bool status); + public: virtual ~ProgressReportingDialog(); diff --git a/src/GuiQt/ProgressReportingWithSlots.cxx b/src/GuiQt/ProgressReportingWithSlots.cxx index 75a8999946bf748bf0fb18d1309fdd1ecffe9a0f..2a751416a993865b623a9269aff1296a52d1eb4a 100644 --- a/src/GuiQt/ProgressReportingWithSlots.cxx +++ b/src/GuiQt/ProgressReportingWithSlots.cxx @@ -44,7 +44,10 @@ ProgressReportingWithSlots::ProgressReportingWithSlots(QObject* parent) : QObject(parent), ProgressReportingInterface() { +#if QT_VERSION >= 0x050000 +#else m_synchronizeMutex.unlock(); +#endif m_cancelled = false; } diff --git a/src/GuiQt/QGLWidgetTextRenderer.cxx b/src/GuiQt/QGLWidgetTextRenderer.cxx index 6ef5abb555c7cf28ff1b6b8c95b64d53d9ee8544..0afbab00f0b8ef2dda7a65875c7c6c5a89e92a99 100644 --- a/src/GuiQt/QGLWidgetTextRenderer.cxx +++ b/src/GuiQt/QGLWidgetTextRenderer.cxx @@ -19,10 +19,23 @@ */ /*LICENSE_END*/ +/* + * QOpenGLWidget does not have renderText() methods. + * renderText() methods are in deprecated QGLWidget. + */ +#ifndef WORKBENCH_USE_QT5_QOPENGL_WIDGET + #define __QT_OPEN_G_L_TEXT_RENDERER_DECLARE__ #include "QGLWidgetTextRenderer.h" #undef __QT_OPEN_G_L_TEXT_RENDERER_DECLARE__ +/* + * When GLEW is used, CaretOpenGLInclude.h will include "Gl/glew.h". + * Gl/glew.h MUST BE BEFORE Gl/gl.h and Gl/gl.h is included by + * QGLWidget so, we must include CaretOpenGL.h before QGLWidget. + */ +#include "CaretOpenGLInclude.h" + #include #include @@ -340,7 +353,7 @@ QGLWidgetTextRenderer::drawHorizontalTextAtWindowCoords(const double windowX, const AString text = annotationText.getText(); double bottomLeft[3], bottomRight[3], topRight[3], topLeft[3]; - getBoundsForTextAtViewportCoords(annotationText, windowX, windowY, 0.0, viewport[3], bottomLeft, bottomRight, topRight, topLeft); + getBoundsForTextAtViewportCoords(annotationText, windowX, windowY, 0.0, viewport[2], viewport[3], bottomLeft, bottomRight, topRight, topLeft); double left = bottomLeft[0]; double right = bottomRight[0]; @@ -672,6 +685,8 @@ QGLWidgetTextRenderer::applyForegroundColoring(const AnnotationText& annotationT * * @param annotationText * Text for width and height estimation. + * @param viewportWidth + * Height of the viewport needed for percentage height text. * @param viewportHeight * Height of the viewport needed for percentage height text. * @param widthOut @@ -681,6 +696,7 @@ QGLWidgetTextRenderer::applyForegroundColoring(const AnnotationText& annotationT */ void QGLWidgetTextRenderer::getTextWidthHeightInPixels(const AnnotationText& annotationText, + const double viewportWidth, const double viewportHeight, double& widthOut, double& heightOut) @@ -697,6 +713,7 @@ QGLWidgetTextRenderer::getTextWidthHeightInPixels(const AnnotationText& annotati 0.0, 0.0, 0.0, + viewportWidth, viewportHeight, bottomLeft, bottomRight, @@ -721,6 +738,8 @@ QGLWidgetTextRenderer::getTextWidthHeightInPixels(const AnnotationText& annotati * Viewport Y-coordinate. * @param viewportZ * Viewport Z-coordinate. + * @param viewportWidth + * Height of the viewport needed for percentage height text. * @param viewportHeight * Height of the viewport needed for percentage height text. * @param bottomLeftOut @@ -737,6 +756,7 @@ QGLWidgetTextRenderer::getBoundsForTextAtViewportCoords(const AnnotationText& an const double viewportX, const double viewportY, const double viewportZ, + const double /*viewportWidth*/, const double /*viewportHeight*/, double bottomLeftOut[3], double bottomRightOut[3], @@ -936,6 +956,7 @@ QGLWidgetTextRenderer::setViewportHeight() glGetIntegerv(GL_VIEWPORT, viewport); + m_viewportWidth = viewport[2]; m_viewportHeight = viewport[3]; } @@ -952,7 +973,8 @@ QGLWidgetTextRenderer::findFont(const AnnotationText& annotationText, const bool creatingDefaultFontFlag) { - const AString fontName = annotationText.getFontRenderingEncodedName(m_viewportHeight); + const AString fontName = annotationText.getFontRenderingEncodedName(m_viewportWidth, + m_viewportHeight); /* * Has the font already has been created? @@ -968,6 +990,7 @@ QGLWidgetTextRenderer::findFont(const AnnotationText& annotationText, * Create and save the font */ FontData* fontData = new FontData(annotationText, + m_viewportWidth, m_viewportHeight); if (fontData->m_valid) { /* @@ -1051,6 +1074,7 @@ QGLWidgetTextRenderer::FontData::FontData() * Height of viewport. */ QGLWidgetTextRenderer::FontData::FontData(const AnnotationText& annotationText, + const int32_t viewportWidth, const int32_t viewportHeight) { m_valid = false; @@ -1088,7 +1112,8 @@ QGLWidgetTextRenderer::FontData::FontData(const AnnotationText& annotationText, m_font = new QFont(fontName); CaretAssert(m_font); - const int32_t fontSizePoints = annotationText.getFontSizeForDrawing(viewportHeight); + const int32_t fontSizePoints = annotationText.getFontSizeForDrawing(viewportWidth, + viewportHeight); m_font->setPointSize(fontSizePoints); m_font->setBold(annotationText.isBoldStyleEnabled()); @@ -1124,3 +1149,5 @@ QGLWidgetTextRenderer::FontData::~FontData() } } +#endif // not - WORKBENCH_USE_QT5_QOPENGL_WIDGET + diff --git a/src/GuiQt/QGLWidgetTextRenderer.h b/src/GuiQt/QGLWidgetTextRenderer.h index 4cddd42523152d72b152086474cd8cc21b3175a9..574cb54c2f6671f8cdb3c3e36da11d505dcfcb64 100644 --- a/src/GuiQt/QGLWidgetTextRenderer.h +++ b/src/GuiQt/QGLWidgetTextRenderer.h @@ -1,6 +1,12 @@ #ifndef __QGLWIDGET_TEXT_RENDERER_H__ #define __QGLWIDGET_TEXT_RENDERER_H__ +/* + * QOpenGLWidget does not have renderText() methods. + * renderText() methods are in deprecated QGLWidget. + */ +#ifndef WORKBENCH_USE_QT5_QOPENGL_WIDGET + /*LICENSE_START*/ /* * Copyright (C) 2015 Washington University School of Medicine @@ -53,6 +59,7 @@ namespace caret { const AnnotationText& annotationText); virtual void getTextWidthHeightInPixels(const AnnotationText& annotationText, + const double viewportWidth, const double viewportHeight, double& widthOut, double& heightOut); @@ -61,6 +68,7 @@ namespace caret { const double viewportX, const double viewportY, const double viewportZ, + const double viewportWidth, const double viewportHeight, double bottomLeftOut[3], double bottomRightOut[3], @@ -79,6 +87,7 @@ namespace caret { FontData(); FontData(const AnnotationText& annotationText, + const int32_t viewportWidth, const int32_t viewportHeight); ~FontData(); @@ -160,6 +169,9 @@ namespace caret { /** Height of the viewport */ int32_t m_viewportHeight; + /** Width of the viewport */ + int32_t m_viewportWidth; + // ADD_NEW_MEMBERS_HERE static const double s_textMarginSize; @@ -170,4 +182,7 @@ namespace caret { #endif // __QT_OPEN_G_L_TEXT_RENDERER_DECLARE__ } // namespace + +#endif // not - WORKBENCH_USE_QT5_QOPENGL_WIDGET + #endif //__QT_OPEN_G_L_TEXT_RENDERER_H__ diff --git a/src/GuiQt/SceneBasePathWidget.cxx b/src/GuiQt/SceneBasePathWidget.cxx new file mode 100644 index 0000000000000000000000000000000000000000..1420706969683cefe62ac522752ada6c951950e4 --- /dev/null +++ b/src/GuiQt/SceneBasePathWidget.cxx @@ -0,0 +1,362 @@ + +/*LICENSE_START*/ +/* + * Copyright (C) 2017 Washington University School of Medicine + * + * 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 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ +/*LICENSE_END*/ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define __SCENE_BASE_PATH_WIDGET_DECLARE__ +#include "SceneBasePathWidget.h" +#undef __SCENE_BASE_PATH_WIDGET_DECLARE__ + +#include "CaretAssert.h" +#include "CaretFileDialog.h" +#include "FileInformation.h" +#include "SceneFile.h" +#include "WuQFactory.h" +#include "WuQMessageBox.h" + +using namespace caret; + + + +/** + * \class caret::SceneBasePathWidget + * \brief Widget for setting scene file base path. + * \ingroup GuiQt + */ + +/** + * Constructor. + * + * @param widget + * Optional parent. + */ +SceneBasePathWidget::SceneBasePathWidget(QWidget* widget) +: QWidget(widget) +{ + m_automaticRadioButton = new QRadioButton("Automatic"); + m_automaticBasePathLineEdit = new QLineEdit; + m_automaticBasePathLineEdit->setReadOnly(true); + + m_customRadioButton = new QRadioButton("Custom"); + + QButtonGroup* buttGroup = new QButtonGroup(this); + buttGroup->addButton(m_automaticRadioButton); + buttGroup->addButton(m_customRadioButton); + QObject::connect(buttGroup, static_cast(&QButtonGroup::buttonClicked), + this, [=](QAbstractButton* button){ this->basePathTypeButtonGroupClicked(button); }); + + m_customBasePathComboBox = WuQFactory::newComboBox(); + m_customBasePathComboBox->setEditable(false); + QObject::connect(m_customBasePathComboBox, static_cast(&QComboBox::activated), + this, [=](int index){ this->customPathComboBoxActivated(index); }); + + QPushButton* copyAutoBasePathPushButton = new QPushButton("Copy"); + copyAutoBasePathPushButton->setToolTip("Copy automatic base path to clipboard"); + QObject::connect(copyAutoBasePathPushButton, &QPushButton::clicked, + this, &SceneBasePathWidget::copyAutoBasePathToClipboard); + + QPushButton* customBrowsePushButton = new QPushButton("Browse..."); + QObject::connect(customBrowsePushButton, &QPushButton::clicked, + this, &SceneBasePathWidget::customBrowseButtonClicked); + + QPushButton* whatsThisPushButton = new QPushButton("What's this?"); + whatsThisPushButton->setSizePolicy(QSizePolicy::Fixed, whatsThisPushButton->sizePolicy().verticalPolicy()); + QObject::connect(whatsThisPushButton, &QPushButton::clicked, + this, &SceneBasePathWidget::whatsThisBasePath); + + QGroupBox* groupBox = new QGroupBox("Base Path"); + QGridLayout* gridLayout = new QGridLayout(groupBox); + gridLayout->setSpacing(2); + gridLayout->setColumnStretch(0, 0); + gridLayout->setColumnStretch(1, 100); + gridLayout->setColumnStretch(2, 0); + int row = 0; + gridLayout->addWidget(whatsThisPushButton, + row, 0, 1, 3, Qt::AlignLeft); + row++; + gridLayout->addWidget(m_automaticRadioButton, + row, 0); + gridLayout->addWidget(m_automaticBasePathLineEdit, + row, 1); + gridLayout->addWidget(copyAutoBasePathPushButton, + row, 2); + row++; + gridLayout->addWidget(m_customRadioButton, + row, 0); + gridLayout->addWidget(m_customBasePathComboBox, + row, 1); + gridLayout->addWidget(customBrowsePushButton, + row, 2); + + QVBoxLayout* widgetLayout = new QVBoxLayout(this); + widgetLayout->setContentsMargins(0, 0, 0, 0); + widgetLayout->addWidget(groupBox); + +// setSizePolicy(sizePolicy().horizontalPolicy(), +// QSizePolicy::Fixed); +} + +/** + * Destructor. + */ +SceneBasePathWidget::~SceneBasePathWidget() +{ +} + +/** + * Update the widget with the given scene file. + * + * @param sceneFile + * The scene file. + */ +void +SceneBasePathWidget::updateWithSceneFile(SceneFile* sceneFile) +{ + m_sceneFile = sceneFile; + + m_automaticBasePathLineEdit->clear(); + + QSignalBlocker customPathSignalBlocker(m_customBasePathComboBox); + + if (m_sceneFile != NULL) { + AString customBaseDirectoryName = m_sceneFile->getBalsaCustomBaseDirectory(); + m_customBasePathComboBox->clear(); + + const std::vector dirNames = m_sceneFile->getBaseDirectoryHierarchyForDataFiles(); + if ( ! customBaseDirectoryName.isEmpty()) { + /* + * If the base directory is in neither the base path hierarchy + * nor the user custom paths, add it to the user custom paths + * so that it appears in the combo box and is selected. + */ + if (std::find(dirNames.begin(), + dirNames.end(), + customBaseDirectoryName) == dirNames.end()) { + if (std::find(s_userCustomBasePaths.begin(), + s_userCustomBasePaths.end(), + customBaseDirectoryName) == s_userCustomBasePaths.end()) { + s_userCustomBasePaths.push_back(customBaseDirectoryName); + } + } + } + for (auto name : s_userCustomBasePaths) { + m_customBasePathComboBox->addItem(name); + } + for (auto name : dirNames) { + m_customBasePathComboBox->addItem(name); + } + + if ( ! customBaseDirectoryName.isEmpty()) { + m_customBasePathComboBox->setCurrentText(customBaseDirectoryName); + } + + switch (m_sceneFile->getBasePathType()) { + case SceneFileBasePathTypeEnum::AUTOMATIC: + m_automaticRadioButton->setChecked(true); + m_customBasePathComboBox->setEnabled(false); + break; + case SceneFileBasePathTypeEnum::CUSTOM: + m_customRadioButton->setChecked(true); + m_customBasePathComboBox->setEnabled(true); + break; + } + + AString baseDirectoryName; + std::vector missingFileNames; + AString errorMessage; + sceneFile->findBaseDirectoryForDataFiles(baseDirectoryName, + missingFileNames, + errorMessage); + m_automaticBasePathLineEdit->setText(baseDirectoryName); + setEnabled(true); + } + else { + m_customBasePathComboBox->clear(); + setEnabled(false); + } +} + +/** + * Copy the automatic base path to the clipboard + */ +void +SceneBasePathWidget::copyAutoBasePathToClipboard() +{ + const QString txt = m_automaticBasePathLineEdit->text().trimmed(); + if ( ! txt.isEmpty()) { + QApplication::clipboard()->setText(txt, + QClipboard::Clipboard); + } +} + +/** + * Called when the user selects and item in the custom + * path combo box. + * + * @param index + * Index of the item selected. + */ +void +SceneBasePathWidget::customPathComboBoxActivated(int index) +{ + const AString text = m_customBasePathComboBox->itemText(index); + if ( ! text.isEmpty()) { + m_sceneFile->setBalsaCustomBaseDirectory(text); + } +} + + +/** + * Gets called when the custom browse button is clicked. + */ +void +SceneBasePathWidget::customBrowseButtonClicked() +{ + const AString msg("The only reason to use this Browse button is when the Automatic mode fails. " + "If you select a base path that is a not a parent directory of the " + "scene file and its data files, zipping of the scene file and its " + "data files will fail.\n\n" + "Do you want to continue?"); + if ( ! WuQMessageBox::warningOkCancel(this, msg)) { + return; + } + + CaretAssert(m_sceneFile); + + /* + * Let user choose directory path + */ + QString directoryName; + FileInformation fileInfo(m_customBasePathComboBox->currentText().trimmed()); + if (fileInfo.exists()) { + if (fileInfo.isDirectory()) { + directoryName = fileInfo.getAbsoluteFilePath(); + } + } + const AString newDirectoryName = CaretFileDialog::getExistingDirectoryDialog(this, + "Choose Base Path", + directoryName); + /* + * If user cancels, return + */ + if (newDirectoryName.isEmpty()) { + return; + } + + if (std::find(s_userCustomBasePaths.begin(), + s_userCustomBasePaths.end(), + newDirectoryName) == s_userCustomBasePaths.end()) { + s_userCustomBasePaths.push_back(newDirectoryName); + } + m_sceneFile->setBasePathType(SceneFileBasePathTypeEnum::CUSTOM); + m_sceneFile->setBalsaCustomBaseDirectory(newDirectoryName); + updateWithSceneFile(m_sceneFile); +} + +/** + * Gets called when on of the radio buttons is clicked. + * + * @param button + * Button that was clicked. + */ +void +SceneBasePathWidget::basePathTypeButtonGroupClicked(QAbstractButton* button) +{ + if (button == m_automaticRadioButton) { + m_sceneFile->setBasePathType(SceneFileBasePathTypeEnum::AUTOMATIC); + } + else if (button == m_customRadioButton) { + m_sceneFile->setBasePathType(SceneFileBasePathTypeEnum::CUSTOM); + } + else { + CaretAssertMessage(0, "Has a new SceneFileBasePathTypeEnum::Enum been added?"); + } + + updateWithSceneFile(m_sceneFile); +} + +/** + * @return True if the selections in the widget are valid, else false. + * + * @param errorMessageOut + * Output contains error information if false returned. + */ +bool +SceneBasePathWidget::isValid(AString& errorMessageOut) const +{ + errorMessageOut.clear(); + + if (m_automaticRadioButton->isChecked()) { + /* valid */ + } + else if (m_customRadioButton->isChecked()) { + const AString basePath = m_customBasePathComboBox->currentText().trimmed(); + if (basePath.isEmpty()) { + errorMessageOut = "CUSTOM base path is empty."; + } + else { + FileInformation fileInfo(basePath); + if (fileInfo.exists() + && fileInfo.isDirectory()) { + /* Valid directory */ + } + else { + errorMessageOut = "CUSTOM base path is not a valid directory"; + } + } + } + else { + CaretAssert(0); + errorMessageOut = "Neither AUTOMATIC nor CUSTOM is selected."; + } + + return (errorMessageOut.isEmpty()); +} + +/** + * Displays a dialog explaining base path options + */ +void +SceneBasePathWidget::whatsThisBasePath() +{ + const AString text("The Automatic Base Path is the \"lowest level\" path that contains the Scene File and " + "all data files referenced by the Scene File. " + "The directory structure of all files contained in the Scene File relative to (at or below) " + "the Base Path will be preserved in your dataset when it is downloaded from BALSA and " + "unzipped by other users. You may set a Custom path above the Automatic Base Path, but " + "it will add additional, unnecessary path layers to the unzipped dataset."); + WuQMessageBox::informationOk(this, + text); + +} + + + diff --git a/src/GuiQt/SceneBasePathWidget.h b/src/GuiQt/SceneBasePathWidget.h new file mode 100644 index 0000000000000000000000000000000000000000..b1f8bd5e1eae40313def1060e05f70e7cb6a2f8e --- /dev/null +++ b/src/GuiQt/SceneBasePathWidget.h @@ -0,0 +1,93 @@ +#ifndef __SCENE_BASE_PATH_WIDGET_H__ +#define __SCENE_BASE_PATH_WIDGET_H__ + +/*LICENSE_START*/ +/* + * Copyright (C) 2017 Washington University School of Medicine + * + * 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 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ +/*LICENSE_END*/ + + + +#include + +#include + +class QAbstractButton; +class QComboBox; +class QLineEdit; +class QRadioButton; + +#include "AString.h" + +namespace caret { + + class SceneFile; + + class SceneBasePathWidget : public QWidget { + + Q_OBJECT + + public: + SceneBasePathWidget(QWidget* parent = 0); + + virtual ~SceneBasePathWidget(); + + void updateWithSceneFile(SceneFile* sceneFile); + + bool isValid(AString& errorMessageOut) const; + + // ADD_NEW_METHODS_HERE + + private slots: + void customBrowseButtonClicked(); + + void customPathComboBoxActivated(int index); + + void copyAutoBasePathToClipboard(); + + void basePathTypeButtonGroupClicked(QAbstractButton* button); + + void whatsThisBasePath(); + + private: + SceneBasePathWidget(const SceneBasePathWidget&); + + SceneBasePathWidget& operator=(const SceneBasePathWidget&); + + SceneFile* m_sceneFile = NULL; + + QComboBox* m_customBasePathComboBox; + + QRadioButton* m_automaticRadioButton; + + QRadioButton* m_customRadioButton; + + QLineEdit* m_automaticBasePathLineEdit; + + static std::vector s_userCustomBasePaths; + + // ADD_NEW_MEMBERS_HERE + + }; + +#ifdef __SCENE_BASE_PATH_WIDGET_DECLARE__ + std::vector SceneBasePathWidget::s_userCustomBasePaths; +#endif // __SCENE_BASE_PATH_WIDGET_DECLARE__ + +} // namespace +#endif //__SCENE_BASE_PATH_WIDGET_H__ diff --git a/src/GuiQt/SceneCreateReplaceDialog.cxx b/src/GuiQt/SceneCreateReplaceDialog.cxx index dc9b3efeae0313a4bc6d7f2ee2a385e216004825..d64250e3f2c0e11917a2238846a9743bef00ec81 100644 --- a/src/GuiQt/SceneCreateReplaceDialog.cxx +++ b/src/GuiQt/SceneCreateReplaceDialog.cxx @@ -25,6 +25,7 @@ #include "SceneCreateReplaceDialog.h" #undef __SCENE_CREATE_REPLACE_DIALOG_DECLARE__ +#include #include #include #include @@ -32,8 +33,10 @@ #include #include #include +#include #include +#include "ApplicationInformation.h" #include "Brain.h" #include "BrainBrowserWindow.h" #include "CaretAssert.h" @@ -48,6 +51,7 @@ #include "PlainTextStringBuilder.h" #include "Scene.h" #include "SceneAttributes.h" +#include "SceneDialog.h" #include "SceneFile.h" #include "SceneInfo.h" #include "SessionManager.h" @@ -129,14 +133,15 @@ SceneCreateReplaceDialog::SceneCreateReplaceDialog(const AString& dialogTitle, m_balsaSceneIDLineEdit = new QLineEdit(); m_balsaSceneIDLineEdit->setToolTip("Scene ID is for use with BALSA Database"); + QPushButton* addWindowDescriptionPushButton = new QPushButton("Add Window Info"); + QObject::connect(addWindowDescriptionPushButton, &QPushButton::clicked, + this, &SceneCreateReplaceDialog::addWindowContentToolButtonClicked); + QLabel* descriptionLabel = new QLabel("Description"); m_descriptionTextEdit = new QPlainTextEdit(); const Qt::Alignment labelAlignment = (Qt::AlignLeft | Qt::AlignTop); - /* - * Layout for widgets - */ int32_t columnCounter = 0; const int32_t labelColumn = columnCounter++; const int32_t widgetColumn = columnCounter++; @@ -157,14 +162,18 @@ SceneCreateReplaceDialog::SceneCreateReplaceDialog(const AString& dialogTitle, infoGridLayout->addWidget(m_balsaSceneIDLineEdit, rowCounter, widgetColumn); rowCounter++; - infoGridLayout->setRowStretch(rowCounter, 100); infoGridLayout->addWidget(descriptionLabel, rowCounter, labelColumn, labelAlignment); infoGridLayout->addWidget(m_descriptionTextEdit, - rowCounter, widgetColumn); + rowCounter, widgetColumn, + 2, 1); + rowCounter++; + infoGridLayout->addWidget(addWindowDescriptionPushButton, + rowCounter, labelColumn, + labelAlignment); + infoGridLayout->setRowStretch(rowCounter, 100); rowCounter++; - infoGridLayout->setRowStretch(rowCounter, 0); infoGridLayout->addWidget(optionsLabel, rowCounter, labelColumn, labelAlignment); @@ -187,40 +196,49 @@ SceneCreateReplaceDialog::SceneCreateReplaceDialog(const AString& dialogTitle, setCentralWidget(dialogWidget, WuQDialog::SCROLL_AREA_NEVER); - QDateTime dateTime = QDateTime::currentDateTime(); - const QString dateTimeText = dateTime.toString("dd MMM yyyy hh:mm:ss"); - - PlainTextStringBuilder description; - description.addLine("Created " + dateTimeText); + PlainTextStringBuilder windowDescriptionBuilder; std::vector windows = GuiManager::get()->getAllOpenBrainBrowserWindows(); for (std::vector::iterator iter = windows.begin(); iter != windows.end(); iter++) { BrainBrowserWindow* window = *iter; - window->getDescriptionOfContent(description); - description.addLine(""); + window->getDescriptionOfContent(windowDescriptionBuilder); + windowDescriptionBuilder.addLine(""); } - QString descriptionString = description.getText().trimmed(); + const AString windowDescription = windowDescriptionBuilder.getText().trimmed(); + + QDateTime dateTime = QDateTime::currentDateTime(); + const QString dateTimeText = dateTime.toString("dd MMM yyyy hh:mm:ss"); + const QString commitName = (ApplicationInformation().getCommit()); + const QString dataTimeCommitText(dateTimeText + "\n" + commitName); + + const AString defaultNewSceneName = ("New Scene " + AString::number(sceneFile->getNumberOfScenes() + 1)); + m_nameLineEdit->setText(defaultNewSceneName); switch (m_mode) { case MODE_ADD_NEW_SCENE: - m_descriptionTextEdit->setPlainText(descriptionString); - break; case MODE_INSERT_NEW_SCENE: - m_descriptionTextEdit->setPlainText(descriptionString); + m_sceneWindowDescription = ("Created on " + dataTimeCommitText + "\n"); + m_sceneWindowDescription.appendWithNewLine(windowDescription); break; case MODE_REPLACE_SCENE: m_nameLineEdit->setText(sceneToInsertOrReplace->getName()); m_balsaSceneIDLineEdit->setText(sceneToInsertOrReplace->getBalsaSceneID()); + m_sceneWindowDescription = ("Replaced on " + dataTimeCommitText + "\n"); + m_sceneWindowDescription.appendWithNewLine(windowDescription); + m_sceneWindowDescription.appendWithNewLine(" "); + m_sceneWindowDescription.appendWithNewLine(sceneToInsertOrReplace->getDescription()); m_descriptionTextEdit->setPlainText(sceneToInsertOrReplace->getDescription()); break; } + m_nameLineEdit->setFocus(); + m_nameLineEdit->selectAll(); + setMinimumWidth(600); setMinimumHeight(300); setSaveWindowPositionForNextTime("SceneCreateDialog"); - } /** @@ -338,9 +356,10 @@ SceneCreateReplaceDialog::replaceExistingScene(QWidget* parent, * Scene to which image is added. */ void -SceneCreateReplaceDialog::addImageToScene(Scene* scene) +SceneCreateReplaceDialog::addImageToScene(Scene* scene, + AString& errorMessageOut) { - AString errorMessage; + errorMessageOut.clear(); CaretAssert(scene); @@ -363,7 +382,7 @@ SceneCreateReplaceDialog::addImageToScene(Scene* scene) if (imageCaptureEvent.getEventProcessCount() > 0) { if (imageCaptureEvent.isError()) { - errorMessage.appendWithNewLine(imageCaptureEvent.getErrorMessage()); + errorMessageOut.appendWithNewLine(imageCaptureEvent.getErrorMessage()); } else { imageFiles.push_back(new ImageFile(imageCaptureEvent.getImage())); @@ -408,9 +427,104 @@ SceneCreateReplaceDialog::addImageToScene(Scene* scene) PREFERRED_IMAGE_FORMAT); } catch (const DataFileException& dfe) { - WuQMessageBox::errorOk(this, - (dfe.whatString() - + "\n\nEven though image failed, scene was created.")); + errorMessageOut.appendWithNewLine((dfe.whatString() + + "\n\nEven though image failed, scene was created.")); + } + } + + /* + * Free memory from the image files. + */ + for (std::vector::iterator iter = imageFiles.begin(); + iter != imageFiles.end(); + iter++) { + delete *iter; + } +} + +/** + * Create an image for the loaded scene. + * + * @param imageOut + * Output image of the scene. + * @param errorMessageOut + * Contains error information if image was not created. + * @return + * True if output image is valid, else false. + */ +bool +SceneCreateReplaceDialog::createSceneImage(QImage& imageOut, + AString& errorMessageOut) +{ + bool validImageFlag = false; + imageOut = QImage(); + errorMessageOut.clear(); + + uint8_t backgroundColor[3] = { 0, 0, 0 }; + bool backgroundColorValid = false; + + /* + * Capture an image of each window + */ + std::vector imageFiles; + std::vector windows = GuiManager::get()->getAllOpenBrainBrowserWindows(); + for (std::vector::iterator iter = windows.begin(); + iter != windows.end(); + iter++) { + BrainBrowserWindow* bbw = *iter; + const int32_t browserWindowIndex = bbw->getBrowserWindowIndex(); + + EventImageCapture imageCaptureEvent(browserWindowIndex); + EventManager::get()->sendEvent(imageCaptureEvent.getPointer()); + + if (imageCaptureEvent.getEventProcessCount() > 0) { + if (imageCaptureEvent.isError()) { + errorMessageOut.appendWithNewLine(imageCaptureEvent.getErrorMessage()); + } + else { + imageFiles.push_back(new ImageFile(imageCaptureEvent.getImage())); + if ( ! backgroundColorValid) { + imageCaptureEvent.getBackgroundColor(backgroundColor); + backgroundColorValid = true; + } + } + } + } + + /* + * Assemble images of each window into a single image + * and add it to the scene. Use one image per row + * since the images are limited in horizontal space + * when shown in the listing of scenes. + */ + if ( ! imageFiles.empty()) { + try { + const int32_t numImagesPerRow = 1; + ImageFile compositeImageFile; + compositeImageFile.combinePreservingAspectAndFillIfNeeded(imageFiles, + numImagesPerRow, + backgroundColor); + + if (backgroundColorValid) { + const int marginSize = 5; + compositeImageFile.cropImageRemoveBackground(marginSize, + backgroundColor); + } + + const int MAXIMUM_IMAGE_WIDTH = 1024; + compositeImageFile.resizeToMaximumWidth(MAXIMUM_IMAGE_WIDTH); + + const AString PREFERRED_IMAGE_FORMAT = "png"; + + QByteArray byteArray; + compositeImageFile.getImageInByteArray(byteArray, + PREFERRED_IMAGE_FORMAT); + + imageOut = *compositeImageFile.getAsQImage(); + validImageFlag = true; + } + catch (const DataFileException&) { + errorMessageOut.appendWithNewLine("Even though image generation failed, scene was created."); } } @@ -422,6 +536,8 @@ SceneCreateReplaceDialog::addImageToScene(Scene* scene) iter++) { delete *iter; } + + return validImageFlag; } /** @@ -515,6 +631,18 @@ SceneCreateReplaceDialog::okButtonClicked() return; } + if ( ! s_previousSelections.m_addModifiedPaletteSettings) { + if ( ! SceneDialog::checkForModifiedFiles(GuiManager::TEST_FOR_MODIFIED_FILES_PALETTE_ONLY_MODE_FOR_SCENE_ADD, + this)) { + /* + * Add modified palettes to scene is off but + * there are modified palettes and user has + * chose to not create the scene + */ + return; + } + } + Scene* newScene = new Scene(SceneTypeEnum::SCENE_TYPE_FULL); Scene::setSceneBeingCreated(newScene); newScene->setName(newSceneName); @@ -553,7 +681,13 @@ SceneCreateReplaceDialog::okButtonClicked() newScene->addClass(GuiManager::get()->saveToScene(sceneAttributes, "guiManager")); - addImageToScene(newScene); + AString imageErrorMessage; + addImageToScene(newScene, + imageErrorMessage); + if ( ! imageErrorMessage.isEmpty()) { + WuQMessageBox::errorOk(this, + imageErrorMessage); + } switch (m_mode) { case MODE_ADD_NEW_SCENE: @@ -576,4 +710,15 @@ SceneCreateReplaceDialog::okButtonClicked() WuQDialogModal::okButtonClicked(); } +/** + * Called when add window content button clicked + */ +void +SceneCreateReplaceDialog::addWindowContentToolButtonClicked() +{ + AString txt = m_descriptionTextEdit->document()->toPlainText(); + txt.appendWithNewLine(m_sceneWindowDescription); + m_descriptionTextEdit->setPlainText(txt); +} + diff --git a/src/GuiQt/SceneCreateReplaceDialog.h b/src/GuiQt/SceneCreateReplaceDialog.h index 7ae6f851760c51511cacea09f787b295cf79aa02..79dcbbb2c221f2ea330a3099478219f30abb92b0 100644 --- a/src/GuiQt/SceneCreateReplaceDialog.h +++ b/src/GuiQt/SceneCreateReplaceDialog.h @@ -50,6 +50,12 @@ namespace caret { SceneFile* sceneFile, Scene* sceneToReplace); + static void addImageToScene(Scene* scene, + AString& errorMessageOut); + + static bool createSceneImage(QImage& imageOut, + AString& errorMessageOut); + virtual ~SceneCreateReplaceDialog(); private: @@ -76,11 +82,12 @@ namespace caret { protected: virtual void okButtonClicked(); + private slots: + void addWindowContentToolButtonClicked(); + private: // ADD_NEW_MEMBERS_HERE - void addImageToScene(Scene* scene); - SceneFile* m_sceneFile; Mode m_mode; @@ -106,6 +113,8 @@ namespace caret { QCheckBox* m_addModifiedPaletteSettingsCheckBox; + AString m_sceneWindowDescription; + struct PreviousSelections { bool m_addSpecFileNameToScene; bool m_addAllTabs; diff --git a/src/GuiQt/SceneDialog.cxx b/src/GuiQt/SceneDialog.cxx index 802829b7024cf8bbf5c85036f7e6978bcd36e1de..87eb67aae3a42ffd3d5579ff5fc21e5b9e0870b2 100644 --- a/src/GuiQt/SceneDialog.cxx +++ b/src/GuiQt/SceneDialog.cxx @@ -21,6 +21,7 @@ #include +#include #include #include #include @@ -33,6 +34,9 @@ #include #include #include +#include +#include +#include #include #include @@ -40,6 +44,7 @@ #include "SceneDialog.h" #undef __SCENE_DIALOG_DECLARE__ +#include "ApplicationInformation.h" #include "BalsaDatabaseUploadSceneFileDialog.h" #include "Brain.h" #include "BrainBrowserWindow.h" @@ -62,6 +67,7 @@ #include "EventManager.h" #include "EventModelGetAll.h" #include "EventUserInterfaceUpdate.h" +#include "EventShowDataFileReadWarningsDialog.h" #include "FileInformation.h" #include "GuiManager.h" #include "ImageFile.h" @@ -73,11 +79,13 @@ #include "SceneFile.h" #include "SceneInfo.h" #include "ScenePreviewDialog.h" +#include "SceneShowOptionsDialog.h" #include "SessionManager.h" #include "UsernamePasswordWidget.h" #include "WuQDataEntryDialog.h" #include "WuQDialogNonModal.h" #include "WuQMessageBox.h" +#include "WuQTextEditorDialog.h" #include "WuQtUtilities.h" #include "WuQWidgetObjectGroup.h" #include "ZipSceneFileDialog.h" @@ -104,15 +112,34 @@ SceneDialog::SceneDialog(QWidget* parent) { m_selectedSceneClassInfoIndex = -1; + m_replaceAllScenesDescription = ("This operation may be slow as it loads and replaces all scenes."); + m_testAllScenesDescription = ("This operation may be slow as it requires displaying all scenes. " + "\n" + "Each scene is displayed and an image of the graphic(s) regions is generated " + "from the scene. After all scenes have been loaded, a dialog is " + "displayed that shows a scene's preview image on the left and " + "on the right, the image created by loading the scene. If the " + "two images do not match, it indicates a problem with the scene. " + "Possible problems include data files missing, data files that " + "have changed, or a change to the software."); + m_testAllScenesDescription = WuQtUtilities::createWordWrappedToolTipText(m_testAllScenesDescription); + /* * No apply buton */ setApplyButtonText(""); + + /* + * Icons + */ + m_cautionIconValid = WuQtUtilities::loadIcon(":/SceneFileDialog/caution.png", + m_cautionIcon); + /* * Set the dialog's widget */ - this->setCentralWidget(createMainPage(), + this->setCentralWidget(createScenesWidget(), WuQDialog::SCROLL_AREA_NEVER); /* @@ -135,6 +162,8 @@ SceneDialog::SceneDialog(QWidget* parent) * so that our receiveEvent() method is called after the files have been * read. */ + EventManager::get()->addProcessedEventListener(this, + EventTypeEnum::EVENT_DATA_FILE_ADD); EventManager::get()->addProcessedEventListener(this, EventTypeEnum::EVENT_DATA_FILE_READ); EventManager::get()->addProcessedEventListener(this, @@ -152,6 +181,53 @@ SceneDialog::~SceneDialog() EventManager::get()->removeAllEventsFromListener(this); } +/** + * Process a close event. + * + * @param event + * The close event. + */ +void +SceneDialog::closeEvent(QCloseEvent* event) +{ + WuQDialogNonModal::closeEvent(event); +} + +/** + * Called when the close button is clicked. + */ +void +SceneDialog::closeButtonClicked() +{ + /* + * If there are any modified scene files, inform the user + */ + Brain* brain = GuiManager::get()->getBrain(); + const int32_t numSceneFiles = brain->getNumberOfSceneFiles(); + int32_t numberOfModifiedSceneFiles = 0; + for (int32_t i = 0; i < numSceneFiles; i++) { + const SceneFile* sceneFile = brain->getSceneFile(i); + if (sceneFile->isModified()) { + numberOfModifiedSceneFiles++; + } + } + + bool allowToCloseFlag = true; + + if (numberOfModifiedSceneFiles > 0) { + const AString msg("There are modified scene file(s). You can save modified scene files using File Menu->Save/Manage Files " + "or by selecting them on the Scene Dialog and using the Save/Save As buttons.

    " + "Continue closing this Scene Dialog?"); + if ( ! WuQMessageBox::warningOkCancel(this, msg)) { + allowToCloseFlag = false; + } + } + + if (allowToCloseFlag) { + WuQDialogNonModal::closeButtonClicked(); + } +} + /** * Update the scene dialog. */ @@ -160,8 +236,28 @@ SceneDialog::updateDialog() { loadSceneFileComboBox(NULL); loadScenesIntoDialog(NULL); + + updateSceneFileModifiedStatusLabel(); +} + +/** + * Create the default scene file if + * there are any models and there are no + * scene files. + */ +void +SceneDialog::createDefaultSceneFile() +{ + if (getSelectedSceneFile() == NULL) { + EventModelGetAll allModelsEvent; + EventManager::get()->sendEvent(allModelsEvent.getPointer()); + if ( ! allModelsEvent.getModels().empty()) { + newSceneFileButtonClicked(); + } + } } + /** * Get the selected scene file. * @return SceneFile or NULL if no scene file. @@ -219,7 +315,36 @@ SceneDialog::getSelectedScene() return selectedScene; } - +/** + * Update the modified icons in the scene file combo box. + */ +void +SceneDialog::updateSceneFileComboBoxModifiedIcons() +{ + if ( ! m_cautionIconValid) { + return; + } + + m_sceneFileSelectionComboBox->blockSignals(true); + + const int32_t numItems = m_sceneFileSelectionComboBox->count(); + for (int32_t itemIndex = 0; itemIndex < numItems; itemIndex++) { + void* filePointer = m_sceneFileSelectionComboBox->itemData(itemIndex).value(); + SceneFile* sceneFile = (SceneFile*)filePointer; + CaretAssert(sceneFile); + if (sceneFile->isModified()) { + m_sceneFileSelectionComboBox->setItemIcon(itemIndex, + m_cautionIcon); + } + else { + m_sceneFileSelectionComboBox->setItemIcon(itemIndex, + QIcon()); + } + } + + m_sceneFileSelectionComboBox->blockSignals(false); + /* comment */ +} /** * Load the scene files into the scene file combo box. @@ -248,33 +373,26 @@ SceneDialog::loadSceneFileComboBox(SceneFile* selectedSceneFileIn) } const AString name = sceneFile->getFileNameNoPath(); - m_sceneFileSelectionComboBox->addItem(name, - qVariantFromValue((void*)sceneFile)); + if (sceneFile->isModified() + && m_cautionIconValid) { + m_sceneFileSelectionComboBox->addItem(m_cautionIcon, + name, + qVariantFromValue((void*)sceneFile)); + } + else { + m_sceneFileSelectionComboBox->addItem(name, + qVariantFromValue((void*)sceneFile)); + } } if (numSceneFiles > 0) { m_sceneFileSelectionComboBox->setCurrentIndex(defaultFileComboIndex); - loadSceneFileMetaDataWidgets(); } m_sceneFileButtonsGroup->setEnabled(getSelectedSceneFile() != NULL); -} -/** - * Load the Scene File BALSA Study ID Line Edit - */ -void -SceneDialog::loadSceneFileMetaDataWidgets() -{ - m_fileBalsaStudyIDLineEdit->clear(); - m_fileBaseDirectoryLineEdit->clear(); - - SceneFile* sceneFile = getSelectedSceneFile(); - if (sceneFile != NULL) { - m_fileBalsaStudyIDLineEdit->setText(sceneFile->getBalsaStudyID()); - m_fileBaseDirectoryLineEdit->setText(sceneFile->getBaseDirectory()); - } + updateSceneFileModifiedStatusLabel(); } /** @@ -362,22 +480,46 @@ SceneDialog::loadScenesIntoDialog(Scene* selectedSceneIn) const bool validFile = (getSelectedSceneFile() != NULL); - bool validScene = false; + bool validScene = false; + bool haveScenes = false; if (validFile) { const Scene* scene = getSelectedScene(); if (scene != NULL) { validScene = true; } + haveScenes = (sceneFile->getNumberOfScenes() > 0); } enableSceneMoveUpAndDownButtons(); m_addNewScenePushButton->setEnabled(validFile); m_deleteScenePushButton->setEnabled(validScene); + if (m_replaceAllScenesPushButton != NULL) { + m_replaceAllScenesPushButton->setEnabled(haveScenes); + } + m_testScenesPushButton->setEnabled(haveScenes); m_insertNewScenePushButton->setEnabled(validScene); m_replaceScenePushButton->setEnabled(validScene); m_showScenePushButton->setEnabled(validScene); m_showSceneImagePreviewPushButton->setEnabled(validScene); + m_showSceneOptionsPushButton->setEnabled(validScene); +} + +/** + * @return Number of valid scene info widgets. + */ +int32_t +SceneDialog::getNumberOfValidSceneInfoWidgets() const +{ + int32_t numValid = 0; + + for (auto ssiw : m_sceneClassInfoWidgets) { + if (ssiw->isValid()) { + numValid++; + } + } + + return numValid; } /** @@ -386,7 +528,7 @@ SceneDialog::loadScenesIntoDialog(Scene* selectedSceneIn) void SceneDialog::enableSceneMoveUpAndDownButtons() { - const int32_t numberOfSceneInfoWidgets = static_cast(m_sceneClassInfoWidgets.size()); + const int32_t numberOfSceneInfoWidgets = getNumberOfValidSceneInfoWidgets(); m_moveSceneUpPushButton->setEnabled(m_selectedSceneClassInfoIndex > 0); m_moveSceneDownPushButton->setEnabled((m_selectedSceneClassInfoIndex >= 0) && (m_selectedSceneClassInfoIndex < (numberOfSceneInfoWidgets - 1))); @@ -460,187 +602,504 @@ SceneDialog::highlightScene(const Scene* scene) } /** - * Called to create a new scene file. + * Called to open a scene file. */ -void -SceneDialog::newSceneFileButtonClicked() +void +SceneDialog::openSceneFileButtonClicked() { + if ( ! displayNewSceneWarning()) { + return; + } + + if ( ! warnIfSceneFileIsModified(ModifiedWarningType::OPEN_FILE_BUTTON)) { + return; + } + /* * Let user choose a different path/name */ - SceneFile* newSceneFile = new SceneFile(); - GuiManager::get()->getBrain()->convertDataFilePathNameToAbsolutePathName(newSceneFile); - AString newSceneFileName = CaretFileDialog::getSaveFileNameDialog(DataFileTypeEnum::SCENE, - this, - "Choose Scene File Name", - newSceneFile->getFileName()); + AString openSceneFileName = CaretFileDialog::getOpenFileNameDialog(DataFileTypeEnum::SCENE, + m_openSceneFilePushButton, + "Open a Scene File", + ""); /* - * If user cancels, delete the new scene file and return + * If user cancels, return */ - if (newSceneFileName.isEmpty()) { - delete newSceneFile; + if (openSceneFileName.isEmpty()) { return; } + CursorDisplayScoped cursor; + cursor.showWaitCursor(); + /* - * Set name of new scene file and add to brain + * Note that this dialog receives EventDataFileRead + * as a post-processed event and will take care of + * the dialog updates. */ - newSceneFile->setFileName(newSceneFileName); - EventManager::get()->sendEvent(EventDataFileAdd(newSceneFile).getPointer()); - this->loadSceneFileComboBox(newSceneFile); - this->sceneFileSelected(); + EventDataFileRead dataFileEvent(GuiManager::get()->getBrain()); + dataFileEvent.addDataFile(DataFileTypeEnum::SCENE, openSceneFileName); + EventManager::get()->sendEvent(dataFileEvent.getPointer()); + + EventManager::get()->sendEvent(EventShowDataFileReadWarningsDialog().getPointer()); } + /** - * Called when upload scene file is selected. + * Called to create a new scene file. */ -void -SceneDialog::uploadSceneFileButtonClicked() +void +SceneDialog::newSceneFileButtonClicked() { - SceneFile* sceneFile = getSelectedSceneFile(); - if (sceneFile == NULL) { - WuQMessageBox::errorOk(m_uploadSceneFilePushButton, - "No Scene File is selected."); - return; - } - if (sceneFile->isModified()) { - WuQMessageBox::errorOk(m_uploadSceneFilePushButton, - "Selected Scene File is modified and must be saved prior to uploading."); + if ( ! displayNewSceneWarning()) { return; } - if (sceneFile->getBalsaStudyID().trimmed().isEmpty()) { - const QString msg("The BALSA Study ID is missing. You must go to the " - "BALSA Database and get a BALSA Study ID."); - WuQMessageBox::errorOk(m_uploadSceneFilePushButton, msg); + if ( ! warnIfSceneFileIsModified(ModifiedWarningType::NEW_FILE_BUTTON)) { return; } - BalsaDatabaseUploadSceneFileDialog uploadDialog(sceneFile, - this); - uploadDialog.exec(); -} + SceneFile* newSceneFile = new SceneFile(); + GuiManager::get()->getBrain()->convertDataFilePathNameToAbsolutePathName(newSceneFile); + newSceneFile->clearModified(); + /* + * Note that this dialog receives EventDataFileAdd + * as a post-processed event and will take care of + * the dialog updates. + */ + EventManager::get()->sendEvent(EventDataFileAdd(newSceneFile).getPointer()); +} /** - * Called when zip scene file is selected. + * Save the selected scene file. */ void -SceneDialog::zipSceneFileButtonClicked() +SceneDialog::saveSceneFileButtonClicked() +{ + saveSelectedSceneFile(); +} + +/** + * Save the selected scene file with a new name. + */ +void SceneDialog::saveAsSceneFileButtonClicked() +{ + saveAsSelectedSceneFile(); +} + +/** + * Save the selected scene file using the file' current name. + * + * @return True if file was saved else false and an error message is displayed. + */ +bool +SceneDialog::saveSelectedSceneFile() { SceneFile* sceneFile = getSelectedSceneFile(); if (sceneFile == NULL) { - WuQMessageBox::errorOk(m_zipSceneFilePushButton, - "No Scene File is selected."); - return; + WuQMessageBox::errorOk(m_saveSceneFilePushButton, "There is no selected scene file to save"); + return false; } - if (sceneFile->isModified()) { - WuQMessageBox::errorOk(m_zipSceneFilePushButton, - "Selected Scene File is modified and must be saved prior to zipping."); - return; + + try { + CursorDisplayScoped cursor; + cursor.showWaitCursor(); + + Brain* brain = GuiManager::get()->getBrain(); + brain->writeDataFile(sceneFile); + } + catch (const DataFileException& e) { + WuQMessageBox::errorOk(m_saveSceneFilePushButton, e.whatString()); + return false; } - ZipSceneFileDialog zipDialog(sceneFile, - this); - zipDialog.exec(); + loadSceneFileComboBox(sceneFile); + + return true; } /** - * Called when a scene file is selected. + * Save the selected scene file with display of a file selection dialog using the file' current name. + * + * @return True if file was saved else false and an error message is displayed. */ -void -SceneDialog::sceneFileSelected() +bool +SceneDialog::saveAsSelectedSceneFile() { - loadScenesIntoDialog(NULL); - loadSceneFileMetaDataWidgets(); + SceneFile* sceneFile = getSelectedSceneFile(); + if (sceneFile == NULL) { + WuQMessageBox::errorOk(m_saveAsSceneFilePushButton, "There is no selected scene file to save"); + return false; + } + + const AString filename = CaretFileDialog::getSaveFileNameDialog(sceneFile->getDataFileType(), + m_saveAsSceneFilePushButton, + "Save Scene File As", + sceneFile->getFileName()); + if (filename.isEmpty()) { + return false; + } + + try { + CursorDisplayScoped cursor; + cursor.showWaitCursor(); + + sceneFile->setFileName(filename); + Brain* brain = GuiManager::get()->getBrain(); + brain->writeDataFile(sceneFile); + } + catch (const DataFileException& e) { + WuQMessageBox::errorOk(m_saveAsSceneFilePushButton, e.whatString()); + return false; + } + + loadSceneFileComboBox(sceneFile); + + return true; } /** - * Called when Edit Study ID button is clicked. + * If the selected scene file is modified, warn the user and + * suggest saving the file prior to continuing. + * + * @param warningType + * Type of warning. + * @return + * True if the operation should continue, else false. */ -void -SceneDialog::editFileBalsaStudyIDButtonClicked() +bool +SceneDialog::warnIfSceneFileIsModified(const ModifiedWarningType warningType) { - SceneFile* sceneFile = getSelectedSceneFile(); + const SceneFile* sceneFile = getSelectedSceneFile(); if (sceneFile == NULL) { - return; + return true; + } + if ( ! sceneFile->isModified()) { + return true; + } + + /** + * Return true if warning is disabled for an operation + */ + switch (warningType) { + case ModifiedWarningType::CLOSE_DIALOG_BUTTON: + break; + case ModifiedWarningType::FILE_COMBO_BOX_CHANGE_FILE: + return true; + break; + case ModifiedWarningType::NEW_FILE_BUTTON: + return true; + break; + case ModifiedWarningType::OPEN_FILE_BUTTON: + return true; + break; + case ModifiedWarningType::UPLOAD_FILE_BUTTON: + break; + case ModifiedWarningType::ZIP_FILE_BUTTON: + break; + } + + /* + * Parent for message box dialogs + */ + QWidget* messageBoxParent = NULL; + switch (warningType) { + case ModifiedWarningType::CLOSE_DIALOG_BUTTON: + messageBoxParent = this; + break; + case ModifiedWarningType::FILE_COMBO_BOX_CHANGE_FILE: + messageBoxParent = m_sceneFileSelectionComboBox; + break; + case ModifiedWarningType::NEW_FILE_BUTTON: + messageBoxParent = m_newSceneFilePushButton; + break; + case ModifiedWarningType::OPEN_FILE_BUTTON: + messageBoxParent = m_openSceneFilePushButton; + break; + case ModifiedWarningType::UPLOAD_FILE_BUTTON: + messageBoxParent = m_uploadSceneFilePushButton; + break; + case ModifiedWarningType::ZIP_FILE_BUTTON: + messageBoxParent = m_zipSceneFilePushButton; + break; + } + CaretAssert(messageBoxParent); + + if (sceneFile->getNumberOfScenes() <= 0) { + switch (warningType) { + case ModifiedWarningType::CLOSE_DIALOG_BUTTON: + return true; + break; + case ModifiedWarningType::FILE_COMBO_BOX_CHANGE_FILE: + return true; + break; + case ModifiedWarningType::NEW_FILE_BUTTON: + return true; + break; + case ModifiedWarningType::OPEN_FILE_BUTTON: + return true; + break; + case ModifiedWarningType::UPLOAD_FILE_BUTTON: + break; + case ModifiedWarningType::ZIP_FILE_BUTTON: + break; + } + + WuQMessageBox::errorOk(messageBoxParent, + "There are no scenes in the scene file !"); + return false; + } + + bool enableContinueButton = false; + bool enableSaveButton = sceneFile->exists(); + QMessageBox::Icon messageBoxIcon = QMessageBox::NoIcon; + + /* + * Set up the informative text + */ + AString saveText = ("Press the Save button to save the scene file with its current name "); + AString saveAsText = ("Press the Save As button to save the scene file to a new name using a file dialog "); + AString continueText = ("Press the Continue button to skip saving the scene file (you may save it at a later time) "); + AString cancelText = ("Press the Cancel button to skip saving the scene file and "); + switch (warningType) { + case ModifiedWarningType::CLOSE_DIALOG_BUTTON: + messageBoxIcon = QMessageBox::Warning; + enableContinueButton = true; + saveText.append("and close the scene dialog"); + saveAsText.append("and close the scene dialog"); + continueText.append("and close the scene dialog"); + cancelText.append("do not close the scene dialog"); + break; + case ModifiedWarningType::FILE_COMBO_BOX_CHANGE_FILE: + messageBoxIcon = QMessageBox::Warning; + enableContinueButton = true; + saveText.append("and change the selected scene file"); + saveAsText.append("and change the selected scene file"); + continueText.append("and change the selected scene file"); + cancelText.append("cancel changing the selected scene file"); + break; + case ModifiedWarningType::NEW_FILE_BUTTON: + messageBoxIcon = QMessageBox::Warning; + enableContinueButton = true; + saveText.append(" and create a new scene file"); + saveAsText.append(" and create a new scene file"); + continueText.append(" and create a new scene file"); + cancelText.append("cancel creation of a new scene file"); + break; + case ModifiedWarningType::OPEN_FILE_BUTTON: + messageBoxIcon = QMessageBox::Warning; + enableContinueButton = true; + saveText.append("and display a dialog for opening a scene file"); + saveAsText.append("and display a dialog for opening a scene file"); + continueText.append("and display a dialog for opening a scene file"); + cancelText.append("cancel opening a scene file"); + break; + case ModifiedWarningType::UPLOAD_FILE_BUTTON: + messageBoxIcon = QMessageBox::Warning; + saveText.append("and continue uploading the scene file"); + saveAsText.append("and continue uploading the scene file"); + continueText.append(""); + cancelText.append("cancel uploading the scene file"); + break; + case ModifiedWarningType::ZIP_FILE_BUTTON: + messageBoxIcon = QMessageBox::Warning; + saveText.append("and zip the scene file"); + saveAsText.append("and zip the scene file"); + continueText.append("and zip the scene file"); + cancelText.append("and cancel zipping the scene file"); + break; + } + + AString informativeText(""); + if (enableSaveButton) { + informativeText.append(saveText + "

    "); + } + informativeText.append(saveAsText + "

    "); + if (enableContinueButton) { + informativeText.append(continueText + "

    "); + } + informativeText.append(cancelText + "

    "); + informativeText.append(""); + + /* + * Create and display the message box dialog + */ + QMessageBox messageBox(messageBoxParent); + messageBox.setText("The selected scene file has been modified but not saved: " + + sceneFile->getFileNameNoPath()); + messageBox.setInformativeText(informativeText); + messageBox.setIcon(messageBoxIcon); + QPushButton* saveButton = NULL; + if (enableSaveButton) { + saveButton = messageBox.addButton("Save", QMessageBox::AcceptRole); + } + QPushButton* saveAsButton = messageBox.addButton("Save As...", QMessageBox::AcceptRole); + QPushButton* cancelButton = messageBox.addButton("Cancel", QMessageBox::NoRole); + QPushButton* continueButton = NULL; + if (enableContinueButton) { + continueButton = messageBox.addButton("Continue", QMessageBox::AcceptRole); } - WuQDataEntryDialog ded("Edit BALSA Database Info", - m_editBalsaStudyIDPushButton, - WuQDialog::SCROLL_AREA_AS_NEEDED); + if (saveButton != NULL) { + messageBox.setDefaultButton(saveButton); + } + else { + messageBox.setDefaultButton(saveAsButton); + } - QLineEdit* lineEdit = ded.addLineEditWidget("BALSA Study ID"); - lineEdit->setText(sceneFile->getBalsaStudyID()); - lineEdit->setMinimumWidth(200); - if (ded.exec() == WuQDataEntryDialog::Accepted) { - const AString idText = lineEdit->text().trimmed(); - sceneFile->setBalsaStudyID(idText); - loadSceneFileMetaDataWidgets(); + messageBox.exec(); + + QAbstractButton* button = messageBox.clickedButton(); + CaretAssert(button); + + bool successFlag = false; + + if (button == saveButton) { + successFlag = saveSelectedSceneFile(); + } + else if (button == saveAsButton) { + successFlag = saveAsSelectedSceneFile(); + + } + else if (button == cancelButton) { + successFlag = false; + + } + else if (button == continueButton) { + successFlag = true; } + else { + CaretAssertMessage(0, "Button clicked not handled " + button->text()); + } + + return successFlag; } /** - * Called when upload scene file is selected. + * Display a message if there are missing files in the Scene File. + * + * @param missingFilesMode + * The missing file mode. + * @return + * True if there are no missing files, else false. */ -void -SceneDialog::editBaseDirectoryPushButtonClicked() +bool +SceneDialog::warnIfMissingFilesInSceneFile(SceneFile* sceneFile, + const MissingFilesMode missingFilesMode) { - SceneFile* sceneFile = getSelectedSceneFile(); - if (sceneFile == NULL) { - return; + CaretAssert(sceneFile); + + AString baseDirectoryName; + std::vector missingFileNames; + AString errorMessage; + sceneFile->findBaseDirectoryForDataFiles(baseDirectoryName, + missingFileNames, + errorMessage); + + if (missingFileNames.empty()) { + return true; } - WuQDataEntryDialog ded("Edit Base Directory", - m_editBaseDirectoryPushButton, - WuQDialog::SCROLL_AREA_AS_NEEDED); + AString verbText; + AString acceptButtonText; + switch (missingFilesMode) { + case MissingFilesMode::UPLOAD: + acceptButtonText = "Upload"; + verbText = "uploading to BALSA"; + break; + case MissingFilesMode::ZIP: + acceptButtonText = "Zip"; + verbText = "zipping the scene file"; + break; + } - QLineEdit* lineEdit = ded.addLineEditWidget("Base Directory"); - lineEdit->setText(sceneFile->getBaseDirectory()); - lineEdit->setMinimumWidth(500); - if (ded.exec() == WuQDataEntryDialog::Accepted) { - const AString idText = lineEdit->text().trimmed(); - sceneFile->setBaseDirectory(idText); - loadSceneFileMetaDataWidgets(); + const AString labelText("Do you want to continue " + verbText + " ?"); + AString text; + for (auto name : missingFileNames) { + text.append(name + "\n"); } + + WuQDataEntryDialog dialog("Data Files Not Found", + this, + false); + dialog.setTextAtTop("These data files were not found but are used by scenes in the scene file. Scenes may not display correctly. " + + labelText, + true); + dialog.addTextEdit("", text, true); + dialog.setMinimumWidth(500); + + if (dialog.exec() == WuQDataEntryDialog::Accepted) { + return true; + } + return false; } + /** * Called when upload scene file is selected. */ void -SceneDialog::browseBaseDirectoryPushButtonClicked() +SceneDialog::uploadSceneFileButtonClicked() { + if ( ! warnIfSceneFileIsModified(ModifiedWarningType::UPLOAD_FILE_BUTTON)) { + return; + } + SceneFile* sceneFile = getSelectedSceneFile(); - if (sceneFile == NULL) { + + if ( ! warnIfMissingFilesInSceneFile(sceneFile, + MissingFilesMode::UPLOAD)) { return; } - /* - * Let user choose directory path - */ - QString directoryName; - FileInformation fileInfo(sceneFile->getBaseDirectory()); - if (fileInfo.exists()) { - if (fileInfo.isDirectory()) { - directoryName = fileInfo.getAbsoluteFilePath(); - } + BalsaDatabaseUploadSceneFileDialog uploadDialog(sceneFile, + this); + uploadDialog.exec(); + + /* Scene ID may have been added and it is shown in info section for each scene */ + loadScenesIntoDialog(NULL); + + updateSceneFileModifiedStatusLabel(); +} + + +/** + * Called when zip scene file is selected. + */ +void +SceneDialog::zipSceneFileButtonClicked() +{ + if ( ! warnIfSceneFileIsModified(ModifiedWarningType::ZIP_FILE_BUTTON)) { + return; } - AString newDirectoryName = CaretFileDialog::getExistingDirectoryDialog(m_browseBaseDirectoryPushButton, - "Choose Base Directory", - directoryName); - /* - * If user cancels, return - */ - if (newDirectoryName.isEmpty()) { + + SceneFile* sceneFile = getSelectedSceneFile(); + + if ( ! warnIfMissingFilesInSceneFile(sceneFile, + MissingFilesMode::ZIP)) { return; } - /* - * Set name of new scene file and add to brain - */ - sceneFile->setBaseDirectory(newDirectoryName); - loadSceneFileMetaDataWidgets(); + ZipSceneFileDialog zipDialog(sceneFile, + this); + zipDialog.exec(); + + updateSceneFileModifiedStatusLabel(); +} + +/** + * Called when a scene file is selected. + * + * @param index + * Index of item selected. + */ +void +SceneDialog::sceneFileSelected(int /*index*/) +{ + loadScenesIntoDialog(NULL); + + updateSceneFileModifiedStatusLabel(); } /** @@ -649,7 +1108,12 @@ SceneDialog::browseBaseDirectoryPushButtonClicked() void SceneDialog::addNewSceneButtonClicked() { - if ( ! checkForModifiedFiles(true)) { + if ( ! displayNewSceneWarning()) { + return; + } + + if ( ! checkForModifiedFiles(GuiManager::TEST_FOR_MODIFIED_FILES_EXCLUDING_PALETTES_MODE_FOR_SCENE_ADD, + this)) { return; } @@ -663,6 +1127,9 @@ SceneDialog::addNewSceneButtonClicked() loadScenesIntoDialog(newScene); } + + + updateSceneFileModifiedStatusLabel(); } /** @@ -671,7 +1138,12 @@ SceneDialog::addNewSceneButtonClicked() void SceneDialog::insertSceneButtonClicked() { - if ( ! checkForModifiedFiles(true)) { + if ( ! displayNewSceneWarning()) { + return; + } + + if ( ! checkForModifiedFiles(GuiManager::TEST_FOR_MODIFIED_FILES_EXCLUDING_PALETTES_MODE_FOR_SCENE_ADD, + this)) { return; } @@ -686,11 +1158,581 @@ SceneDialog::insertSceneButtonClicked() s_informUserAboutScenesOnExitFlag = false; } - loadScenesIntoDialog(newScene); + loadScenesIntoDialog(newScene); + } + } + + updateSceneFileModifiedStatusLabel(); +} + +/** + * Get the image from the scene info. + * + * @param sceneInfo + * The scene info. + * @return + * Point to image that caller must delete. Will be NULL + * if image is not valid. + */ +QImage* +SceneDialog::getQImageFromSceneInfo(const SceneInfo* sceneInfo) const +{ + QImage* imageOut = NULL; + + QByteArray imageByteArray; + AString imageBytesFormat; + sceneInfo->getImageBytes(imageByteArray, + imageBytesFormat); + + if (imageByteArray.isEmpty()) { + return new QImage(); + } + + ImageFile imageFile; + imageFile.setImageFromByteArray(imageByteArray, + imageBytesFormat); + const QImage* image = imageFile.getAsQImage(); + if (image != NULL) { + if (image->isNull()) { + CaretLogSevere("Preview image is invalid (isNull)"); + } + else { + imageOut = new QImage(*image); + } + } + + return imageOut; +} + +/** + * Replace all scenes. + */ +void +SceneDialog::replaceAllScenesPushButtonClicked() +{ + SceneFile* sceneFile = getSelectedSceneFile(); + if (sceneFile == NULL) { + WuQMessageBox::errorOk(m_replaceAllScenesPushButton, "No scene file is loaded."); + return; + } + + const int32_t numScenes = sceneFile->getNumberOfScenes(); + if (numScenes == 0) { + WuQMessageBox::errorOk(m_replaceAllScenesPushButton, "Selected scene file contains no scenes."); + return; + } + + if ( ! WuQMessageBox::warningOkCancel(m_replaceAllScenesPushButton, + "Replace All Scenes", + m_replaceAllScenesDescription)) { + return; + } + + AString username; + AString password; + + /* + * Check to see if any scenes need a login/password. + * Same login/password is used for all scenes. + */ + for (int32_t i = 0; i < numScenes; i++) { + const Scene* scene = sceneFile->getSceneAtIndex(i); + if (scene != NULL) { + if (scene->hasFilesWithRemotePaths()) { + const QString msg("This scene contains files that are on the network. " + "If accessing the files requires a username and " + "password, enter it here. Otherwise, remove any " + "text from the username and password fields."); + + + if (UsernamePasswordWidget::getUserNameAndPasswordInDialog(m_replaceAllScenesPushButton, + "Username and Password", + msg, + username, + password)) { + CaretDataFile::setFileReadingUsernameAndPassword(username, + password); + break; + } + } + } + } + + showWaitCursor(); + + ProgressReportingDialog progressDialog("Replace All Scenes", + "Starting...", + m_testScenesPushButton); + progressDialog.setEventReceivingEnabled(false); + progressDialog.setMinimumDuration(0); + progressDialog.setMinimum(0); + progressDialog.setMaximum(numScenes); + progressDialog.setValue(1); + progressDialog.setLabelText("Starting replacement..."); + progressDialog.repaint(); + progressDialog.setCursor(Qt::ArrowCursor); + progressDialog.show(); + QApplication::processEvents(); + + const int IMAGE_DISPLAY_WIDTH = 400; + std::vector sceneNames; + std::vector sceneErrors; + std::vector oldImages; + std::vector newImages; + + bool canceledFlag = false; + for (int32_t i = 0; i < numScenes; i++) { + if (progressDialog.wasCanceled()) { + canceledFlag = true; + break; + } + + Scene* origScene = sceneFile->getSceneAtIndex(i); + if (origScene != NULL) { + progressDialog.setValue(i+1); + progressDialog.setLabelText("Replacing " + + AString::number(i + 1) + + " of " + + AString::number(numScenes) + + ": " + + origScene->getName() + + "\n\nPressing the Cancel button will stop replacement after the next scene finishes loading."); + progressDialog.repaint(); + progressDialog.show(); + QApplication::processEvents(); + + if (origScene->hasFilesWithRemotePaths()) { + CaretDataFile::setFileReadingUsernameAndPassword(username, + password); + } + + const QImage* imageFromScene = getQImageFromSceneInfo(origScene->getSceneInfo()); + if (imageFromScene != NULL) { + if (imageFromScene->isNull()) { + oldImages.push_back(QImage()); + } + else { + oldImages.push_back(imageFromScene->scaledToWidth(IMAGE_DISPLAY_WIDTH)); + } + delete imageFromScene; + } + else { + oldImages.push_back(QImage()); + } + + /* + * Display the scene + */ + AString errorMessage; + displayScenePrivateWithErrorMessage(sceneFile, + origScene, + false, + errorMessage); + sceneNames.push_back(origScene->getName()); + + /* + * Assume scene loads correctly + */ + Scene* newScene = new Scene(SceneTypeEnum::SCENE_TYPE_FULL); + Scene::setSceneBeingCreated(newScene); + newScene->setName(origScene->getName()); + newScene->setDescription(origScene->getDescription()); + newScene->setBalsaSceneID(origScene->getBalsaSceneID()); + + const std::vector windowIndices = GuiManager::get()->getAllOpenBrainBrowserWindowIndices(); + + /* + * Get all browser tabs and only save transformations for tabs + * that are valid. + */ + std::vector tabIndices; + EventBrowserTabGetAll getAllTabs; + EventManager::get()->sendEvent(getAllTabs.getPointer()); + tabIndices = getAllTabs.getBrowserTabIndices(); + std::sort(tabIndices.begin(), + tabIndices.end()); + + SceneAttributes* sceneAttributes = newScene->getAttributes(); + sceneAttributes->setSceneFileName(getSelectedSceneFile()->getFileName()); + sceneAttributes->setSceneName(origScene->getName()); + sceneAttributes->setIndicesOfTabsAndWindowsForSavingToScene(tabIndices, + windowIndices); + sceneAttributes->setSpecFileNameSavedToScene(true); + sceneAttributes->setAllLoadedFilesSavedToScene(true); + sceneAttributes->setModifiedPaletteSettingsSavedToScene(true); + + newScene->addClass(GuiManager::get()->saveToScene(sceneAttributes, + "guiManager")); + + AString imageErrorMessage; + SceneCreateReplaceDialog::addImageToScene(newScene, + imageErrorMessage); + if ( ! imageErrorMessage.isEmpty()) { + errorMessage.appendWithNewLine(imageErrorMessage); + } + + getSelectedSceneFile()->replaceScene(newScene, + origScene); + EventManager::get()->sendEvent(EventGraphicsUpdateAllWindows().getPointer()); + + loadScenesIntoDialog(newScene); + const QImage* newImage = getQImageFromSceneInfo(newScene->getSceneInfo()); + if (newImage != NULL) { + if (newImage->isNull()) { + newImages.push_back(QImage()); + } + else { + newImages.push_back(newImage->scaledToWidth(IMAGE_DISPLAY_WIDTH)); + } + delete newImage; + } + else { + newImages.push_back(QImage()); + } + sceneErrors.push_back(errorMessage); + } + } + + progressDialog.close(); + + CaretAssert(sceneNames.size() == sceneErrors.size()); + CaretAssert(sceneNames.size() == oldImages.size()); + CaretAssert(sceneNames.size() == newImages.size()); + + showNormalCursor(); + + if (canceledFlag) { + WuQMessageBox::warningOk(m_replaceAllScenesPushButton, + "Replacment of scenes was canceled so scene file may contain both original and " + "replaced scenes. It may be best to reload the scene file or exit without saving " + "the scene file."); + } + + const int32_t numValidScenes = static_cast(sceneNames.size()); + if (numValidScenes <= 0) { + WuQMessageBox::errorOk(m_replaceAllScenesPushButton, + "No scenes were loaded."); + return; + } + + QGridLayout* gridLayout = new QGridLayout(); + gridLayout->setColumnStretch(0, 0); + gridLayout->setColumnStretch(1, 0); + gridLayout->setColumnStretch(2, 0); + gridLayout->setColumnStretch(3, 0); + + for (int32_t i = 0; i < numValidScenes; i++) { + int row = gridLayout->rowCount(); + if (i > 0) { + gridLayout->addWidget(WuQtUtilities::createHorizontalLineWidget(), + row, 0, 1, 4); + row++; + } + + gridLayout->addWidget(new QLabel("Scene Name: " + sceneNames[i]), + row, 0, Qt::AlignLeft); + + if ( ! sceneErrors[i].isEmpty()) { + QLabel* errorLabel = new QLabel("Scene Errors: " + sceneErrors[i]); + errorLabel->setWordWrap(true); + gridLayout->addWidget(errorLabel, + row, 1, Qt::AlignLeft); + } + + row++; + + const QSizePolicy::Policy imageSizePolicy = QSizePolicy::Preferred; + + QLabel* sceneImageLabel = new QLabel("Image Invalid"); + if ( ! oldImages[i].isNull()) { + sceneImageLabel->clear(); + sceneImageLabel->setPixmap(QPixmap::fromImage(oldImages[i])); + sceneImageLabel->setSizePolicy(imageSizePolicy, + imageSizePolicy); + } + gridLayout->addWidget(sceneImageLabel, + row, 0); + + QLabel* newImageLabel = new QLabel("Image Invalid"); + if ( ! newImages[i].isNull()) { + newImageLabel->clear(); + newImageLabel->setPixmap(QPixmap::fromImage(newImages[i])); + newImageLabel->setSizePolicy(imageSizePolicy, + imageSizePolicy); + } + gridLayout->addWidget(newImageLabel, + row, 1); + } + + const int numRows = gridLayout->rowCount(); + for (int32_t i = 0; i < numRows; i++) { + gridLayout->setRowStretch(i, 0); + } + + QWidget* dialogWidget = new QWidget(); + QVBoxLayout* dialogLayout = new QVBoxLayout(dialogWidget); + dialogLayout->addLayout(gridLayout); + + WuQDialogNonModal* dialog = new WuQDialogNonModal("Replace All Scenes Comparison", + m_replaceAllScenesPushButton); + dialog->setDeleteWhenClosed(true); + dialog->setApplyButtonText(""); + dialog->setCentralWidget(dialogWidget, + WuQDialogNonModal::SCROLL_AREA_ALWAYS); + dialog->show(); + + WuQtUtilities::limitWindowSizePercentageOfMaximum(dialog, + 100.0, + 100.0); + + const int dialogWidth = (IMAGE_DISPLAY_WIDTH * 2) + 50; + const int dialogHeight = (IMAGE_DISPLAY_WIDTH * 2.5); + WuQtUtilities::resizeWindow(dialog, + dialogWidth, + dialogHeight); + + dialog->show(); +} + +/** + * Test all scenes by loading them and then + * displaying images that are stored with the + * scene and new image of the displayed scene. + */ +void +SceneDialog::testScenesPushButtonClicked() +{ + SceneFile* sceneFile = getSelectedSceneFile(); + if (sceneFile == NULL) { + WuQMessageBox::errorOk(m_testScenesPushButton, "No scene file is loaded."); + return; + } + + + const int32_t numScenes = sceneFile->getNumberOfScenes(); + if (numScenes == 0) { + WuQMessageBox::errorOk(m_testScenesPushButton, "Selected scene file contains no scenes."); + return; + } + + if ( ! WuQMessageBox::warningOkCancel(m_testScenesPushButton, + "Test All Scenes", + m_testAllScenesDescription)) { + return; + } + + AString username; + AString password; + + /* + * Check to see if any scenes need a login/password. + * Same login/password is used for all scenes. + */ + for (int32_t i = 0; i < numScenes; i++) { + const Scene* scene = sceneFile->getSceneAtIndex(i); + if (scene != NULL) { + if (scene->hasFilesWithRemotePaths()) { + const QString msg("This scene contains files that are on the network. " + "If accessing the files requires a username and " + "password, enter it here. Otherwise, remove any " + "text from the username and password fields."); + + + if (UsernamePasswordWidget::getUserNameAndPasswordInDialog(this, + "Username and Password", + msg, + username, + password)) { + CaretDataFile::setFileReadingUsernameAndPassword(username, + password); + break; + } + } + } + } + + ProgressReportingDialog progressDialog("Test All Scenes", + "Starting...", + m_testScenesPushButton); + progressDialog.setEventReceivingEnabled(false); + progressDialog.setMinimumDuration(0); + progressDialog.setMinimum(0); + progressDialog.setMaximum(numScenes); + progressDialog.setValue(1); + progressDialog.setLabelText("Starting testing..."); + progressDialog.repaint(); + progressDialog.setCursor(Qt::ArrowCursor); + progressDialog.show(); + QApplication::processEvents(); + + std::vector sceneNames; + std::vector sceneErrors; + std::vector sceneImages; + std::vector newImages; + + const int IMAGE_DISPLAY_WIDTH = 500; + for (int32_t i = 0; i < numScenes; i++) { + if (progressDialog.wasCanceled()) { + break; + } + + Scene* origScene = sceneFile->getSceneAtIndex(i); + if (origScene != NULL) { + progressDialog.setValue(i+1); + progressDialog.setLabelText("Testing " + + AString::number(i + 1) + + " of " + + AString::number(numScenes) + + ": " + + origScene->getName() + + "\n\nPressing the Cancel button will stop testing after the next scene finishes loading."); + progressDialog.repaint(); + progressDialog.show(); + QApplication::processEvents(); + if (origScene->hasFilesWithRemotePaths()) { + CaretDataFile::setFileReadingUsernameAndPassword(username, + password); + } + + AString errorMessage; + displayScenePrivateWithErrorMessage(sceneFile, + origScene, + false, + errorMessage); + EventManager::get()->sendEvent(EventGraphicsUpdateAllWindows().getPointer()); + + /* + * Always generate an image, even if the scene fails and failure may + * be a very minor error. + */ + QImage image; + AString imageErrorMessage; + const bool validImage = SceneCreateReplaceDialog::createSceneImage(image, + imageErrorMessage); + if (validImage) { + newImages.push_back(image.scaledToWidth(IMAGE_DISPLAY_WIDTH)); + } + else { + newImages.push_back(QImage()); + errorMessage.appendWithNewLine("Generate image error: " + imageErrorMessage); + } + + sceneNames.push_back(origScene->getName()); + const QImage* imageFromScene = getQImageFromSceneInfo(origScene->getSceneInfo()); + if (imageFromScene != NULL) { + if (imageFromScene->isNull()) { + sceneImages.push_back(QImage()); + } + else { + sceneImages.push_back(imageFromScene->scaledToWidth(IMAGE_DISPLAY_WIDTH)); + } + delete imageFromScene; + } + else { + sceneImages.push_back(QImage()); + } + + sceneErrors.push_back(errorMessage); + } + } + + progressDialog.close(); + + CaretAssert(sceneNames.size() == sceneErrors.size()); + CaretAssert(sceneNames.size() == sceneImages.size()); + CaretAssert(sceneNames.size() == sceneErrors.size()); + CaretAssert(sceneNames.size() == newImages.size()); + + showNormalCursor(); + + const int32_t numValidScenes = static_cast(sceneNames.size()); + if (numValidScenes <= 0) { + WuQMessageBox::errorOk(m_testScenesPushButton, + "No scenes were loaded."); + return; + } + + QGridLayout* gridLayout = new QGridLayout(); + gridLayout->setColumnStretch(0, 0); + gridLayout->setColumnStretch(1, 0); + gridLayout->setColumnStretch(2, 100); + + for (int32_t i = 0; i < numValidScenes; i++) { + int row = gridLayout->rowCount(); + if (i > 0) { + gridLayout->addWidget(WuQtUtilities::createHorizontalLineWidget(), + row, 0, 1, 4); + row = gridLayout->rowCount(); + } + + gridLayout->addWidget(new QLabel("Scene Name: " + sceneNames[i]), + row, 0, 1, 2, Qt::AlignLeft); + + row = gridLayout->rowCount(); + + if ( ! sceneErrors[i].isEmpty()) { + QLabel* errorLabel = new QLabel("Scene Errors: " + sceneErrors[i]); + errorLabel->setWordWrap(true); + gridLayout->addWidget(errorLabel, + row, 0, 1, 2, Qt::AlignLeft); + + row = gridLayout->rowCount(); } - } + + const QSizePolicy::Policy imageSizePolicy = QSizePolicy::Preferred; + + QLabel* sceneImageLabel = new QLabel("Image Invalid"); + if ( ! sceneImages[i].isNull()) { + sceneImageLabel->clear(); + sceneImageLabel->setPixmap(QPixmap::fromImage(sceneImages[i])); + sceneImageLabel->setSizePolicy(imageSizePolicy, + imageSizePolicy); + } + gridLayout->addWidget(sceneImageLabel, + row, 0); + + QLabel* newImageLabel = new QLabel("Image Invalid"); + if ( ! newImages[i].isNull()) { + newImageLabel->clear(); + newImageLabel->setPixmap(QPixmap::fromImage(newImages[i])); + newImageLabel->setSizePolicy(imageSizePolicy, + imageSizePolicy); + } + gridLayout->addWidget(newImageLabel, + row, 1); + } + + const int numRows = gridLayout->rowCount(); + for (int32_t i = 0; i < numRows; i++) { + gridLayout->setRowStretch(i, 0); + } + + QWidget* dialogWidget = new QWidget(); + QVBoxLayout* dialogLayout = new QVBoxLayout(dialogWidget); + dialogLayout->addLayout(gridLayout); + dialogLayout->addStretch(); + + WuQDialogNonModal* dialog = new WuQDialogNonModal("Test All Scene Comparisons", + m_testScenesPushButton); + dialog->setDeleteWhenClosed(true); + dialog->setApplyButtonText(""); + dialog->setCentralWidget(dialogWidget, + WuQDialogNonModal::SCROLL_AREA_ALWAYS); + dialog->show(); + WuQtUtilities::limitWindowSizePercentageOfMaximum(dialog, + 100.0, + 100.0); + + const int dialogWidth = (IMAGE_DISPLAY_WIDTH * 2) + 50; + const int dialogHeight = (IMAGE_DISPLAY_WIDTH * 2.5); + WuQtUtilities::resizeWindow(dialog, + dialogWidth, + dialogHeight); + + dialog->show(); } + /** * Move the selected scene up. */ @@ -703,6 +1745,8 @@ SceneDialog::moveSceneUpButtonClicked() if (scene != NULL) { sceneFile->moveScene(scene, -1); loadScenesIntoDialog(scene); + + updateSceneFileModifiedStatusLabel(); } } } @@ -719,6 +1763,8 @@ SceneDialog::moveSceneDownButtonClicked() if (scene != NULL) { sceneFile->moveScene(scene, 1); loadScenesIntoDialog(scene); + + updateSceneFileModifiedStatusLabel(); } } } @@ -729,7 +1775,12 @@ SceneDialog::moveSceneDownButtonClicked() void SceneDialog::replaceSceneButtonClicked() { - if ( ! checkForModifiedFiles(true)) { + if ( ! displayNewSceneWarning()) { + return; + } + + if ( ! checkForModifiedFiles(GuiManager::TEST_FOR_MODIFIED_FILES_EXCLUDING_PALETTES_MODE_FOR_SCENE_ADD, + this)) { return; } @@ -745,91 +1796,10 @@ SceneDialog::replaceSceneButtonClicked() } loadScenesIntoDialog(newScene); - } - } -} - -/** - * Add an image to the scene. - * - * @param scene - * Scene to which image is added. - */ -void -SceneDialog::addImageToScene(Scene* scene) -{ - AString errorMessage; - - CaretAssert(scene); - - uint8_t backgroundColor[3] = { 0, 0, 0 }; - bool backgroundColorValid = false; - - /* - * Capture an image of each window - */ - std::vector imageFiles; - std::vector windows = GuiManager::get()->getAllOpenBrainBrowserWindows(); - for (std::vector::iterator iter = windows.begin(); - iter != windows.end(); - iter++) { - BrainBrowserWindow* bbw = *iter; - const int32_t browserWindowIndex = bbw->getBrowserWindowIndex(); - - EventImageCapture imageCaptureEvent(browserWindowIndex); - EventManager::get()->sendEvent(imageCaptureEvent.getPointer()); - - if (imageCaptureEvent.getEventProcessCount() > 0) { - if (imageCaptureEvent.isError()) { - errorMessage.appendWithNewLine(imageCaptureEvent.getErrorMessage()); - } - else { - imageFiles.push_back(new ImageFile(imageCaptureEvent.getImage())); - if ( ! backgroundColorValid) { - imageCaptureEvent.getBackgroundColor(backgroundColor); - backgroundColorValid = true; - } - } - } - } - - /* - * Assemble images of each window into a single image - * and add it to the scene. Use one image per row - * since the images are limited in horizontal space - * when shown in the listing of scenes. - */ - if ( ! imageFiles.empty()) { - try { - const int32_t numImagesPerRow = 1; - ImageFile compositeImageFile; - compositeImageFile.combinePreservingAspectAndFillIfNeeded(imageFiles, - numImagesPerRow, - backgroundColor); - - compositeImageFile.resizeToMaximumWidth(512); - QByteArray byteArray; - compositeImageFile.getImageInByteArray(byteArray, - SceneDialog::PREFERRED_IMAGE_FORMAT); - - scene->getSceneInfo()->setImageBytes(byteArray, - SceneDialog::PREFERRED_IMAGE_FORMAT); - } - catch (const DataFileException& dfe) { - WuQMessageBox::errorOk(m_addNewScenePushButton, - dfe.whatString()); + updateSceneFileModifiedStatusLabel(); } } - - /* - * Free memory from the image files. - */ - for (std::vector::iterator iter = imageFiles.begin(); - iter != imageFiles.end(); - iter++) { - delete *iter; - } } /** @@ -842,14 +1812,28 @@ SceneDialog::addImageToScene(Scene* scene) * true if the scene should be created, otherwise false. */ bool -SceneDialog::checkForModifiedFiles(const bool creatingSceneFlag) +SceneDialog::checkForModifiedFiles(const GuiManager::TestModifiedMode testMode, + QWidget* parent) { + bool creatingSceneFlag = false; + switch (testMode) { + case GuiManager::TEST_FOR_MODIFIED_FILES_EXCLUDING_PALETTES_MODE_FOR_SCENE_ADD: + creatingSceneFlag = true; + break; + case GuiManager::TEST_FOR_MODIFIED_FILES_MODE_FOR_EXIT: + break; + case GuiManager::TEST_FOR_MODIFIED_FILES_MODE_FOR_SCENE_SHOW: + break; + case GuiManager::TEST_FOR_MODIFIED_FILES_PALETTE_ONLY_MODE_FOR_SCENE_ADD: + break; + } + if (creatingSceneFlag) { EventModelGetAll allModelsEvent; EventManager::get()->sendEvent(allModelsEvent.getPointer()); if (allModelsEvent.getModels().empty()) { const QString msg("No surfaces or volumes are loaded. Continue creating scene?"); - if ( ! WuQMessageBox::warningYesNo(this, + if ( ! WuQMessageBox::warningYesNo(parent, msg)) { return false; } @@ -858,10 +1842,6 @@ SceneDialog::checkForModifiedFiles(const bool creatingSceneFlag) AString dialogMessage; AString modifiedFilesMessage; - GuiManager::TestModifiedMode testMode = GuiManager::TEST_FOR_MODIFIED_FILES_MODE_FOR_SCENE_SHOW; - if (creatingSceneFlag) { - testMode = GuiManager::TEST_FOR_MODIFIED_FILES_MODE_FOR_SCENE_ADD; - } const bool haveModifiedFilesFlag = GuiManager::get()->testForModifiedFiles(testMode, dialogMessage, modifiedFilesMessage); @@ -873,7 +1853,7 @@ SceneDialog::checkForModifiedFiles(const bool creatingSceneFlag) "Warning", dialogMessage, QMessageBox::NoButton, - this); + parent); warningDialog.setInformativeText(modifiedFilesMessage); QPushButton* yesButton = warningDialog.addButton("Yes", QMessageBox::AcceptRole); @@ -899,18 +1879,89 @@ SceneDialog::checkForModifiedFiles(const bool creatingSceneFlag) } /** - * Create the main page. - * @return the main page. + * Show the data file structure in a dialog. + */ +void +SceneDialog::showFileStructure() +{ + SceneFile* sceneFile = getSelectedSceneFile(); + CaretAssert(sceneFile); + + std::vector filenames = sceneFile->getAllDataFileNamesFromAllScenes(); + + if (filenames.empty()) { + WuQMessageBox::errorOk(m_showFileStructurePushButton, + "Scene file is empty."); + return; + } + + const AString sceneFileName = sceneFile->getFileName(); + AString text(""); + + AString baseDirectoryName; + std::vector missingFileNames; + AString errorMessage; + const bool validBasePathFlag = sceneFile->findBaseDirectoryForDataFiles(baseDirectoryName, + missingFileNames, + errorMessage); + text.appendWithNewLine("Automatic Base Path: " + + (validBasePathFlag ? baseDirectoryName : ("INVALID: " + errorMessage)) + + "

    "); + text.appendWithNewLine("Scene File: " + + sceneFileName + + "

    "); + text.append("Data File paths relative to Scene File:"); + text.append("

      "); + + for (auto name : filenames) { + FileInformation fileInfo(name); + AString missingText; + if ( ! fileInfo.exists()) { + missingText = "MISSING: "; + } + + if (fileInfo.isAbsolute()) { + FileInformation specFileInfo(sceneFileName); + if (specFileInfo.isAbsolute()) { + const AString newPath = SystemUtilities::relativePath(fileInfo.getPathName(), + specFileInfo.getPathName()); + if (newPath.isEmpty()) { + name = fileInfo.getFileName(); + } + else { + name = (newPath + + "/" + + fileInfo.getFileName()); + } + } + } + + text.append("
    • " + + missingText + + name); + } + text.append("
    "); + text.append(""); + + WuQTextEditorDialog::runNonModal("Scene File Paths", + text, + WuQTextEditorDialog::TextMode::HTML, + WuQTextEditorDialog::WrapMode::NO, + this); +} + +/** + * @return The Scenes widget. */ QWidget* -SceneDialog::createMainPage() +SceneDialog::createScenesWidget() { QWidget* sceneFileWidget = createSceneFileWidget(); /* * Scene controls */ - QLabel* sceneLabel = new QLabel("Scenes"); + QLabel* sceneLabel = new QLabel(" Scenes"); /* * Add new scene button @@ -928,6 +1979,22 @@ SceneDialog::createMainPage() QObject::connect(m_deleteScenePushButton, SIGNAL(clicked()), this, SLOT(deleteSceneButtonClicked())); + /* + * Replace all scenes + */ + m_replaceAllScenesPushButton = new QPushButton("Replace All..."); + m_replaceAllScenesPushButton->setToolTip(m_replaceAllScenesDescription); + QObject::connect(m_replaceAllScenesPushButton, SIGNAL(clicked()), + this, SLOT(replaceAllScenesPushButtonClicked())); + + /* + * Test all scenes + */ + m_testScenesPushButton = new QPushButton("Test All..."); + m_testScenesPushButton->setToolTip(m_testAllScenesDescription); + QObject::connect(m_testScenesPushButton, SIGNAL(clicked()), + this, SLOT(testScenesPushButtonClicked())); + /* * Move scene up button */ @@ -972,26 +2039,80 @@ SceneDialog::createMainPage() * Show scene image button */ m_showSceneImagePreviewPushButton = new QPushButton("Preview..."); - m_showSceneImagePreviewPushButton->setToolTip("Show larger image and full description\n" + m_showSceneImagePreviewPushButton->setToolTip("Show larger preview image and full description\n" "for the selected scene"); QObject::connect(m_showSceneImagePreviewPushButton, SIGNAL(clicked()), this, SLOT(showImagePreviewButtonClicked())); + /* + * Show scene options button + */ + m_showSceneOptionsPushButton = new QPushButton("Options..."); + m_showSceneOptionsPushButton->setToolTip("Display a dialog for show scene options"); + QObject::connect(m_showSceneOptionsPushButton, &QPushButton::clicked, + this, &SceneDialog::showSceneOptionsButtonClicked); + + /* + * Group for show buttons + */ + QGroupBox* showGroupBox = new QGroupBox("Show Scene"); + showGroupBox->setFlat(true); + showGroupBox->setSizePolicy(showGroupBox->sizePolicy().horizontalPolicy(), QSizePolicy::Fixed); + QVBoxLayout* showGroupLayout = new QVBoxLayout(showGroupBox); + WuQtUtilities::setLayoutSpacingAndMargins(showGroupLayout, 2, 2); + showGroupLayout->addWidget(m_showScenePushButton); + showGroupLayout->addWidget(m_showSceneImagePreviewPushButton); + showGroupLayout->addWidget(m_showSceneOptionsPushButton); + + /* + * Group for create buttons + */ + QGroupBox* createGroupBox = new QGroupBox("Create Scene"); + createGroupBox->setFlat(true); + createGroupBox->setSizePolicy(createGroupBox->sizePolicy().horizontalPolicy(), QSizePolicy::Fixed); + QVBoxLayout* createGroupLayout = new QVBoxLayout(createGroupBox); + WuQtUtilities::setLayoutSpacingAndMargins(createGroupLayout, 2, 2); + createGroupLayout->addWidget(m_addNewScenePushButton); + createGroupLayout->addWidget(m_insertNewScenePushButton); + createGroupLayout->addWidget(m_replaceScenePushButton); + + /* + * Group for test buttons + */ + QGroupBox* testGroupBox = new QGroupBox("Testing"); + testGroupBox->setFlat(true); + testGroupBox->setSizePolicy(testGroupBox->sizePolicy().horizontalPolicy(), QSizePolicy::Fixed); + QVBoxLayout* testGroupLayout = new QVBoxLayout(testGroupBox); + WuQtUtilities::setLayoutSpacingAndMargins(testGroupLayout, 2, 2); + if (m_replaceAllScenesPushButton != NULL) { + testGroupLayout->addWidget(m_replaceAllScenesPushButton); + } + testGroupLayout->addWidget(m_testScenesPushButton); + + /* + * Group for organize buttons + */ + QGroupBox* organizeGroupBox = new QGroupBox("Selected Scene"); + organizeGroupBox->setFlat(true); + organizeGroupBox->setSizePolicy(organizeGroupBox->sizePolicy().horizontalPolicy(), QSizePolicy::Fixed); + QVBoxLayout* organizeGroupLayout = new QVBoxLayout(organizeGroupBox); + WuQtUtilities::setLayoutSpacingAndMargins(organizeGroupLayout, 2, 2); + organizeGroupLayout->addWidget(m_moveSceneUpPushButton); + organizeGroupLayout->addWidget(m_moveSceneDownPushButton); + organizeGroupLayout->addSpacing(5); + organizeGroupLayout->addWidget(m_deleteScenePushButton); + /* * Layout for scene buttons */ QVBoxLayout* sceneButtonLayout = new QVBoxLayout(); - sceneButtonLayout->addWidget(m_showScenePushButton); - sceneButtonLayout->addWidget(m_showSceneImagePreviewPushButton); + sceneButtonLayout->addWidget(showGroupBox); sceneButtonLayout->addSpacing(20); - sceneButtonLayout->addWidget(m_addNewScenePushButton); - sceneButtonLayout->addWidget(m_insertNewScenePushButton); - sceneButtonLayout->addWidget(m_replaceScenePushButton); - sceneButtonLayout->addStretch(); - sceneButtonLayout->addWidget(m_moveSceneUpPushButton); - sceneButtonLayout->addWidget(m_moveSceneDownPushButton); + sceneButtonLayout->addWidget(createGroupBox); sceneButtonLayout->addSpacing(20); - sceneButtonLayout->addWidget(m_deleteScenePushButton); + sceneButtonLayout->addWidget(organizeGroupBox); + sceneButtonLayout->addStretch(); + sceneButtonLayout->addWidget(testGroupBox); /* * Widget and layout containing the scene class info. @@ -1009,11 +2130,6 @@ SceneDialog::createMainPage() m_sceneSelectionScrollArea->setWidget(m_sceneSelectionWidget); m_sceneSelectionScrollArea->setWidgetResizable(true); - QVBoxLayout* showOptionsLabelsLayout = new QVBoxLayout(); - WuQtUtilities::setLayoutMargins(showOptionsLabelsLayout, 0); - showOptionsLabelsLayout->addWidget(new QLabel(" Show Scene"), 0, Qt::AlignRight); - showOptionsLabelsLayout->addWidget(new QLabel("Options"), 0, Qt::AlignHCenter); - /* * Layout widgets */ @@ -1035,9 +2151,6 @@ SceneDialog::createMainPage() row++; gridLayout->addWidget(WuQtUtilities::createHorizontalLineWidget(), row, 0, 1, 3); row++; - gridLayout->addLayout(showOptionsLabelsLayout, row, 0, Qt::AlignTop); - gridLayout->addWidget(createShowOptionsWidget(), row, 1, 1, 1); //, Qt::AlignTop); - row++; return widget; } @@ -1060,16 +2173,55 @@ SceneDialog::createSceneFileWidget() "Selects an existing scene file\n" "to which new scenes are added."); QObject::connect(m_sceneFileSelectionComboBox, SIGNAL(activated(int)), - this, SLOT(sceneFileSelected())); + this, SLOT(sceneFileSelected(int))); + + /* + * Scene file modified status + */ + QLabel* modifiedLabel = new QLabel("Modified"); + m_sceneFileModifiedStatusLabel = new QLabel(""); + + /* + * File structure buttons + */ + m_showFileStructurePushButton = new QPushButton("List Files..."); + WuQtUtilities::setWordWrappedToolTip(m_showFileStructurePushButton, + "In a dialog, show data files from all scenes with paths relative to the scene file"); + QObject::connect(m_showFileStructurePushButton, &QPushButton::clicked, + this, &SceneDialog::showFileStructure); /* * New File button */ - QPushButton* newSceneFilePushButton = new QPushButton("New..."); - newSceneFilePushButton->setToolTip("Create a new scene file"); - QObject::connect(newSceneFilePushButton, SIGNAL(clicked()), + m_newSceneFilePushButton = new QPushButton("New"); + m_newSceneFilePushButton->setToolTip("Create a new scene file"); + QObject::connect(m_newSceneFilePushButton, SIGNAL(clicked()), this, SLOT(newSceneFileButtonClicked())); + /* + * Open File button + */ + m_openSceneFilePushButton = new QPushButton("Open..."); + m_openSceneFilePushButton->setToolTip("Open an existing scene file"); + QObject::connect(m_openSceneFilePushButton, SIGNAL(clicked()), + this, SLOT(openSceneFileButtonClicked())); + + /* + * Save File button + */ + m_saveSceneFilePushButton = new QPushButton("Save"); + m_saveSceneFilePushButton->setToolTip("Save the scene file using its current name"); + QObject::connect(m_saveSceneFilePushButton, SIGNAL(clicked()), + this, SLOT(saveSceneFileButtonClicked())); + + /* + * Save As File button + */ + m_saveAsSceneFilePushButton = new QPushButton("Save As..."); + m_saveAsSceneFilePushButton->setToolTip("Display a file selection dialog for saving scene file with a new name"); + QObject::connect(m_saveAsSceneFilePushButton, SIGNAL(clicked()), + this, SLOT(saveAsSceneFileButtonClicked())); + /* * Upload button */ @@ -1086,56 +2238,14 @@ SceneDialog::createSceneFileWidget() QObject::connect(m_zipSceneFilePushButton, SIGNAL(clicked()), this, SLOT(zipSceneFileButtonClicked())); - /* - * Scene BALSA Study ID - */ - QLabel* studyIDLabel = new QLabel("BALSA Study ID"); - m_fileBalsaStudyIDLineEdit = new QLineEdit(); - m_fileBalsaStudyIDLineEdit->setToolTip("Press Edit button to change Study ID for use with BALSA Database"); - m_fileBalsaStudyIDLineEdit->setReadOnly(true); - - /* - * Edit BALSA Study ID button - */ - m_editBalsaStudyIDPushButton = new QPushButton("Edit..."); - m_editBalsaStudyIDPushButton->setToolTip("Edit the Scene File's BALSA Study ID"); - QObject::connect(m_editBalsaStudyIDPushButton, SIGNAL(clicked()), - this, SLOT(editFileBalsaStudyIDButtonClicked())); - - /* - * Base Directory - */ - QLabel* baseDirectoryLabel = new QLabel("Base Directory"); - m_fileBaseDirectoryLineEdit = new QLineEdit(); - m_fileBaseDirectoryLineEdit->setToolTip("Press Browse or Edit button to change base directory"); - m_fileBaseDirectoryLineEdit->setReadOnly(true); - m_editBaseDirectoryPushButton = new QPushButton("Edit..."); - m_editBaseDirectoryPushButton->setToolTip("Edit the base directory in a text edit"); - QObject::connect(m_editBaseDirectoryPushButton, SIGNAL(clicked()), - this, SLOT(editBaseDirectoryPushButtonClicked())); - m_browseBaseDirectoryPushButton = new QPushButton("Browse..."); - m_browseBaseDirectoryPushButton->setToolTip("Use a file system dialog to choose the base directory"); - QObject::connect(m_browseBaseDirectoryPushButton, SIGNAL(clicked()), - this, SLOT(browseBaseDirectoryPushButtonClicked())); - /* * Group for scene file buttons that are only valid when a scene file is available */ m_sceneFileButtonsGroup = new WuQWidgetObjectGroup(this); + m_sceneFileButtonsGroup->add(m_saveSceneFilePushButton); + m_sceneFileButtonsGroup->add(m_saveAsSceneFilePushButton); m_sceneFileButtonsGroup->add(m_uploadSceneFilePushButton); m_sceneFileButtonsGroup->add(m_zipSceneFilePushButton); - m_sceneFileButtonsGroup->add(m_editBalsaStudyIDPushButton); - m_sceneFileButtonsGroup->add(m_editBaseDirectoryPushButton); - m_sceneFileButtonsGroup->add(m_browseBaseDirectoryPushButton); - - /* - * Scene file buttons layout - */ - QHBoxLayout* fileButtonsLayout = new QHBoxLayout(); - fileButtonsLayout->setContentsMargins(0, 0, 0, 0); - fileButtonsLayout->addWidget(newSceneFilePushButton); - fileButtonsLayout->addWidget(m_uploadSceneFilePushButton); - fileButtonsLayout->addWidget(m_zipSceneFilePushButton); /* * Layout widgets @@ -1151,66 +2261,26 @@ SceneDialog::createSceneFileWidget() gridLayout->setColumnStretch(1, 100); gridLayout->setColumnStretch(2, 0); gridLayout->setColumnStretch(3, 0); + gridLayout->setColumnStretch(4, 0); + gridLayout->setColumnStretch(5, 0); int row = 0; - gridLayout->addWidget(sceneFileLabel, row, 0, Qt::AlignRight); - gridLayout->addWidget(m_sceneFileSelectionComboBox, row, 1); - gridLayout->addLayout(fileButtonsLayout, row, 2, 1, 2); -// gridLayout->addWidget(newSceneFilePushButton, row, 2); -// gridLayout->addWidget(m_uploadSceneFilePushButton, row, 3); -// gridLayout->addWidget(m_zipSceneFilePushButton, row, 4); + gridLayout->addWidget(sceneFileLabel, row, 0, Qt::AlignRight); + gridLayout->addWidget(m_sceneFileSelectionComboBox, row, 1, 1, 2); + gridLayout->addWidget(m_newSceneFilePushButton, row, 3); + gridLayout->addWidget(m_saveSceneFilePushButton, row, 4); + gridLayout->addWidget(m_zipSceneFilePushButton, row, 5); row++; - gridLayout->addWidget(baseDirectoryLabel, row, 0, Qt::AlignRight); - gridLayout->addWidget(m_fileBaseDirectoryLineEdit, row, 1); - gridLayout->addWidget(m_editBaseDirectoryPushButton, row, 2); - gridLayout->addWidget(m_browseBaseDirectoryPushButton, row, 3); + gridLayout->addWidget(modifiedLabel, row, 0, Qt::AlignRight); + gridLayout->addWidget(m_sceneFileModifiedStatusLabel, row, 1, Qt::AlignLeft); + gridLayout->addWidget(m_showFileStructurePushButton, row, 2); + gridLayout->addWidget(m_openSceneFilePushButton, row, 3); + gridLayout->addWidget(m_saveAsSceneFilePushButton, row, 4); + gridLayout->addWidget(m_uploadSceneFilePushButton, row, 5); row++; - gridLayout->addWidget(studyIDLabel, row, 0, Qt::AlignRight); - gridLayout->addWidget(m_fileBalsaStudyIDLineEdit, row, 1); - gridLayout->addWidget(m_editBalsaStudyIDPushButton, row, 2); return widget; } - -/** - * @return The show scene options widget. - */ -QWidget* -SceneDialog::createShowOptionsWidget() -{ - /* - * Use colors contained in the scene for background and foreground - */ - const QString colorTip1("Scenes created after 01 May 2016 contain the active background and foreground\n" - "colors. When this box is checked and the colors are present in the scene loaded,\n" - "they will temporarily override the colors in the Preferences Dialog. The background\n" - "and foreground colors will revert to those on the Preferences Dialog when any\n" - "of these events occur:\n" - " * This checkbox is unchecked\n" - " * A scene without background and foreground colors is loaded\n" - " * A Spec File and its data files are loaded\n" - " * File Menu->Close All Files is selected\n" - " * Any of the colors on the Preferences Dialog are changed\n" - " * The user quits wb_view"); - - m_useSceneColorsCheckBox = new QCheckBox("Use background and foreground colors from scene"); - m_useSceneColorsCheckBox->setToolTip(colorTip1); - m_useSceneColorsCheckBox->setChecked(true); - QObject::connect(m_useSceneColorsCheckBox, SIGNAL(clicked(bool)), - this, SLOT(useSceneColorsCheckBoxClicked(bool))); - - QFrame* frame = new QFrame(); - frame->setFrameShape(QFrame::StyledPanel); - frame->setFrameShadow(QFrame::Plain); - frame->setLineWidth(1); - frame->setMidLineWidth(1); - QVBoxLayout* layout = new QVBoxLayout(frame); - WuQtUtilities::setLayoutMargins(layout, 5); - layout->addWidget(m_useSceneColorsCheckBox, 0, Qt::AlignLeft); - - return frame; -} - /** * Gets called to verify the content of the scene creation dialog. * @param sceneCreateDialog @@ -1312,12 +2382,10 @@ SceneDialog::deleteSceneButtonClicked() void SceneDialog::showSceneButtonClicked() { -// At this time, do not test for modified files -// since a scene previously loaded may contain -// modified palette status. -// if ( ! checkForModifiedFiles(false)) { -// return; -// } + if ( ! checkForModifiedFiles(GuiManager::TEST_FOR_MODIFIED_FILES_MODE_FOR_SCENE_SHOW, + this)) { + return; + } AString sceneFileName; SceneFile* sceneFile = getSelectedSceneFile(); @@ -1347,17 +2415,42 @@ SceneDialog::showSceneButtonClicked() return; } } + ProgressReportingDialog progressDialog(("Restoring Scene " + scene->getName()), "", this); progressDialog.setValue(0); - displayScenePrivate(sceneFile, + displayScenePrivateWithErrorMessageDialog(sceneFile, scene, false); } } +/** + * Display the scene show options dialog + */ +void +SceneDialog::showSceneOptionsButtonClicked() +{ + Scene* scene = getSelectedScene(); + if (scene != NULL) { + SceneShowOptionsDialog::Options options(s_useSceneForegroundBackgroundColorsFlag); + SceneShowOptionsDialog optionsDialog(options, + m_showSceneOptionsPushButton); + if (optionsDialog.exec() == QDialog::Accepted) { + const SceneShowOptionsDialog::Options optionsResult = optionsDialog.getOptions(); + s_useSceneForegroundBackgroundColorsFlag = optionsResult.isUseSceneColorsSelected(); + + if ( ! optionsResult.isUseSceneColorsSelected()) { + CaretPreferences* prefs = SessionManager::get()->getCaretPreferences(); + prefs->setBackgroundAndForegroundColorsMode(BackgroundAndForegroundColorsModeEnum::USER_PREFERENCES); + EventManager::get()->sendEvent(EventGraphicsUpdateAllWindows().getPointer()); + } + } + } +} + /** * Called when show image preview button is clicked. */ @@ -1417,6 +2510,7 @@ SceneDialog::showImagePreviewButtonClicked() AString descriptionText; const int32_t negativeIsUnlimitedNumberOfLines = -1; SceneClassInfoWidget::getFormattedTextForSceneNameAndDescription(scene->getSceneInfo(), + -1, nameText, sceneIdText, descriptionText, @@ -1470,7 +2564,7 @@ bool SceneDialog::displayScene(SceneFile* sceneFile, Scene* scene) { - const bool isSuccessful = displayScenePrivate(sceneFile, + const bool isSuccessful = displayScenePrivateWithErrorMessageDialog(sceneFile, scene, true); loadSceneFileComboBox(sceneFile); @@ -1479,32 +2573,97 @@ SceneDialog::displayScene(SceneFile* sceneFile, return isSuccessful; } +/** + * Display the given scene from the given scene file. + * If the scene fails to load, an error message is displayed + * in a dialog. + * + * @param sceneFile + * Scene file. + * @param scene + * Scene that is displayed. + * param showWaitCursor + * Show a wait cursor while loading scene + * @return + * true if scene was displayed without error, else false. + */ +bool +SceneDialog::displayScenePrivateWithErrorMessageDialog(SceneFile* sceneFile, + Scene* scene, + const bool showWaitCursor) +{ + AString errorMessage; + + ElapsedTimer et; + et.start(); + + const bool successFlag = displayScenePrivateWithErrorMessage(sceneFile, + scene, + showWaitCursor, + errorMessage); + AString msg = (AString::number(et.getElapsedTimeSeconds()) + + " seconds to read Scene " + + scene->getName()); + CaretLogInfo(msg); + + if ( ! successFlag) { + WuQMessageBox::errorOk(this, + errorMessage); + } + + EventManager::get()->sendEvent(EventShowDataFileReadWarningsDialog().getPointer()); + + return successFlag; +} + /** * Display the given scene from the given scene file. * @param sceneFile * Scene file. * @param scene * Scene that is displayed. + * param showWaitCursor + * Show a wait cursor while loading scene + * @param errorMessageOut + * Contains error information if scene fails to load. * @return * true if scene was displayed without error, else false. */ bool -SceneDialog::displayScenePrivate(SceneFile* sceneFile, - Scene* scene, - const bool showWaitCursor) +SceneDialog::displayScenePrivateWithErrorMessage(SceneFile* sceneFile, + Scene* scene, + const bool showWaitCursor, + AString& errorMessageOut) { CaretAssert(sceneFile); CaretAssert(scene); + errorMessageOut.clear(); + const AString sceneFileName = sceneFile->getFileName(); const SceneClass* guiManagerClass = scene->getClassWithName("guiManager"); if (guiManagerClass->getName() != "guiManager") { - WuQMessageBox::errorOk(this,"Top level scene class should be guiManager but it is: " - + guiManagerClass->getName()); + errorMessageOut = ("Top level scene class should be guiManager but it is: " + + guiManagerClass->getName()); return false; } + /* + * When compiled in debug mode, log any SceneObject and + * subclasses that fail to restore + */ + bool logObjectFailedToRestoreFlag = true; +#ifdef NDEBUG + logObjectFailedToRestoreFlag = false; +#endif // NDEBUG + std::vector allSceneObjects; + if (logObjectFailedToRestoreFlag) { + allSceneObjects = scene->getDescendants(); + } + + SceneClass::setDebugLoggingEnabled(logObjectFailedToRestoreFlag); + /* * Show the wait cursor */ @@ -1521,10 +2680,23 @@ SceneDialog::displayScenePrivate(SceneFile* sceneFile, sceneAttributes->setSceneFileName(sceneFileName); sceneAttributes->setSceneName(scene->getName()); sceneAttributes->setWindowRestoreBehaviorInSceneDisplay(SceneAttributes::RESTORE_WINDOW_POSITION_RELATIVE_TO_FIRST_AND_USE_SIZES); - sceneAttributes->setUseSceneForegroundAndBackgroundColors(m_useSceneColorsCheckBox->isChecked()); + sceneAttributes->setUseSceneForegroundAndBackgroundColors(s_useSceneForegroundBackgroundColorsFlag); + + if ( ! allSceneObjects.empty()) { + SceneObject::setRestoredStatus(allSceneObjects, + false); + } GuiManager::get()->restoreFromScene(sceneAttributes, guiManagerClass); + guiManagerClass->setRestored(true); + + if ( ! allSceneObjects.empty()) { + SceneObject::logObjectsFailedRestore(scene->getName(), + allSceneObjects); + } + + SceneClass::setDebugLoggingEnabled(false); cursor.restoreCursor(); @@ -1545,8 +2717,7 @@ SceneDialog::displayScenePrivate(SceneFile* sceneFile, } } else { - WuQMessageBox::errorOk(this, - sceneErrorMessage); + errorMessageOut = sceneErrorMessage; return false; } @@ -1572,6 +2743,19 @@ SceneDialog::receiveEvent(Event* event) updateDialog(); uiEvent->setEventProcessed(); } + else if (event->getEventType() == EventTypeEnum::EVENT_DATA_FILE_ADD) { + EventDataFileAdd* addEvent = + dynamic_cast(event); + CaretAssert(addEvent); + + /* + * Determine if a scene file was read + */ + CaretDataFile* dataFile = addEvent->getCaretDataFile(); + if (dataFile != NULL) { + lastSceneFileRead = dynamic_cast(dataFile); + } + } else if (event->getEventType() == EventTypeEnum::EVENT_DATA_FILE_READ) { EventDataFileRead* readEvent = dynamic_cast(event); @@ -1614,6 +2798,8 @@ SceneDialog::receiveEvent(Event* event) highlightSceneAtIndex(0); m_sceneSelectionScrollArea->horizontalScrollBar()->setSliderPosition(0); } + + updateSceneFileModifiedStatusLabel(); } /** @@ -1627,6 +2813,96 @@ SceneDialog::isInformUserAboutScenesOnExit() return s_informUserAboutScenesOnExitFlag; } +/** + * Under some circumstances, the user may be warned + * when creating a new scene or scene file. + * + * @return + * True if scene or scene file should be created, else false. + */ +bool +SceneDialog::displayNewSceneWarning() +{ + if (s_warnUserWhenCreatingSceneFlag) { + s_warnUserWhenCreatingSceneFlag = false; + + ApplicationInformation appInfo; + switch (appInfo.getWorkbenchSpecialVersion()) { + case WorkbenchSpecialVersionEnum::WORKBENCH_SPECIAL_VERSION_NO: + break; + case WorkbenchSpecialVersionEnum::WORKBENCH_SPECIAL_VERSION_FIRST_CHARTING: + { + const QString text("This is a special version of Workbench that correctly " + "displays scene files containing the first implementation " + "of matrix and line charting. Charting is undergoing a " + "redesign and any scenes created with this special version " + "of Workbench will not display correctly in newer, official " + "releases of Workbench."); + + if (WuQMessageBox::warningOkCancel(this, + "Do you want to continue?", + text)) { + return true; + } + else { + return false; + } + } + break; + } + } + + return true; +} + +/** + * Update the modified status label. + */ +void +SceneDialog::updateSceneFileModifiedStatusLabel() +{ + /* + * Modified icons in scene file selection combo box + */ + updateSceneFileComboBoxModifiedIcons(); + + /* + * Modified status of selected scene file + */ + AString statusText; + + /* + * Save button is enabled only if: + * (1) The scene file is modified + * (2) The filename is not the untitled file name + */ + bool haveScenesFlag = false; + bool saveButtonEnabledFlag = false; + SceneFile* sceneFile = getSelectedSceneFile(); + if (sceneFile != NULL) { + if (sceneFile->isModified()) { + if (sceneFile->exists()) { + saveButtonEnabledFlag = true; + } + statusText = "YES"; + } + else { + statusText = "NO"; + } + + if (sceneFile->getNumberOfScenes() > 0) { + haveScenesFlag = true; + } + } + + m_sceneFileModifiedStatusLabel->setText(statusText); + m_saveSceneFilePushButton->setEnabled(saveButtonEnabledFlag); + + m_showFileStructurePushButton->setEnabled(haveScenesFlag); + m_zipSceneFilePushButton->setEnabled(haveScenesFlag); + m_uploadSceneFilePushButton->setEnabled(haveScenesFlag); +} + /* ======================================================================== */ @@ -1734,6 +3010,7 @@ SceneClassInfoWidget::updateContent(Scene* scene, AString descriptionText; const int32_t numLinesToDisplay = 9; SceneClassInfoWidget::getFormattedTextForSceneNameAndDescription(scene->getSceneInfo(), + sceneIndex, nameText, sceneIdText, descriptionText, @@ -1755,16 +3032,18 @@ SceneClassInfoWidget::updateContent(Scene* scene, bool previewImageValid = false; if (imageByteArray.length() > 0) { - try { - ImageFile imageFile; - imageFile.setImageFromByteArray(imageByteArray, - imageBytesFormat); + ImageFile imageFile; + imageFile.setImageFromByteArray(imageByteArray, + imageBytesFormat); + + previewImage = *imageFile.getAsQImage(); + if ( ! previewImage.isNull()) { imageFile.resizeToWidth(previewImageWidth); - previewImage = *imageFile.getAsQImage(); - previewImageValid = true; - } - catch (const DataFileException& dfe) { - CaretLogSevere(dfe.whatString()); + QImage newPreviewImage = *imageFile.getAsQImage(); + if ( ! newPreviewImage.isNull()) { + previewImage = newPreviewImage; + previewImageValid = true; + } } } @@ -1777,13 +3056,6 @@ SceneClassInfoWidget::updateContent(Scene* scene, else { m_previewImageLabel->setText("No preview
    image"); } - -// const int32_t maxHeight = 150; -// const int32_t maxSizeHintHeight = std::max(m_leftSideWidget->sizeHint().height(), -// m_rightSideWidget->sizeHint().height()); -// const int32_t fixedHeight = std::min(maxSizeHintHeight, -// maxHeight); -// setFixedHeight(fixedHeight); } } @@ -1835,6 +3107,7 @@ SceneClassInfoWidget::limitToNumberOfLines(AString& textLines, */ void SceneClassInfoWidget::getFormattedTextForSceneNameAndDescription(const SceneInfo* sceneInfo, + const int32_t sceneIndex, AString& nameTextOut, AString& sceneIdTextOut, AString& descriptionTextOut, @@ -1847,7 +3120,19 @@ SceneClassInfoWidget::getFormattedTextForSceneNameAndDescription(const SceneInfo name = "NAME IS MISSING !!!"; } - nameTextOut = ("NAME: " + AString indexText; + const bool showSceneIndexFlag = false; + if (showSceneIndexFlag) { + if (sceneIndex >= 0) { + indexText = ("(" + AString::number(sceneIndex + 1) + ") "); + } + } + + nameTextOut = ("" + + indexText + + "NAME (" + + AString::number(sceneIndex + 1) + + "): " + name + ""); diff --git a/src/GuiQt/SceneDialog.h b/src/GuiQt/SceneDialog.h index b867782061b48d30ad00929d4cbf4efa6b1585c9..307fe98f484683aeab68abdd4fc95d10c14481e5 100644 --- a/src/GuiQt/SceneDialog.h +++ b/src/GuiQt/SceneDialog.h @@ -22,11 +22,14 @@ /*LICENSE_END*/ #include +#include #include "EventListenerInterface.h" +#include "GuiManager.h" #include "WuQDialogNonModal.h" class QCheckBox; +class QCloseEvent; class QComboBox; class QLabel; class QLineEdit; @@ -58,18 +61,31 @@ namespace caret { bool displayScene(SceneFile* sceneFile, Scene* scene); + void createDefaultSceneFile(); + static bool isInformUserAboutScenesOnExit(); + static bool checkForModifiedFiles(const GuiManager::TestModifiedMode testMode, + QWidget* parent); + private: SceneDialog(const SceneDialog&); SceneDialog& operator=(const SceneDialog&); private slots: - void sceneFileSelected(); + virtual void closeButtonClicked(); + + void sceneFileSelected(int); void newSceneFileButtonClicked(); + void openSceneFileButtonClicked(); + + void saveSceneFileButtonClicked(); + + void saveAsSceneFileButtonClicked(); + void uploadSceneFileButtonClicked(); void zipSceneFileButtonClicked(); @@ -90,6 +106,8 @@ namespace caret { void showImagePreviewButtonClicked(); + void showSceneOptionsButtonClicked(); + void validateContentOfCreateSceneDialog(WuQDataEntryDialog*); void sceneHighlighted(const int32_t sceneIndex); @@ -98,16 +116,19 @@ namespace caret { void useSceneColorsCheckBoxClicked(bool checked); - void editFileBalsaStudyIDButtonClicked(); + void testScenesPushButtonClicked(); - void editBaseDirectoryPushButtonClicked(); + void replaceAllScenesPushButtonClicked(); - void browseBaseDirectoryPushButtonClicked(); + void showFileStructure(); public: // ADD_NEW_METHODS_HERE + protected: + virtual void closeEvent(QCloseEvent* event); + private: SceneFile* getSelectedSceneFile(); @@ -115,34 +136,78 @@ namespace caret { void loadSceneFileComboBox(SceneFile* selectedSceneFileIn); + void updateSceneFileComboBoxModifiedIcons(); + void loadScenesIntoDialog(Scene* selectedSceneIn); + int32_t getNumberOfValidSceneInfoWidgets() const; + void addImageToScene(Scene* scene); void highlightSceneAtIndex(const int32_t sceneIndex); void highlightScene(const Scene* scene); - QWidget* createMainPage(); - - QWidget* createShowOptionsWidget(); + QWidget* createScenesWidget(); QWidget* createSceneFileWidget(); - bool displayScenePrivate(SceneFile* sceneFile, - Scene* scene, - const bool showWaitCursor); + bool displayScenePrivateWithErrorMessageDialog(SceneFile* sceneFile, + Scene* scene, + const bool showWaitCursor); + + bool displayScenePrivateWithErrorMessage(SceneFile* sceneFile, + Scene* scene, + const bool showWaitCursor, + AString& errorMessageOut); - bool checkForModifiedFiles(const bool creatingSceneFlag); + bool displayNewSceneWarning(); void enableSceneMoveUpAndDownButtons(); - void loadSceneFileMetaDataWidgets(); + void updateSceneFileModifiedStatusLabel(); + + QImage* getQImageFromSceneInfo(const SceneInfo* sceneInfo) const; + + enum class ModifiedWarningType { + CLOSE_DIALOG_BUTTON, + FILE_COMBO_BOX_CHANGE_FILE, + NEW_FILE_BUTTON, + OPEN_FILE_BUTTON, + UPLOAD_FILE_BUTTON, + ZIP_FILE_BUTTON + }; + + enum class MissingFilesMode { + UPLOAD, + ZIP + }; + + bool warnIfSceneFileIsModified(const ModifiedWarningType warningType); + + bool saveSelectedSceneFile(); + + bool saveAsSelectedSceneFile(); + + bool warnIfMissingFilesInSceneFile(SceneFile* sceneFile, + const MissingFilesMode missingFilesMode); // ADD_NEW_MEMBERS_HERE + QLabel* m_sceneFileModifiedStatusLabel; + QComboBox* m_sceneFileSelectionComboBox; + QPushButton* m_showFileStructurePushButton; + + QPushButton* m_newSceneFilePushButton; + + QPushButton* m_openSceneFilePushButton; + + QPushButton* m_saveSceneFilePushButton; + + QPushButton* m_saveAsSceneFilePushButton; + QPushButton* m_uploadSceneFilePushButton; QPushButton* m_zipSceneFilePushButton; @@ -153,6 +218,10 @@ namespace caret { QPushButton* m_deleteScenePushButton; + QPushButton* m_replaceAllScenesPushButton; + + QPushButton* m_testScenesPushButton; + QPushButton* m_moveSceneUpPushButton; QPushButton* m_moveSceneDownPushButton; @@ -163,7 +232,7 @@ namespace caret { QPushButton* m_showSceneImagePreviewPushButton; - QCheckBox* m_useSceneColorsCheckBox; + QPushButton* m_showSceneOptionsPushButton; QScrollArea* m_sceneSelectionScrollArea; @@ -175,21 +244,23 @@ namespace caret { int32_t m_selectedSceneClassInfoIndex; - QPushButton* m_editBalsaStudyIDPushButton; - - QLineEdit* m_fileBalsaStudyIDLineEdit; + WuQWidgetObjectGroup* m_sceneFileButtonsGroup; - QPushButton* m_browseBaseDirectoryPushButton; + AString m_replaceAllScenesDescription; - QPushButton* m_editBaseDirectoryPushButton; + AString m_testAllScenesDescription; - QLineEdit* m_fileBaseDirectoryLineEdit; + QIcon m_cautionIcon; - WuQWidgetObjectGroup* m_sceneFileButtonsGroup; + bool m_cautionIconValid = false; static const AString PREFERRED_IMAGE_FORMAT; static bool s_informUserAboutScenesOnExitFlag; + + static bool s_warnUserWhenCreatingSceneFlag; + + static bool s_useSceneForegroundBackgroundColorsFlag; }; class SceneClassInfoWidget : public QGroupBox { @@ -212,6 +283,7 @@ namespace caret { bool isValid() const; static void getFormattedTextForSceneNameAndDescription(const SceneInfo* sceneInfo, + const int32_t sceneIndex, AString& nameTextOut, AString& sceneIdTextOut, AString& descriptionTextOut, @@ -263,6 +335,9 @@ namespace caret { #ifdef __SCENE_DIALOG_DECLARE__ const AString SceneDialog::PREFERRED_IMAGE_FORMAT = "jpg"; bool SceneDialog::s_informUserAboutScenesOnExitFlag = true; + bool SceneDialog::s_warnUserWhenCreatingSceneFlag = true; + bool SceneDialog::s_useSceneForegroundBackgroundColorsFlag = true; + #endif // __SCENE_DIALOG_DECLARE__ } // namespace diff --git a/src/GuiQt/ScenePreviewDialog.cxx b/src/GuiQt/ScenePreviewDialog.cxx index 8cdef6f5d10e61bc4cb47d4affb3d9ce4ea05cdd..7d04eb6f853af504eaa141335b26e2ff32ed8d6e 100644 --- a/src/GuiQt/ScenePreviewDialog.cxx +++ b/src/GuiQt/ScenePreviewDialog.cxx @@ -33,6 +33,7 @@ #include "Scene.h" #include "SceneDialog.h" #include "SceneInfo.h" +#include "WuQtUtilities.h" using namespace caret; @@ -97,6 +98,7 @@ ScenePreviewDialog::ScenePreviewDialog(const Scene* scene, AString descriptionText; const int32_t negativeIsUnlimitedNumberOfLines = -1; SceneClassInfoWidget::getFormattedTextForSceneNameAndDescription(scene->getSceneInfo(), + -1, nameText, sceneIdText, descriptionText, @@ -120,7 +122,9 @@ ScenePreviewDialog::ScenePreviewDialog(const Scene* scene, layout->addWidget(descriptionLabel); } - setCentralWidget(widget, SCROLL_AREA_AS_NEEDED); + setCentralWidget(widget, SCROLL_AREA_ALWAYS); + + WuQtUtilities::limitWindowSizePercentageOfMaximum(this, 90.0, 80.0); } /** diff --git a/src/GuiQt/SceneShowOptionsDialog.cxx b/src/GuiQt/SceneShowOptionsDialog.cxx new file mode 100644 index 0000000000000000000000000000000000000000..a148f5fd121986970444de0bc36c0e3366b2d30d --- /dev/null +++ b/src/GuiQt/SceneShowOptionsDialog.cxx @@ -0,0 +1,134 @@ + +/*LICENSE_START*/ +/* + * Copyright (C) 2017 Washington University School of Medicine + * + * 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 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ +/*LICENSE_END*/ + +#define __SCENE_SHOW_OPTIONS_DIALOG_DECLARE__ +#include "SceneShowOptionsDialog.h" +#undef __SCENE_SHOW_OPTIONS_DIALOG_DECLARE__ + +#include +#include +#include +#include +#include + +#include "CaretAssert.h" +#include "WuQDataEntryDialog.h" + +using namespace caret; + + + +/** + * \class caret::SceneShowOptionsDialog + * \brief Dialog for setting show scene options + * \ingroup GuiQt + */ + +/** + * Constructor. + * + * @param options + * Options displayed in dialog. + * @param parent + * Optional parent for dialog. + */ +SceneShowOptionsDialog::SceneShowOptionsDialog(const Options& options, + QWidget* parent) +: WuQDialogModal("Show Scene Options", + parent) +{ + /* + * Use colors contained in the scene for background and foreground + */ + const QString colorTip("Override foreground/background colors from preferences with those in scene"); + + m_useSceneColorsCheckBox = new QCheckBox("Use background and foreground colors from scene"); + m_useSceneColorsCheckBox->setToolTip(colorTip); + m_useSceneColorsCheckBox->setChecked(options.isUseSceneColorsSelected()); + + m_useSceneColorsToolButton = new QToolButton(); + m_useSceneColorsToolButton->setText("Info..."); + QObject::connect(m_useSceneColorsToolButton, &QToolButton::clicked, + this, &SceneShowOptionsDialog::useSceneColorsInfoButtonClicked); + + QHBoxLayout* useSceneColorsLayout = new QHBoxLayout(); + useSceneColorsLayout->addWidget(m_useSceneColorsCheckBox); + useSceneColorsLayout->addWidget(m_useSceneColorsToolButton); + useSceneColorsLayout->addStretch(); + + QWidget* dialogWidget = new QWidget; + QVBoxLayout* dialogLayout = new QVBoxLayout(dialogWidget); + dialogLayout->addLayout(useSceneColorsLayout); + + + dialogWidget->setSizePolicy(QSizePolicy::Fixed, + QSizePolicy::Fixed); + setCentralWidget(dialogWidget, WuQDialog::SCROLL_AREA_NEVER); +} + +/** + * Destructor. + */ +SceneShowOptionsDialog::~SceneShowOptionsDialog() +{ +} + +/** + * @return Status of options in dialog. + */ +SceneShowOptionsDialog::Options +SceneShowOptionsDialog::getOptions() const +{ + return Options(m_useSceneColorsCheckBox->isChecked()); +} + +void +SceneShowOptionsDialog::useSceneColorsInfoButtonClicked() +{ + const QString infoText("Scenes created after 01 May 2016 contain the background and foreground from when the\n" + "scene was created. If this box is checked, background and foreground colors from the\n" + "scene will override the foreground and background colors in the Preferences Dialog.\n" + "The background and foreground colors will revert to those on the Preferences Dialog\n" + "when any of these events occur:\n" + " * This checkbox is unchecked\n" + " * A scene without background and foreground colors is loaded\n" + " * A Spec File and its data files are loaded\n" + " * File Menu->Close All Files is selected\n" + " * Any of the colors on the Preferences Dialog are changed\n" + " * The user quits wb_view"); + + WuQDataEntryDialog ded("Scene Colors Info", + m_useSceneColorsToolButton); + ded.hideCancelButton(); + QLabel* label = new QLabel(infoText); + ded.addWidget(label); + ded.exec(); +} + +/** + * Gets called if the user presses the OK button. + */ +void +SceneShowOptionsDialog::okButtonClicked() +{ + WuQDialogModal::okButtonClicked(); +} + diff --git a/src/GuiQt/SceneShowOptionsDialog.h b/src/GuiQt/SceneShowOptionsDialog.h new file mode 100644 index 0000000000000000000000000000000000000000..05f66defedee504d1da10bd2083385f9d89b0828 --- /dev/null +++ b/src/GuiQt/SceneShowOptionsDialog.h @@ -0,0 +1,94 @@ +#ifndef __SCENE_SHOW_OPTIONS_DIALOG_H__ +#define __SCENE_SHOW_OPTIONS_DIALOG_H__ + +/*LICENSE_START*/ +/* + * Copyright (C) 2017 Washington University School of Medicine + * + * 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 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ +/*LICENSE_END*/ + + + +#include + +#include "WuQDialogModal.h" + +class QCheckBox; +class QToolButton; + +namespace caret { + + class SceneShowOptionsDialog : public WuQDialogModal { + + Q_OBJECT + + public: + /** + * Options for display in dialog + */ + class Options { + public: + /** + * Constructor + * + * @param useSceneColorsSelected + * Status of use scene colors + */ + Options(const bool useSceneColorsSelected) + : m_useSceneColorsSelected(useSceneColorsSelected) { } + + /** @return Status of use scene colors */ + bool isUseSceneColorsSelected() const { return m_useSceneColorsSelected; } + + private: + bool m_useSceneColorsSelected = false; + }; + + + SceneShowOptionsDialog(const Options& options, + QWidget* parent = 0); + + virtual ~SceneShowOptionsDialog(); + + Options getOptions() const; + + // ADD_NEW_METHODS_HERE + + protected: + virtual void okButtonClicked(); + + void useSceneColorsInfoButtonClicked(); + + private: + SceneShowOptionsDialog(const SceneShowOptionsDialog&); + + SceneShowOptionsDialog& operator=(const SceneShowOptionsDialog&); + + QCheckBox* m_useSceneColorsCheckBox; + + QToolButton* m_useSceneColorsToolButton; + + // ADD_NEW_MEMBERS_HERE + + }; + +#ifdef __SCENE_SHOW_OPTIONS_DIALOG_DECLARE__ + // +#endif // __SCENE_SHOW_OPTIONS_DIALOG_DECLARE__ + +} // namespace +#endif //__SCENE_SHOW_OPTIONS_DIALOG_H__ diff --git a/src/GuiQt/SceneWindowGeometry.cxx b/src/GuiQt/SceneWindowGeometry.cxx index a0ba72bfbedcb5d249add46e12cd9f97ce8d8d46..a5e591844adb3864c1e40904a3276717c275cc78 100644 --- a/src/GuiQt/SceneWindowGeometry.cxx +++ b/src/GuiQt/SceneWindowGeometry.cxx @@ -170,6 +170,10 @@ SceneWindowGeometry::restoreFromScene(const SceneAttributes* sceneAttributes, -1); const int32_t sceneHeight = sceneClass->getIntegerValue("geometryHeight", -1); + const int32_t offsetX = sceneClass->getIntegerValue("geometryOffsetX", 100); + const int32_t offsetY = sceneClass->getIntegerValue("geometryOffsetY", 100); + const bool windowVisible = sceneClass->getBooleanValue("visibility", + true); const bool isDialog = (qobject_cast(m_window) != NULL); QDockWidget* dockWidget = qobject_cast(m_window); @@ -216,8 +220,6 @@ SceneWindowGeometry::restoreFromScene(const SceneAttributes* sceneAttributes, /* * Get offset from parent */ - const int32_t offsetX = sceneClass->getIntegerValue("geometryOffsetX", 100); - const int32_t offsetY = sceneClass->getIntegerValue("geometryOffsetY", 100); const int32_t windowX = m_parentWindow->x() + offsetX; const int32_t windowY = m_parentWindow->y() + offsetY; @@ -238,8 +240,6 @@ SceneWindowGeometry::restoreFromScene(const SceneAttributes* sceneAttributes, /* * Visibility */ - const bool windowVisible = sceneClass->getBooleanValue("visibility", - true); m_window->setVisible(windowVisible); /* diff --git a/src/GuiQt/SpecFileManagementDialog.cxx b/src/GuiQt/SpecFileManagementDialog.cxx index fd06d7e0722cdaa9566983f4743e12039be35158..83dbc3b68c6a814ff8b46d42b0bdd34205abcbb3 100644 --- a/src/GuiQt/SpecFileManagementDialog.cxx +++ b/src/GuiQt/SpecFileManagementDialog.cxx @@ -62,6 +62,7 @@ #include "EventGetDisplayedDataFiles.h" #include "EventGraphicsUpdateAllWindows.h" #include "EventManager.h" +#include "EventShowDataFileReadWarningsDialog.h" #include "EventSpecFileReadDataFiles.h" #include "EventSurfaceColoringInvalidate.h" #include "EventUserInterfaceUpdate.h" @@ -1477,26 +1478,37 @@ SpecFileManagementDialog::loadSpecFileContentIntoDialog() CaretAssert(statusItem); statusItem->setText(""); if (caretDataFile != NULL) { - if (caretDataFile->isModified()) { - if (isFileSavable) { - statusItem->setText("YES"); - + if (isFileSavable) { + /* + * Is this a Caret Mappable Data file and is the modification + * only in the palette color mapping? + */ + CaretMappableDataFile* mapFile = dynamic_cast(caretDataFile); + if (mapFile != NULL) { /* - * Is this a Caret Mappable Data file and is the modification - * only in the palette color mapping? + * Is modification just the palette color mapping? */ - CaretMappableDataFile* mapFile = dynamic_cast(caretDataFile); - if (mapFile != NULL) { - /* - * Is modification just the palette color mapping? - */ - if (mapFile->isModifiedPaletteColorMapping()) { - if ( ! mapFile->isModifiedExcludingPaletteColorMapping()) { + if (mapFile->isModifiedExcludingPaletteColorMapping()) { + statusItem->setText("YES"); + } + else { + switch (mapFile->getPaletteColorMappingModifiedStatus()) { + case PaletteModifiedStatusEnum::MODIFIED: statusItem->setText("PALETTE"); - } + break; + case PaletteModifiedStatusEnum::MODIFIED_BY_SHOW_SCENE: + statusItem->setText("SCENE PALETTE"); + break; + case PaletteModifiedStatusEnum::UNMODIFIED: + break; } } } + else { + if (caretDataFile->isModified()) { + statusItem->setText("YES"); + } + } } } @@ -1845,7 +1857,6 @@ SpecFileManagementDialog::okButtonClickedOpenSpecFile() ProgressReportingDialog::runEvent(&readSpecFileEvent, this, m_specFile->getFileNameNoPath()); - errorMessages.appendWithNewLine(readSpecFileEvent.getErrorMessage()); updateGraphicWindowsAndUserInterface(); @@ -2170,6 +2181,8 @@ SpecFileManagementDialog::fileReloadOrOpenFileActionSelected(int rowIndex) WuQMessageBox::errorOk(toolButtonWidget, errorMessage); } + + EventManager::get()->sendEvent(EventShowDataFileReadWarningsDialog().getPointer()); } /** @@ -2763,27 +2776,9 @@ SpecFileManagementDialog::createManageFilesLoadedNotLoadedToolBar(QLabel* &label AString SpecFileManagementDialog::getEditedDataFileTypeName(const DataFileTypeEnum::Enum dataFileType) { - const AString typeName = DataFileTypeEnum::toGuiName(dataFileType); - - const AString connectivityPrefix("Connectivity - "); - const int connectivityPrefixLength = connectivityPrefix.length(); - - const AString temporarySuffix(" TEMPORARY"); - const int temporarySuffixLength = temporarySuffix.length(); - - AString text = typeName; - if (text.startsWith(connectivityPrefix)) { - text = text.mid(connectivityPrefixLength); - } - - if (text.endsWith(temporarySuffix)) { - text = text.left(text.length() - temporarySuffixLength); - } - - return text; + return DataFileTypeEnum::toShortGuiName(dataFileType); } - /** * @return Create and return a toolbar for viewing files by structure. */ diff --git a/src/GuiQt/SplashScreen.cxx b/src/GuiQt/SplashScreen.cxx index ffa807eea7303c6f167a638fbd9e115161d10ebd..9de4fcfd9cb7b11aca16b6f48122701ca5e6b7d8 100644 --- a/src/GuiQt/SplashScreen.cxx +++ b/src/GuiQt/SplashScreen.cxx @@ -82,14 +82,15 @@ SplashScreen::SplashScreen(QWidget* parent) " font: 20px bold " "}"); - QLabel* workbenchLabel = new QLabel("" - "Connectome
    " - "Workbench" - ""); + ApplicationInformation appInfo; + QLabel* workbenchLabel = new QLabel(appInfo.getNameForGuiLabel()); +// QLabel* workbenchLabel = new QLabel("" +// "Connectome
    " +// "Workbench" +// ""); workbenchLabel->setStyleSheet(labelStyle); workbenchLabel->setAlignment(Qt::AlignCenter); - ApplicationInformation appInfo; QLabel* versionLabel = new QLabel("Version: " + appInfo.getVersion()); versionLabel->setAlignment(Qt::AlignCenter); diff --git a/src/GuiQt/SurfaceSelectionViewController.cxx b/src/GuiQt/SurfaceSelectionViewController.cxx index 355fe5ea56054d856be1c18e087300502b52ba2c..f8b52c432d008d25b03a965b2b2ce560629d1e02 100644 --- a/src/GuiQt/SurfaceSelectionViewController.cxx +++ b/src/GuiQt/SurfaceSelectionViewController.cxx @@ -26,6 +26,8 @@ #include #include "BrainStructure.h" +#include "FileInformation.h" +#include "FilePathNamePrefixCompactor.h" #include "Surface.h" #include "SurfaceSelectionModel.h" #include "WuQEventBlockingFilter.h" @@ -149,7 +151,7 @@ SurfaceSelectionViewController::initializeControl(const Mode mode, * Selection model used to update this control. If this parameter is NULL * all selections are removed from the control. */ -void +void SurfaceSelectionViewController::updateControl(SurfaceSelectionModel* selectionModel) { std::vector surfaces; @@ -162,13 +164,24 @@ SurfaceSelectionViewController::updateControl(SurfaceSelectionModel* selectionMo this->surfaceComboBox->blockSignals(true); + /* + * There may be surface files with the same name but different + * paths so include unique parts of path in names + */ + std::vector caretDataFiles(surfaces.begin(), + surfaces.end()); + std::vector displayNames; + FilePathNamePrefixCompactor::removeMatchingPathPrefixFromCaretDataFiles(caretDataFiles, + displayNames); + CaretAssert(surfaces.size() == displayNames.size()); + int32_t selectedIndex = 0; this->surfaceComboBox->clear(); const int32_t numSurfaces = static_cast(surfaces.size()); for (int32_t i = 0; i < numSurfaces; i++) { const int32_t indx = this->surfaceComboBox->count(); - this->surfaceComboBox->addItem(surfaces[i]->getFileNameNoPath()); + this->surfaceComboBox->addItem(displayNames[i]); this->surfaceComboBox->setItemData(indx, qVariantFromValue((void*)surfaces[i])); if (surfaces[i] == selectedSurface) { @@ -176,12 +189,26 @@ SurfaceSelectionViewController::updateControl(SurfaceSelectionModel* selectionMo } } + AString toolTipText; if (numSurfaces > 0) { this->surfaceComboBox->setCurrentIndex(selectedIndex); + + /* + * ToolTip contains file and path + */ + CaretAssertVectorIndex(surfaces, selectedIndex); + const Surface* s = surfaces[selectedIndex]; + if (s != NULL) { + FileInformation fileInfo(s->getFileName()); + toolTipText = ("Name: " + + fileInfo.getFileName() + + "\nPath: " + + fileInfo.getPathName()); + } } + this->surfaceComboBox->setToolTip(toolTipText); this->surfaceComboBox->blockSignals(false); - } /** diff --git a/src/GuiQt/ThresholdingSetMapsDialog.cxx b/src/GuiQt/ThresholdingSetMapsDialog.cxx new file mode 100644 index 0000000000000000000000000000000000000000..3ec4bd6c1fa26c04e1ab777dbbe60e53da5f91c0 --- /dev/null +++ b/src/GuiQt/ThresholdingSetMapsDialog.cxx @@ -0,0 +1,141 @@ + +/*LICENSE_START*/ +/* + * Copyright (C) 2018 Washington University School of Medicine + * + * 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 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ +/*LICENSE_END*/ + +#define __THRESHOLDING_SET_MAPS_DIALOG_DECLARE__ +#include "ThresholdingSetMapsDialog.h" +#undef __THRESHOLDING_SET_MAPS_DIALOG_DECLARE__ + +#include "CaretAssert.h" +#include "CaretMappableDataFile.h" +#include "WuQMessageBox.h" + +using namespace caret; + +#include +#include +#include + +/** + * \class caret::ThresholdingSetMapsDialog + * \brief Dialog for setting threshold + * \ingroup GuiQt + */ + +/** + * Constructor. + * + * @param dataFile + * Data file that is being thresholded + * @param thresholdFile + * File that is used for thresholding + * @param thresholdFileMapIndex + * Map index from thresholding file. + * @param parent + * Parent widget for this dialog. + */ +ThresholdingSetMapsDialog::ThresholdingSetMapsDialog(CaretMappableDataFile* dataFile, + CaretMappableDataFile* thresholdFile, + const int32_t thresholdFileMapIndex, + QWidget* parent) +: WuQDialogModal("Set Thresholding Maps", + parent), +m_dataFile(dataFile), +m_thresholdFile(thresholdFile), +m_thresholdFileMapIndex(thresholdFileMapIndex) +{ + CaretAssert(m_dataFile); + CaretAssert(m_thresholdFile); + CaretAssert(m_thresholdFileMapIndex >= 0); + CaretAssert(m_thresholdFileMapIndex < m_thresholdFile->getNumberOfMaps()); + + const AString numName("(" + + AString::number(m_thresholdFileMapIndex + 1) + + "): " + + m_thresholdFile->getMapName(m_thresholdFileMapIndex)); + m_setSameIndexRadioButton = new QRadioButton("Threshold Each Map With Map " + + numName); + const int32_t numMaps = std::min(m_dataFile->getNumberOfMaps(), + m_thresholdFile->getNumberOfMaps()); + m_setOneToEnRadioButton = new QRadioButton("Threshold Each Map With Sequential Indices " + "1 to " + + AString::number(numMaps)); + m_setOneToEnRadioButton->setEnabled(m_thresholdFile->getNumberOfMaps() > 1); + + QButtonGroup* buttonGroup = new QButtonGroup(this); + buttonGroup->addButton(m_setSameIndexRadioButton); + buttonGroup->addButton(m_setOneToEnRadioButton); + + QWidget* widget = new QWidget(); + QVBoxLayout* layout = new QVBoxLayout(widget); + layout->addWidget(m_setSameIndexRadioButton); + layout->addWidget(m_setOneToEnRadioButton); + widget->setSizePolicy(QSizePolicy::Fixed, QSizePolicy::Fixed); + + setCentralWidget(widget, WuQDialog::SCROLL_AREA_NEVER); +} + +/** + * Destructor. + */ +ThresholdingSetMapsDialog::~ThresholdingSetMapsDialog() +{ +} + + +/** + * Called when user presses the OK or Cancel button + * + * @param resultCode + * Result code from dialog. + */ +void +ThresholdingSetMapsDialog::done(int resultCode) +{ + if (resultCode == Accepted) { + if (m_setSameIndexRadioButton->isChecked()) { + const int32_t numMaps = m_dataFile->getNumberOfMaps(); + for (int32_t i = 0; i < numMaps; i++) { + m_dataFile->getMapThresholdFileSelectionModel(i)->setSelectedFile(m_thresholdFile); + m_dataFile->getMapThresholdFileSelectionModel(i)->setSelectedMapIndex(m_thresholdFileMapIndex); + } + } + else if (m_setOneToEnRadioButton->isChecked()) { + const int32_t numMaps = std::min(m_dataFile->getNumberOfMaps(), + m_thresholdFile->getNumberOfMaps()); + for (int32_t i = 0; i < numMaps; i++) { + m_dataFile->getMapThresholdFileSelectionModel(i)->setSelectedFile(m_thresholdFile); + m_dataFile->getMapThresholdFileSelectionModel(i)->setSelectedMapIndex(i); + } + } + else { + WuQMessageBox::errorOk(this, "Select a Threshold Option"); + return; + } + } + else if (resultCode == Rejected) { + /* nothing */ + } + else { + CaretAssertMessage(0, "Should never get here"); + } + + WuQDialogModal::done(resultCode); +} diff --git a/src/GuiQt/ThresholdingSetMapsDialog.h b/src/GuiQt/ThresholdingSetMapsDialog.h new file mode 100644 index 0000000000000000000000000000000000000000..e7299af4a5916842812c9052bca0e584612dfed7 --- /dev/null +++ b/src/GuiQt/ThresholdingSetMapsDialog.h @@ -0,0 +1,78 @@ +#ifndef __THRESHOLDING_SET_MAPS_DIALOG_H__ +#define __THRESHOLDING_SET_MAPS_DIALOG_H__ + +/*LICENSE_START*/ +/* + * Copyright (C) 2018 Washington University School of Medicine + * + * 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 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ +/*LICENSE_END*/ + + + +#include + +#include "WuQDialogModal.h" + +class QRadioButton; + +namespace caret { + + class CaretMappableDataFile; + + class ThresholdingSetMapsDialog : public WuQDialogModal { + + Q_OBJECT + + public: + ThresholdingSetMapsDialog(CaretMappableDataFile* dataFile, + CaretMappableDataFile* thresholdFile, + const int32_t thresholdFileMapIndex, + QWidget* parent = 0); + + virtual ~ThresholdingSetMapsDialog(); + + ThresholdingSetMapsDialog(const ThresholdingSetMapsDialog&) = delete; + + ThresholdingSetMapsDialog& operator=(const ThresholdingSetMapsDialog&) = delete; + + + // ADD_NEW_METHODS_HERE + + public slots: + virtual void done(int resultCode) override; + + private: + CaretMappableDataFile* m_dataFile; + + CaretMappableDataFile* m_thresholdFile; + + const int32_t m_thresholdFileMapIndex; + + QRadioButton* m_setSameIndexRadioButton; + + QRadioButton* m_setOneToEnRadioButton; + + // ADD_NEW_MEMBERS_HERE + + }; + +#ifdef __THRESHOLDING_SET_MAPS_DIALOG_DECLARE__ + // +#endif // __THRESHOLDING_SET_MAPS_DIALOG_DECLARE__ + +} // namespace +#endif //__THRESHOLDING_SET_MAPS_DIALOG_H__ diff --git a/src/GuiQt/TileTabsConfigurationDialog.cxx b/src/GuiQt/TileTabsConfigurationDialog.cxx index a9817355cce4e7739614f587052808c9c2be0cbc..e59a808db834d300861d359e74be0e48e7893212 100644 --- a/src/GuiQt/TileTabsConfigurationDialog.cxx +++ b/src/GuiQt/TileTabsConfigurationDialog.cxx @@ -219,7 +219,7 @@ TileTabsConfigurationDialog::createEditConfigurationWidget() Qt::AlignHCenter); row++; - const float stretchMinimumValue = 1.0; + const float stretchMinimumValue = 0.1; const float stretchMaximumValue = 10000000.0; const float stretchStep = 0.1; const float stretchDigitsRightOfDecimal = 2; diff --git a/src/GuiQt/UserInputModeAnnotations.cxx b/src/GuiQt/UserInputModeAnnotations.cxx index d524d33a55844e3d139b42bd95e16c442e401341..eeded042368d7be4f4c551983ad894696970c73d 100644 --- a/src/GuiQt/UserInputModeAnnotations.cxx +++ b/src/GuiQt/UserInputModeAnnotations.cxx @@ -94,7 +94,7 @@ m_annotationBeingDragged(NULL) m_annotationUnderMouseSizeHandleType = AnnotationSizingHandleTypeEnum::ANNOTATION_SIZING_HANDLE_NONE; m_modeNewAnnotationFileSpaceAndType.grabNew(new NewAnnotationFileSpaceAndType(NULL, - AnnotationCoordinateSpaceEnum::PIXELS, + AnnotationCoordinateSpaceEnum::VIEWPORT, AnnotationTypeEnum::LINE)); m_newAnnotationCreatingWithMouseDrag.grabNew(NULL); @@ -404,10 +404,11 @@ UserInputModeAnnotations::keyPressEvent(const KeyEvent& keyEvent) bool changeCoordFlag = false; bool moveOnePixelFlag = false; switch (selectedAnnotation->getCoordinateSpace()) { - case AnnotationCoordinateSpaceEnum::STEREOTAXIC: + case AnnotationCoordinateSpaceEnum::CHART: changeCoordFlag = true; break; - case AnnotationCoordinateSpaceEnum::PIXELS: + case AnnotationCoordinateSpaceEnum::STEREOTAXIC: + changeCoordFlag = true; break; case AnnotationCoordinateSpaceEnum::SURFACE: break; @@ -415,6 +416,8 @@ UserInputModeAnnotations::keyPressEvent(const KeyEvent& keyEvent) changeCoordFlag = true; moveOnePixelFlag = true; break; + case AnnotationCoordinateSpaceEnum::VIEWPORT: + break; case AnnotationCoordinateSpaceEnum::WINDOW: changeCoordFlag = true; moveOnePixelFlag = true; @@ -465,15 +468,17 @@ UserInputModeAnnotations::keyPressEvent(const KeyEvent& keyEvent) { bool surfaceFlag = false; switch (selectedAnnotation->getCoordinateSpace()) { - case AnnotationCoordinateSpaceEnum::STEREOTAXIC: + case AnnotationCoordinateSpaceEnum::CHART: break; - case AnnotationCoordinateSpaceEnum::PIXELS: + case AnnotationCoordinateSpaceEnum::STEREOTAXIC: break; case AnnotationCoordinateSpaceEnum::SURFACE: surfaceFlag = true; break; case AnnotationCoordinateSpaceEnum::TAB: break; + case AnnotationCoordinateSpaceEnum::VIEWPORT: + break; case AnnotationCoordinateSpaceEnum::WINDOW: break; } @@ -585,7 +590,7 @@ UserInputModeAnnotations::mouseLeftDrag(const MouseEvent& mouseEvent) break; } - AnnotationCoordinateSpaceEnum::Enum draggingCoordinateSpace = AnnotationCoordinateSpaceEnum::PIXELS; + AnnotationCoordinateSpaceEnum::Enum draggingCoordinateSpace = AnnotationCoordinateSpaceEnum::VIEWPORT; std::vector selectedAnnotations = annotationManager->getAnnotationsSelectedForEditing(m_browserWindowIndex); const int32_t numSelectedAnnotations = static_cast(selectedAnnotations.size()); @@ -612,6 +617,25 @@ UserInputModeAnnotations::mouseLeftDrag(const MouseEvent& mouseEvent) } } + switch (draggingCoordinateSpace) { + case AnnotationCoordinateSpaceEnum::CHART: + break; + case AnnotationCoordinateSpaceEnum::STEREOTAXIC: + break; + case AnnotationCoordinateSpaceEnum::SURFACE: + break; + case AnnotationCoordinateSpaceEnum::TAB: + break; + case AnnotationCoordinateSpaceEnum::VIEWPORT: + /* + * No dragging in viewport space + */ + draggingValid = false; + break; + case AnnotationCoordinateSpaceEnum::WINDOW: + break; + } + if (draggingValid) { BrainOpenGLViewportContent* vpContent = mouseEvent.getViewportContent(); if (vpContent == NULL) { @@ -632,6 +656,24 @@ UserInputModeAnnotations::mouseLeftDrag(const MouseEvent& mouseEvent) float spaceHeight = 0.0; switch (draggingCoordinateSpace) { + case AnnotationCoordinateSpaceEnum::CHART: + { + /* + * Want viewport within montage that contains the surface + */ + Matrix4x4 projectionMatrix; + Matrix4x4 modelviewMatrix; + int32_t chartVP[4]; + if (vpContent->getChartDataMatricesAndViewport(projectionMatrix, + modelviewMatrix, + chartVP)) { + spaceOriginX = chartVP[0]; + spaceOriginY = chartVP[1]; + spaceWidth = chartVP[2]; + spaceHeight = chartVP[3]; + } + } + break; case AnnotationCoordinateSpaceEnum::STEREOTAXIC: { /* @@ -647,16 +689,6 @@ UserInputModeAnnotations::mouseLeftDrag(const MouseEvent& mouseEvent) spaceHeight = montVP[3]; } break; - case AnnotationCoordinateSpaceEnum::PIXELS: - { - int viewport[4]; - vpContent->getWindowViewport(viewport); - spaceOriginX = viewport[0]; - spaceOriginY = viewport[1]; - spaceWidth = viewport[2]; - spaceHeight = viewport[3]; - } - break; case AnnotationCoordinateSpaceEnum::SURFACE: { /* @@ -699,6 +731,9 @@ UserInputModeAnnotations::mouseLeftDrag(const MouseEvent& mouseEvent) spaceHeight = viewport[3]; } break; + case AnnotationCoordinateSpaceEnum::VIEWPORT: + CaretAssert(0); + break; case AnnotationCoordinateSpaceEnum::WINDOW: { int viewport[4]; @@ -743,7 +778,26 @@ UserInputModeAnnotations::mouseLeftDrag(const MouseEvent& mouseEvent) coordInfo.m_modelXYZ[2]); } + if (coordInfo.m_chartXYZValid) { + annSpatialMod.setChartCoordinateAtMouseXY(coordInfo.m_chartXYZ[0], + coordInfo.m_chartXYZ[1], + coordInfo.m_chartXYZ[2]); + } + if ((dx != 0.0) + || (dy != 0.0)) { + AnnotationCoordinateInformation previousMouseXYCoordInfo; + AnnotationCoordinateInformation::createCoordinateInformationFromXY(mouseEvent.getOpenGLWidget(), + mouseEvent.getViewportContent(), + mouseEvent.getX() - dx, + mouseEvent.getY() - dy, + previousMouseXYCoordInfo); + if (previousMouseXYCoordInfo.m_chartXYZValid) { + annSpatialMod.setChartCoordinateAtPreviousMouseXY(previousMouseXYCoordInfo.m_chartXYZ[0], + previousMouseXYCoordInfo.m_chartXYZ[1], + previousMouseXYCoordInfo.m_chartXYZ[2]); + } + } std::vector annotationsBeforeMoveAndResize; std::vector annotationsAfterMoveAndResize; @@ -953,6 +1007,11 @@ UserInputModeAnnotations::setAnnotationUnderMouse(const MouseEvent& mouseEvent, } openGLWidget->updateCursor(); +#if BRAIN_OPENGL_INFO_SUPPORTS_DISPLAY_LISTS + openGLWidget->updateGL(); +#else + openGLWidget->update(); +#endif } /** @@ -1326,7 +1385,7 @@ UserInputModeAnnotations::showContextMenu(const MouseEvent& mouseEvent, UserInputModeAnnotationsContextMenu contextMenu(this, mouseEvent, - NULL, //idManager, + NULL, tabContent, openGLWidget); if (contextMenu.actions().size() > 0) { @@ -1566,29 +1625,37 @@ UserInputModeAnnotations::getEnabledEditMenuItems(std::vector::const_iterator iter = allSelectedAnnotations.begin(); iter != allSelectedAnnotations.end(); iter++) { const Annotation* ann = *iter; - if (ann->getType() == AnnotationTypeEnum::COLOR_BAR) { - noColorBarsSelectedFlag = false; + if ( ! ann->testProperty(Annotation::Property::COPY_CUT_PASTE)) { + allAllowCopyCutPasteFlag = false; + } + if ( ! ann->testProperty(Annotation::Property::DELETION)) { + allAllowDeleteFlag = false; } } - if (noColorBarsSelectedFlag) { - const bool anySelectedFlag = ( ! allSelectedAnnotations.empty()); - const bool oneSelectedFlag = (allSelectedAnnotations.size() == 1); - + const bool anySelectedFlag = ( ! allSelectedAnnotations.empty()); + const bool oneSelectedFlag = (allSelectedAnnotations.size() == 1); + if (allAllowCopyCutPasteFlag) { if (oneSelectedFlag) { enabledEditMenuItemsOut.push_back(BrainBrowserWindowEditMenuItemEnum::COPY); enabledEditMenuItemsOut.push_back(BrainBrowserWindowEditMenuItemEnum::CUT); } - + } + + if (allAllowDeleteFlag) { if (anySelectedFlag) { enabledEditMenuItemsOut.push_back(BrainBrowserWindowEditMenuItemEnum::DELETER); } - + } + + if (allAllowSelectFlag) { enabledEditMenuItemsOut.push_back(BrainBrowserWindowEditMenuItemEnum::SELECT_ALL); } diff --git a/src/GuiQt/UserInputModeAnnotationsContextMenu.cxx b/src/GuiQt/UserInputModeAnnotationsContextMenu.cxx index fd845d8dd630d41b5d0432bb835847411a1f3e7c..78158b914900b9e139649708cf44b7035a99a6d8 100644 --- a/src/GuiQt/UserInputModeAnnotationsContextMenu.cxx +++ b/src/GuiQt/UserInputModeAnnotationsContextMenu.cxx @@ -39,6 +39,8 @@ #include "BrainOpenGLWidget.h" #include "BrowserTabContent.h" #include "CaretAssert.h" +#include "DisplayPropertiesAnnotation.h" +#include "EventBrowserTabGetAll.h" #include "EventGraphicsUpdateAllWindows.h" #include "EventManager.h" #include "EventUserInterfaceUpdate.h" @@ -90,46 +92,60 @@ m_newAnnotationCreatedByContextMenu(NULL) const int32_t browserWindexIndex = m_mouseEvent.getBrowserWindowIndex(); std::vector > selectedAnnotations; AnnotationManager* annotationManager = GuiManager::get()->getBrain()->getAnnotationManager(); - annotationManager->getAnnotationsSelectedForEditing(browserWindexIndex, - selectedAnnotations); + annotationManager->getAnnotationsSelectedForEditingIncludingLabels(browserWindexIndex, + selectedAnnotations); m_annotationFile = NULL; m_annotation = NULL; - m_stereotaxicAndSurfaceAnnotations.clear(); + bool allSelectedAnnotationsDeletableFlag = true; + if (selectedAnnotations.empty()) { + allSelectedAnnotationsDeletableFlag = false; + } + + m_threeDimCoordAnnotations.clear(); for (std::vector >::iterator iter = selectedAnnotations.begin(); iter != selectedAnnotations.end(); iter++) { Annotation* ann = iter->first; CaretAssert(ann); - bool stereoOrSurfaceSpaceFlag = false; + bool threeDimCoordFlag = false; switch (ann->getCoordinateSpace()) { - case AnnotationCoordinateSpaceEnum::PIXELS: + case AnnotationCoordinateSpaceEnum::CHART: + threeDimCoordFlag = true; break; case AnnotationCoordinateSpaceEnum::STEREOTAXIC: - stereoOrSurfaceSpaceFlag = true; + threeDimCoordFlag = true; break; case AnnotationCoordinateSpaceEnum::SURFACE: - stereoOrSurfaceSpaceFlag = true; + threeDimCoordFlag = true; break; case AnnotationCoordinateSpaceEnum::TAB: break; + case AnnotationCoordinateSpaceEnum::VIEWPORT: + break; case AnnotationCoordinateSpaceEnum::WINDOW: break; } - if (stereoOrSurfaceSpaceFlag) { - m_stereotaxicAndSurfaceAnnotations.push_back(ann); + if (threeDimCoordFlag) { + m_threeDimCoordAnnotations.push_back(ann); + } + + if ( ! ann->testProperty(Annotation::Property::DELETION)) { + allSelectedAnnotationsDeletableFlag = false; } } - const bool haveStereotaxicAnnotationsFlag = ( ! m_stereotaxicAndSurfaceAnnotations.empty()); + const bool haveThreeDimCoordAnnotationsFlag = ( ! m_threeDimCoordAnnotations.empty()); - bool oneAnnotationSelectedFlag = false; + bool oneDeletableAnnotationSelectedFlag = false; if (selectedAnnotations.size() == 1) { CaretAssertVectorIndex(selectedAnnotations, 0); m_annotationFile = selectedAnnotations[0].second; m_annotation = selectedAnnotations[0].first; - oneAnnotationSelectedFlag = true; + if (m_annotation->testProperty(Annotation::Property::DELETION)) { + oneDeletableAnnotationSelectedFlag = true; + } } m_textAnnotation = NULL; @@ -153,21 +169,29 @@ m_newAnnotationCreatedByContextMenu(NULL) */ QAction* cutAction = addAction(BrainBrowserWindowEditMenuItemEnum::toGuiName(BrainBrowserWindowEditMenuItemEnum::CUT), this, SLOT(cutAnnnotation())); - cutAction->setEnabled(oneAnnotationSelectedFlag); + cutAction->setEnabled(oneDeletableAnnotationSelectedFlag); /* * Copy */ QAction* copyAction = addAction(BrainBrowserWindowEditMenuItemEnum::toGuiName(BrainBrowserWindowEditMenuItemEnum::COPY), this, SLOT(copyAnnotationToAnnotationClipboard())); - copyAction->setEnabled(oneAnnotationSelectedFlag); + copyAction->setEnabled(oneDeletableAnnotationSelectedFlag); /* * Delete */ QAction* deleteAction = addAction(BrainBrowserWindowEditMenuItemEnum::toGuiName(BrainBrowserWindowEditMenuItemEnum::DELETER), this, SLOT(deleteAnnotations())); - deleteAction->setEnabled( ! selectedAnnotations.empty()); + deleteAction->setEnabled(allSelectedAnnotationsDeletableFlag); + + /* + * Duplicate tab space annotation + */ + QMenu* duplicateMenu = createDuplicateTabSpaceAnnotationMenu(); + if (duplicateMenu != NULL) { + addMenu(duplicateMenu); + } /* * Paste @@ -213,23 +237,32 @@ m_newAnnotationCreatedByContextMenu(NULL) addSeparator(); /* - * Turn on/off display in tabs + * Turn off display in tabs */ - QAction* turnOffTabDisplayAction = addAction("Turn Off Stereotaxic/Surface Annotation Display in Other Tabs", + QAction* turnOffTabDisplayAction = addAction("Turn Off Chart/Stereotaxic/Surface Annotation Display in Other Tabs", this, SLOT(turnOffDisplayInOtherTabs())); - QAction* turnOnTabDisplayAction = addAction("Turn On Stereotaxic/Surface Annotation Display in All Tabs", + + /* + * Separator + */ + addSeparator(); + + /* + * Turn on display in tabs + */ + QAction* turnOnTabDisplayAction = addAction("Turn On Chart/Stereotaxic/Surface Annotation Display in All Tabs", this, SLOT(turnOnDisplayInAllTabs())); - turnOffTabDisplayAction->setEnabled(haveStereotaxicAnnotationsFlag); - turnOnTabDisplayAction->setEnabled(haveStereotaxicAnnotationsFlag); + turnOffTabDisplayAction->setEnabled(haveThreeDimCoordAnnotationsFlag); + turnOnTabDisplayAction->setEnabled(haveThreeDimCoordAnnotationsFlag); /* * Turn on/off display in groups */ - QAction* turnOnGroupDisplayAction = addAction("Turn On Stereotaxic/Surface Annotation Display in All Groups", + QAction* turnOnGroupDisplayAction = addAction("Turn On Chart/Stereotaxic/Surface Annotation Display in All Groups", this, SLOT(turnOnDisplayInAllGroups())); - turnOnGroupDisplayAction->setEnabled(haveStereotaxicAnnotationsFlag); + turnOnGroupDisplayAction->setEnabled(haveThreeDimCoordAnnotationsFlag); QMenu* turnOnInDisplayGroupMenu = createTurnOnInDisplayGroupMenu(); - turnOnInDisplayGroupMenu->setEnabled(haveStereotaxicAnnotationsFlag); + turnOnInDisplayGroupMenu->setEnabled(haveThreeDimCoordAnnotationsFlag); addMenu(turnOnInDisplayGroupMenu); /* @@ -379,8 +412,8 @@ UserInputModeAnnotationsContextMenu::setAnnotationText() void UserInputModeAnnotationsContextMenu::turnOffDisplayInOtherTabs() { - for (std::vector::iterator iter = m_stereotaxicAndSurfaceAnnotations.begin(); - iter != m_stereotaxicAndSurfaceAnnotations.end(); + for (std::vector::iterator iter = m_threeDimCoordAnnotations.begin(); + iter != m_threeDimCoordAnnotations.end(); iter++) { (*iter)->setItemDisplaySelectedInOneTab(m_browserTabContent->getTabNumber()); } @@ -395,8 +428,8 @@ UserInputModeAnnotationsContextMenu::turnOffDisplayInOtherTabs() void UserInputModeAnnotationsContextMenu::turnOnDisplayInAllTabs() { - for (std::vector::iterator iter = m_stereotaxicAndSurfaceAnnotations.begin(); - iter != m_stereotaxicAndSurfaceAnnotations.end(); + for (std::vector::iterator iter = m_threeDimCoordAnnotations.begin(); + iter != m_threeDimCoordAnnotations.end(); iter++) { (*iter)->setItemDisplaySelectedInAllTabs(); } @@ -411,8 +444,8 @@ UserInputModeAnnotationsContextMenu::turnOnDisplayInAllTabs() void UserInputModeAnnotationsContextMenu::turnOnDisplayInAllGroups() { - for (std::vector::iterator iter = m_stereotaxicAndSurfaceAnnotations.begin(); - iter != m_stereotaxicAndSurfaceAnnotations.end(); + for (std::vector::iterator iter = m_threeDimCoordAnnotations.begin(); + iter != m_threeDimCoordAnnotations.end(); iter++) { (*iter)->setItemDisplaySelectedInAllGroups(); } @@ -444,8 +477,8 @@ UserInputModeAnnotationsContextMenu::turnOnDisplayInGroup(QAction* action) return; } - for (std::vector::iterator iter = m_stereotaxicAndSurfaceAnnotations.begin(); - iter != m_stereotaxicAndSurfaceAnnotations.end(); + for (std::vector::iterator iter = m_threeDimCoordAnnotations.begin(); + iter != m_threeDimCoordAnnotations.end(); iter++) { (*iter)->setItemDisplaySelectedInOneGroup(displayGroup); } @@ -454,10 +487,13 @@ UserInputModeAnnotationsContextMenu::turnOnDisplayInGroup(QAction* action) EventManager::get()->sendEvent(EventGraphicsUpdateAllWindows().getPointer()); } +/** + * @return New menu for turning on in display group + */ QMenu* UserInputModeAnnotationsContextMenu::createTurnOnInDisplayGroupMenu() { - QMenu* menu = new QMenu("Turn On Stereotaxic/Surface Annotation Only in Group"); + QMenu* menu = new QMenu("Turn On Chart/Stereotaxic/Surface Annotation Only in Group"); QObject::connect(menu, SIGNAL(triggered(QAction*)), this, SLOT(turnOnDisplayInGroup(QAction*))); @@ -475,6 +511,88 @@ UserInputModeAnnotationsContextMenu::createTurnOnInDisplayGroupMenu() return menu; } +/** + * @return New menu for duplicating a tab annotation. + * NULL is returned if no other tabs. + */ +QMenu* +UserInputModeAnnotationsContextMenu::createDuplicateTabSpaceAnnotationMenu() +{ + if (m_annotation == NULL) { + return NULL; + } + + EventBrowserTabGetAll tabIndicesEvent; + EventManager::get()->sendEvent(tabIndicesEvent.getPointer()); + const std::vector allTabs = tabIndicesEvent.getAllBrowserTabs(); + + bool menuValidFlag = false; + if (m_annotation->getCoordinateSpace() == AnnotationCoordinateSpaceEnum::TAB) { + if (allTabs.size() > 1) { + menuValidFlag = true; + } + } + + QMenu* menu = new QMenu("Duplicate to Tab"); + QObject::connect(menu, SIGNAL(triggered(QAction*)), + this, SLOT(duplicateAnnotationSelected(QAction*))); + + if (menuValidFlag) { + for (BrowserTabContent* tabContent : allTabs) { + if (tabContent->getTabNumber() != m_annotation->getTabIndex()) { + QAction* action = menu->addAction(tabContent->getName()); + action->setData((int)tabContent->getTabNumber()); + } + } + } + else { + menu->setEnabled(false); + } + + return menu; +} + +/** + * Gets called when a selection is made from Duplicate Tab Annotation menu + * + * @param action + * Action selected from Duplicate menu + */ +void +UserInputModeAnnotationsContextMenu::duplicateAnnotationSelected(QAction* action) +{ + CaretAssert(action); + CaretAssert(m_annotationFile); + CaretAssert(m_annotation); + + AnnotationManager* annotationManager = GuiManager::get()->getBrain()->getAnnotationManager(); + + const int32_t tabIndex = action->data().toInt(); + + Annotation* annCopy = m_annotation->clone(); + annCopy->setTabIndex(tabIndex); + + DisplayPropertiesAnnotation* dpa = GuiManager::get()->getBrain()->getDisplayPropertiesAnnotation(); + dpa->updateForNewAnnotation(annCopy); + + AnnotationRedoUndoCommand* undoCommand = new AnnotationRedoUndoCommand(); + undoCommand->setModeDuplicateAnnotation(m_annotationFile, + annCopy); + AString errorMessage; + if ( ! annotationManager->applyCommand(undoCommand, + errorMessage)) { + WuQMessageBox::errorOk(this, + errorMessage); + } + annotationManager->selectAnnotationForEditing(m_mouseEvent.getBrowserWindowIndex(), + AnnotationManager::SELECTION_MODE_SINGLE, + false, + annCopy); + + EventManager::get()->sendEvent(EventGraphicsUpdateAllWindows().getPointer()); + EventManager::get()->sendEvent(EventUserInterfaceUpdate().getPointer()); +} + /** * Group annotations. */ diff --git a/src/GuiQt/UserInputModeAnnotationsContextMenu.h b/src/GuiQt/UserInputModeAnnotationsContextMenu.h index 05257f7fb258e17faf87e756e37dbc6f400c6305..61d582c55cc7d73b21c1ef5c141f98672cc84c2d 100644 --- a/src/GuiQt/UserInputModeAnnotationsContextMenu.h +++ b/src/GuiQt/UserInputModeAnnotationsContextMenu.h @@ -83,6 +83,8 @@ namespace caret { void applyGroupingUngroup(); + void duplicateAnnotationSelected(QAction*); + private: UserInputModeAnnotationsContextMenu(const UserInputModeAnnotationsContextMenu&); @@ -92,6 +94,8 @@ namespace caret { QMenu* createTurnOnInDisplayGroupMenu(); + QMenu* createDuplicateTabSpaceAnnotationMenu(); + UserInputModeAnnotations* m_userInputModeAnnotations; /* @@ -107,7 +111,7 @@ namespace caret { AnnotationFile* m_annotationFile; - std::vector m_stereotaxicAndSurfaceAnnotations; + std::vector m_threeDimCoordAnnotations; Annotation* m_annotation; diff --git a/src/GuiQt/UserInputModeBordersWidget.cxx b/src/GuiQt/UserInputModeBordersWidget.cxx index 52fff673119652abe4948f066c6e7d10506daa43..61ef74a4d988ac9ff6e4e12a97e0dcdf9020f644 100644 --- a/src/GuiQt/UserInputModeBordersWidget.cxx +++ b/src/GuiQt/UserInputModeBordersWidget.cxx @@ -284,6 +284,7 @@ UserInputModeBordersWidget::createDrawOperationWidget() drawAction->setData(static_cast(UserInputModeBorders::DRAW_OPERATION_CREATE)); QToolButton* drawToolButton = new QToolButton(); drawToolButton->setDefaultAction(drawAction); + WuQtUtilities::setToolButtonStyleForQt5Mac(drawToolButton); /* * Erase @@ -301,6 +302,7 @@ UserInputModeBordersWidget::createDrawOperationWidget() eraseAction->setData(static_cast(UserInputModeBorders::DRAW_OPERATION_ERASE)); QToolButton* eraseToolButton = new QToolButton(); eraseToolButton->setDefaultAction(eraseAction); + WuQtUtilities::setToolButtonStyleForQt5Mac(eraseToolButton); /* * Extend @@ -322,6 +324,7 @@ UserInputModeBordersWidget::createDrawOperationWidget() extendAction->setData(static_cast(UserInputModeBorders::DRAW_OPERATION_EXTEND)); QToolButton* extendToolButton = new QToolButton(); extendToolButton->setDefaultAction(extendAction); + WuQtUtilities::setToolButtonStyleForQt5Mac(extendToolButton); /* * Replace @@ -343,6 +346,7 @@ UserInputModeBordersWidget::createDrawOperationWidget() replaceAction->setData(static_cast(UserInputModeBorders::DRAW_OPERATION_REPLACE)); QToolButton* replaceToolButton = new QToolButton(); replaceToolButton->setDefaultAction(replaceAction); + WuQtUtilities::setToolButtonStyleForQt5Mac(replaceToolButton); /* * Optimize @@ -357,6 +361,7 @@ UserInputModeBordersWidget::createDrawOperationWidget() optimizeAction->setData(static_cast(UserInputModeBorders::DRAW_OPERATION_OPTIMIZE)); QToolButton* optimizeToolButton = new QToolButton(); optimizeToolButton->setDefaultAction(optimizeAction); + WuQtUtilities::setToolButtonStyleForQt5Mac(optimizeToolButton); /* * Finish @@ -371,6 +376,7 @@ UserInputModeBordersWidget::createDrawOperationWidget() SLOT(drawFinishButtonClicked())); QToolButton* finishToolButton = new QToolButton(); finishToolButton->setDefaultAction(finishAction); + WuQtUtilities::setToolButtonStyleForQt5Mac(finishToolButton); /* * Undo @@ -388,6 +394,7 @@ UserInputModeBordersWidget::createDrawOperationWidget() undoToolButton->setAutoRepeat(true); undoToolButton->setAutoRepeatDelay(500); // 500ms = 1/2 second undoToolButton->setAutoRepeatInterval(100); // 100ms = 1/10 second + WuQtUtilities::setToolButtonStyleForQt5Mac(undoToolButton); QAction* undoFinishAction = WuQtUtilities::createAction("Undo Finish", @@ -398,6 +405,7 @@ UserInputModeBordersWidget::createDrawOperationWidget() SLOT(drawUndoLastEditButtonClicked())); m_undoFinishToolButton = new QToolButton(); m_undoFinishToolButton->setDefaultAction(undoFinishAction); + WuQtUtilities::setToolButtonStyleForQt5Mac(m_undoFinishToolButton); /* * Reset @@ -409,6 +417,7 @@ UserInputModeBordersWidget::createDrawOperationWidget() SLOT(drawResetButtonClicked())); QToolButton* resetToolButton = new QToolButton(); resetToolButton->setDefaultAction(resetAction); + WuQtUtilities::setToolButtonStyleForQt5Mac(resetToolButton); this->drawOperationActionGroup = new QActionGroup(this); this->drawOperationActionGroup->addAction(drawAction); @@ -600,11 +609,16 @@ UserInputModeBordersWidget::drawFinishButtonClicked() } if (surface == NULL) { - CaretLogSevere("PROGRAM ERROR: Cannot find surface for border drawing."); + AString tabsMessage; + if (browserWindow->isTileTabsSelected()) { + tabsMessage = "Verify that points are added in the SELECTED tab."; + } + WuQMessageBox::errorOk(this, "Borders may only be drawn on surface models. " + + tabsMessage); return; } if (brain == NULL) { - CaretLogSevere("PROGRAM ERROR: Cannot find surface for border drawing."); + CaretLogSevere("PROGRAM ERROR: Cannot find brain for border drawing."); return; } @@ -988,6 +1002,7 @@ UserInputModeBordersWidget::createEditOperationWidget() deleteAction->setData(static_cast(UserInputModeBorders::EDIT_OPERATION_DELETE)); QToolButton* deleteToolButton = new QToolButton(); deleteToolButton->setDefaultAction(deleteAction); + WuQtUtilities::setToolButtonStyleForQt5Mac(deleteToolButton); const AString propertiesToolTipText = ("A dialog for editing a border's properties is displayed by " "clicking any point in a border." @@ -999,6 +1014,7 @@ UserInputModeBordersWidget::createEditOperationWidget() propertiesAction->setData(static_cast(UserInputModeBorders::EDIT_OPERATION_PROPERTIES)); QToolButton* propertiesToolButton = new QToolButton(); propertiesToolButton->setDefaultAction(propertiesAction); + WuQtUtilities::setToolButtonStyleForQt5Mac(propertiesToolButton); this->editOperationActionGroup = new QActionGroup(this); this->editOperationActionGroup->addAction(deleteAction); diff --git a/src/GuiQt/UserInputModeFociWidget.cxx b/src/GuiQt/UserInputModeFociWidget.cxx index 1782b84c1eb0b23ab33902fa541397fb6c9114ae..82da5b06ad35d544bdc3ac17cf3fbbdfeb4dc20d 100644 --- a/src/GuiQt/UserInputModeFociWidget.cxx +++ b/src/GuiQt/UserInputModeFociWidget.cxx @@ -233,6 +233,7 @@ UserInputModeFociWidget::createCreateOperationWidget() SLOT(createNewFocusActionTriggered())); QToolButton* newFocusToolButton = new QToolButton(); newFocusToolButton->setDefaultAction(newFocusAction); + WuQtUtilities::setToolButtonStyleForQt5Mac(newFocusToolButton); const AString lastIDToolTipText = ("Press this button to display a dialog for creating a new focus " "with the focus' coordinates set to the stereotaxic location of the " @@ -246,6 +247,7 @@ UserInputModeFociWidget::createCreateOperationWidget() SLOT(createLastIdentificationFocusActionTriggered())); QToolButton* lastIdFocusToolButton = new QToolButton(); lastIdFocusToolButton->setDefaultAction(lastIdFocusAction); + WuQtUtilities::setToolButtonStyleForQt5Mac(lastIdFocusToolButton); QWidget* widget = new QWidget(); QHBoxLayout* layout = new QHBoxLayout(widget); @@ -391,6 +393,7 @@ UserInputModeFociWidget::createEditOperationWidget() deleteAction->setData(static_cast(UserInputModeFoci::EDIT_OPERATION_DELETE)); QToolButton* deleteToolButton = new QToolButton(); deleteToolButton->setDefaultAction(deleteAction); + WuQtUtilities::setToolButtonStyleForQt5Mac(deleteToolButton); const AString propertiesToolTipText = ("Click the mouse over a focus to display a dialog " "for editing the focus' properties." @@ -402,6 +405,7 @@ UserInputModeFociWidget::createEditOperationWidget() propertiesAction->setData(static_cast(UserInputModeFoci::EDIT_OPERATION_PROPERTIES)); QToolButton* propertiesToolButton = new QToolButton(); propertiesToolButton->setDefaultAction(propertiesAction); + WuQtUtilities::setToolButtonStyleForQt5Mac(propertiesToolButton); m_editOperationActionGroup = new QActionGroup(this); m_editOperationActionGroup->addAction(deleteAction); diff --git a/src/GuiQt/UserInputModeImage.cxx b/src/GuiQt/UserInputModeImage.cxx index 3d4ec5f3e4e818c2122cfe66888fc075c964e6ca..7b567a8ad097fc0e443230ef09a0348374dea031 100644 --- a/src/GuiQt/UserInputModeImage.cxx +++ b/src/GuiQt/UserInputModeImage.cxx @@ -33,7 +33,7 @@ #include "ControlPoint3D.h" #include "ControlPointFile.h" #include "DisplayPropertiesImages.h" -#include "EventBrowserWindowContentGet.h" +#include "EventBrowserWindowDrawingContent.h" #include "EventGraphicsUpdateAllWindows.h" #include "EventGraphicsUpdateOneWindow.h" #include "EventManager.h" @@ -225,11 +225,11 @@ UserInputModeImage::mouseLeftClick(const MouseEvent& mouseEvent) updateAfterControlPointsChanged(); if ( ! toolTipMessage.isEmpty()) { - WuQTimedMessageDisplay::show(openGLWidget, - mouseEvent.getX(), - mouseEvent.getY(), - 2, - toolTipMessage); + WuQTimedMessageDisplay::showModal(openGLWidget, + mouseEvent.getX(), + mouseEvent.getY(), + 2, + toolTipMessage); } } @@ -297,7 +297,7 @@ UserInputModeImage::deleteAllControlPoints() ImageFile* UserInputModeImage::getImageFile() const { - EventBrowserWindowContentGet windowGet(m_windowIndex); + EventBrowserWindowDrawingContent windowGet(m_windowIndex); EventManager::get()->sendEvent(windowGet.getPointer()); DisplayPropertiesImages* dpi = GuiManager::get()->getBrain()->getDisplayPropertiesImages(); @@ -322,7 +322,7 @@ UserInputModeImage::getTabIndex() const { int32_t tabIndex = -1; - EventBrowserWindowContentGet windowGet(m_windowIndex); + EventBrowserWindowDrawingContent windowGet(m_windowIndex); EventManager::get()->sendEvent(windowGet.getPointer()); //DisplayPropertiesImages* dpi = GuiManager::get()->getBrain()->getDisplayPropertiesImages(); diff --git a/src/GuiQt/UserInputModeImageWidget.cxx b/src/GuiQt/UserInputModeImageWidget.cxx index 69a2895e9734e964d67eb86b3461b9d47e4c3dd5..7a1690aa5fa9b6e1676a232d32f91db80bc72191 100644 --- a/src/GuiQt/UserInputModeImageWidget.cxx +++ b/src/GuiQt/UserInputModeImageWidget.cxx @@ -169,6 +169,7 @@ UserInputModeImageWidget::createEditOperationWidget() addAction->setData(static_cast(UserInputModeImage::EDIT_OPERATION_ADD)); QToolButton* addToolButton = new QToolButton(); addToolButton->setDefaultAction(addAction); + WuQtUtilities::setToolButtonStyleForQt5Mac(addToolButton); /* * Delete button @@ -182,6 +183,7 @@ UserInputModeImageWidget::createEditOperationWidget() deleteAction->setData(static_cast(UserInputModeImage::EDIT_OPERATION_DELETE)); QToolButton* deleteToolButton = new QToolButton(); deleteToolButton->setDefaultAction(deleteAction); + WuQtUtilities::setToolButtonStyleForQt5Mac(deleteToolButton); /* * Action group to make add/delete actions mutually exclusive @@ -205,6 +207,7 @@ UserInputModeImageWidget::createEditOperationWidget() convertAction->setCheckable(false); m_convertToolButton = new QToolButton(); m_convertToolButton->setDefaultAction(convertAction); + WuQtUtilities::setToolButtonStyleForQt5Mac(m_convertToolButton); /* * Delete all button @@ -218,7 +221,7 @@ UserInputModeImageWidget::createEditOperationWidget() deleteAllAction->setCheckable(false); m_deleteAllToolButton = new QToolButton(); m_deleteAllToolButton->setDefaultAction(deleteAllAction); - + WuQtUtilities::setToolButtonStyleForQt5Mac(m_deleteAllToolButton); QWidget* widget = new QWidget(); diff --git a/src/GuiQt/UserInputModeView.cxx b/src/GuiQt/UserInputModeView.cxx index 3614e5b10f366e133e8de372eef253f0ef07c6f0..158453e5ef802da97fbcdf61964958f728c193f0 100644 --- a/src/GuiQt/UserInputModeView.cxx +++ b/src/GuiQt/UserInputModeView.cxx @@ -20,6 +20,7 @@ /*LICENSE_END*/ #include +#include #define __USER_INPUT_MODE_VIEW_DECLARE__ #include "UserInputModeView.h" @@ -29,12 +30,19 @@ #include "BrainOpenGLViewportContent.h" #include "BrainOpenGLWidget.h" #include "BrowserTabContent.h" +#include "ChartTwoCartesianAxis.h" +#include "ChartTwoOverlaySet.h" #include "EventGraphicsUpdateOneWindow.h" +#include "EventGraphicsUpdateAllWindows.h" #include "EventUpdateYokedWindows.h" +#include "EventUserInterfaceUpdate.h" #include "EventManager.h" #include "GuiManager.h" #include "MouseEvent.h" +#include "SelectionItemChartTwoLabel.h" +#include "SelectionManager.h" #include "UserInputModeViewContextMenu.h" +#include "WuQDataEntryDialog.h" using namespace caret; @@ -162,6 +170,50 @@ UserInputModeView::toString() const return "UserInputModeView"; } +/** + * Process a mouse left double-click event. + * + * @param mouseEvent + * Mouse event information. + */ +void +UserInputModeView::mouseLeftDoubleClick(const MouseEvent& mouseEvent) +{ + const bool allowDoubleClickToEditChartLabel = false; + if (allowDoubleClickToEditChartLabel) { + const int32_t mouseX = mouseEvent.getX(); + const int32_t mouseY = mouseEvent.getY(); + + BrainOpenGLWidget* openGLWidget = mouseEvent.getOpenGLWidget(); + SelectionManager* idManager = openGLWidget->performIdentification(mouseX, + mouseY, + false); + CaretAssert(idManager); + SelectionItemChartTwoLabel* labelID = idManager->getChartTwoLabelIdentification(); + if (labelID->isValid()) { + ChartTwoCartesianAxis* axis = labelID->getChartTwoCartesianAxis(); + ChartTwoOverlaySet* chartOverlaySet = labelID->getChartOverlaySet(); + if ((axis != NULL) + && (chartOverlaySet != NULL)) { + WuQDataEntryDialog newNameDialog("Axis Label", + openGLWidget); + QLineEdit* lineEdit = newNameDialog.addLineEditWidget("Label"); + lineEdit->setText(chartOverlaySet->getAxisLabel(axis)); + if (newNameDialog.exec() == WuQDataEntryDialog::Accepted) { + const AString name = lineEdit->text().trimmed(); + chartOverlaySet->setAxisLabel(axis, + name); + + /* + * Update graphics. + */ + updateGraphics(mouseEvent); + } + } + } + } +} + /** * Process a mouse left click event. * @@ -332,14 +384,12 @@ UserInputModeView::showContextMenu(const MouseEvent& mouseEvent, mouseY, false); - UserInputModeViewContextMenu contextMenu(idManager, - tabContent, + UserInputModeViewContextMenu contextMenu(viewportContent, + idManager, openGLWidget); contextMenu.exec(menuPosition); } - - /** * If this windows is yoked, issue an event to update other * windows that are using the same yoking. @@ -353,10 +403,23 @@ UserInputModeView::updateGraphics(const MouseEvent& mouseEvent) const int32_t browserWindowIndex = mouseEvent.getBrowserWindowIndex(); EventManager::get()->sendEvent(EventGraphicsUpdateOneWindow(browserWindowIndex).getPointer()); + YokingGroupEnum::Enum brainYokingGroup = YokingGroupEnum::YOKING_GROUP_OFF; + YokingGroupEnum::Enum chartYokingGroup = YokingGroupEnum::YOKING_GROUP_OFF; + if (browserTabContent != NULL) { - if (browserTabContent->isYoked()) { + if (browserTabContent->isBrainModelYoked()) { + brainYokingGroup = browserTabContent->getBrainModelYokingGroup(); + issuedYokeEvent = true; + } + if (browserTabContent->isChartModelYoked()) { + chartYokingGroup = browserTabContent->getChartModelYokingGroup(); issuedYokeEvent = true; - EventManager::get()->sendEvent(EventUpdateYokedWindows(browserTabContent->getYokingGroup()).getPointer()); + } + + if (issuedYokeEvent) { + EventManager::get()->sendEvent(EventGraphicsUpdateAllWindows().getPointer()); + EventManager::get()->sendEvent(EventUpdateYokedWindows(brainYokingGroup, + chartYokingGroup).getPointer()); } } } @@ -364,9 +427,8 @@ UserInputModeView::updateGraphics(const MouseEvent& mouseEvent) /* * If not yoked, just need to update graphics. */ - if (issuedYokeEvent == false) { + if ( ! issuedYokeEvent) { EventManager::get()->sendEvent(EventGraphicsUpdateOneWindow(mouseEvent.getBrowserWindowIndex()).getPointer()); } } - diff --git a/src/GuiQt/UserInputModeView.h b/src/GuiQt/UserInputModeView.h index 59a9f40f4a0aa363d4c1822f52518a1faef0ffc0..e1192307516b712b067b9894374b89ec61f412a1 100644 --- a/src/GuiQt/UserInputModeView.h +++ b/src/GuiQt/UserInputModeView.h @@ -42,6 +42,8 @@ namespace caret { virtual CursorEnum::Enum getCursor() const; + virtual void mouseLeftDoubleClick(const MouseEvent& mouseEvent); + virtual void mouseLeftClick(const MouseEvent& mouseEvent); virtual void mouseLeftClickWithShift(const MouseEvent& mouseEvent); diff --git a/src/GuiQt/UserInputModeViewContextMenu.cxx b/src/GuiQt/UserInputModeViewContextMenu.cxx index 54e5e3f54f627954c63868efe456f2bf2bb35965..cb9dc83929e12ad9836b4d91777daa6cfcbb1605 100644 --- a/src/GuiQt/UserInputModeViewContextMenu.cxx +++ b/src/GuiQt/UserInputModeViewContextMenu.cxx @@ -20,6 +20,7 @@ /*LICENSE_END*/ #include +#include #define __USER_INPUT_MODE_VIEW_CONTEXT_MENU_DECLARE__ #include "UserInputModeViewContextMenu.h" @@ -29,11 +30,15 @@ #include "AlgorithmNodesInsideBorder.h" #include "Border.h" #include "Brain.h" +#include "BrainBrowserWindow.h" +#include "BrainOpenGLViewportContent.h" #include "BrainOpenGLWidget.h" #include "BrainStructure.h" #include "BrowserTabContent.h" #include "ChartableLineSeriesBrainordinateInterface.h" #include "ChartingDataManager.h" +#include "ChartTwoCartesianAxis.h" +#include "ChartTwoOverlaySet.h" #include "CiftiBrainordinateLabelFile.h" #include "CiftiConnectivityMatrixDataFileManager.h" #include "CiftiFiberTrajectoryFile.h" @@ -57,6 +62,7 @@ #include "Model.h" #include "ProgressReportingDialog.h" #include "SelectionItemBorderSurface.h" +#include "SelectionItemChartTwoLabel.h" #include "SelectionItemFocusSurface.h" #include "SelectionItemFocusVolume.h" #include "SelectionItemSurfaceNode.h" @@ -66,7 +72,9 @@ #include "SessionManager.h" #include "Surface.h" #include "UserInputModeFociWidget.h" +#include "UserInputTileTabsContextMenu.h" #include "VolumeFile.h" +#include "WuQDataEntryDialog.h" #include "WuQMessageBox.h" #include "WuQtUtilities.h" @@ -83,22 +91,36 @@ using namespace caret; */ /** * Constructor. + * + * @param viewportContent + * Content of the viewport. * @param selectionManager * The selection manager, provides data under the cursor. - * @param browserTabContent - * Content of browser tab. * @param parentOpenGLWidget * Parent OpenGL Widget on which the menu is displayed. */ -UserInputModeViewContextMenu::UserInputModeViewContextMenu(SelectionManager* selectionManager, - BrowserTabContent* browserTabContent, +UserInputModeViewContextMenu::UserInputModeViewContextMenu(BrainOpenGLViewportContent* viewportContent, + SelectionManager* selectionManager, BrainOpenGLWidget* parentOpenGLWidget) : QMenu(parentOpenGLWidget) { + this->viewportContent = viewportContent; + CaretAssert(this->viewportContent); this->parentOpenGLWidget = parentOpenGLWidget; this->selectionManager = selectionManager; - this->browserTabContent = browserTabContent; - + this->browserTabContent = viewportContent->getBrowserTabContent(); + CaretAssert(this->browserTabContent); + + UserInputTileTabsContextMenu* tabMenu = new UserInputTileTabsContextMenu(this->parentOpenGLWidget, + this->viewportContent); + if (tabMenu->isValid()) { + addSubMenuToMenu(tabMenu, + true); + } + else { + delete tabMenu; + tabMenu = NULL; + } /* * Add the identification actions. */ @@ -109,6 +131,11 @@ UserInputModeViewContextMenu::UserInputModeViewContextMenu(SelectionManager* sel */ addBorderRegionOfInterestActions(); + /* + * Add the chart actions + */ + addChartActions(); + /* * Add the foci actions. */ @@ -117,16 +144,41 @@ UserInputModeViewContextMenu::UserInputModeViewContextMenu(SelectionManager* sel /* * Show Label ROI operations only for surfaces */ - addLabelRegionOfInterestActions(); + addLabelRegionOfInterestActions(); const SelectionItemSurfaceNodeIdentificationSymbol* idSymbol = selectionManager->getSurfaceNodeIdentificationSymbol(); - if (this->actions().count() > 0) { - this->addSeparator(); + bool showRemoveVertexSymbolsFlag = false; + if (this->browserTabContent != NULL) { + switch (this->browserTabContent->getSelectedModelType()) { + case ModelTypeEnum::MODEL_TYPE_CHART: + break; + case ModelTypeEnum::MODEL_TYPE_CHART_TWO: + break; + case ModelTypeEnum::MODEL_TYPE_INVALID: + break; + case ModelTypeEnum::MODEL_TYPE_SURFACE: + showRemoveVertexSymbolsFlag = true; + break; + case ModelTypeEnum::MODEL_TYPE_SURFACE_MONTAGE: + showRemoveVertexSymbolsFlag = true; + break; + case ModelTypeEnum::MODEL_TYPE_VOLUME_SLICES: + showRemoveVertexSymbolsFlag = true; + break; + case ModelTypeEnum::MODEL_TYPE_WHOLE_BRAIN: + showRemoveVertexSymbolsFlag = true; + break; + } + } + if (showRemoveVertexSymbolsFlag) { + if (this->actions().count() > 0) { + this->addSeparator(); + } + this->addAction("Remove All Vertex Identification Symbols", + this, + SLOT(removeAllNodeIdentificationSymbolsSelected())); } - this->addAction("Remove All Vertex Identification Symbols", - this, - SLOT(removeAllNodeIdentificationSymbolsSelected())); if (idSymbol->isValid()) { const AString text = ("Remove Identification of Vertices " @@ -156,7 +208,7 @@ UserInputModeViewContextMenu::~UserInputModeViewContextMenu() * Add the actions to this context menu. * * @param actionsToAdd - * Actions to add to the menum. + * Actions to add to the menu. * @param addSeparatorBeforeActions * If true and there are actions presently in the menu, a separator * (horizontal bar) is added prior to adding the given actions. @@ -176,6 +228,30 @@ UserInputModeViewContextMenu::addActionsToMenu(QList& actionsToAdd, } } +/** + * Add a submenu to this menu. + * + * @param menu + * Menu that is added. + * @param addSeparatorBeforeMenu + * If true and the menu is not empty, add a separator before + * adding the sub menu. + */ +void +UserInputModeViewContextMenu::addSubMenuToMenu(QMenu* menu, + const bool addSeparatorBeforeMenu) +{ + CaretAssert(menu); + + if (addSeparatorBeforeMenu) { + if (actions().isEmpty() == false) { + addSeparator(); + } + + addMenu(menu); + } + +} /** * Add the identification actions to the menu. @@ -644,6 +720,57 @@ UserInputModeViewContextMenu::addLabelRegionOfInterestActions() true); } +/** + * Add chart options to the menu + */ +void +UserInputModeViewContextMenu::addChartActions() +{ + QList chartActions; + + SelectionItemChartTwoLabel* labelID = this->selectionManager->getChartTwoLabelIdentification(); + if (labelID->isValid()) { + chartActions.push_back(WuQtUtilities::createAction("Edit Chart Axis Label...", + "", + this, + this, + SLOT(editChartLabelSelected()))); + } + + addActionsToMenu(chartActions, true); +} + +/** + * Called to edit the chart label. + */ +void +UserInputModeViewContextMenu::editChartLabelSelected() +{ + SelectionItemChartTwoLabel* labelID = this->selectionManager->getChartTwoLabelIdentification(); + if (labelID->isValid()) { + ChartTwoCartesianAxis* axis = labelID->getChartTwoCartesianAxis(); + ChartTwoOverlaySet* chartOverlaySet = labelID->getChartOverlaySet(); + if ((axis != NULL) + && (chartOverlaySet != NULL)) { + WuQDataEntryDialog newNameDialog("Axis Label", + this); + QLineEdit* lineEdit = newNameDialog.addLineEditWidget("Label"); + lineEdit->setText(chartOverlaySet->getAxisLabel(axis)); + if (newNameDialog.exec() == WuQDataEntryDialog::Accepted) { + const AString name = lineEdit->text().trimmed(); + chartOverlaySet->setAxisLabel(axis, + name); + + /* + * Update graphics. + */ + EventManager::get()->sendEvent(EventUserInterfaceUpdate().getPointer()); + EventManager::get()->sendEvent(EventGraphicsUpdateAllWindows().getPointer()); + } + } + } +} + /** * Add the foci options to the menu. */ @@ -813,7 +940,7 @@ UserInputModeViewContextMenu::parcelCiftiConnectivityActionSelected(QAction* act "", this); progressDialog.setValue(0); - + switch (pc->parcelType) { case ParcelConnectivity::PARCEL_TYPE_INVALID: break; @@ -956,6 +1083,7 @@ UserInputModeViewContextMenu::borderCiftiConnectivitySelected() "", this); progressDialog.setValue(0); + CiftiConnectivityMatrixDataFileManager* ciftiConnMann = SessionManager::get()->getCiftiConnectivityMatrixDataFileManager(); ciftiConnMann->loadAverageDataForSurfaceNodes(borderID->getBrain(), surface, diff --git a/src/GuiQt/UserInputModeViewContextMenu.h b/src/GuiQt/UserInputModeViewContextMenu.h index 0fc744c8f17a7d46d1a41cf06c5f029ffb5db4a0..7879cc9920c9f345e14d16da2487c5a3dd188052 100644 --- a/src/GuiQt/UserInputModeViewContextMenu.h +++ b/src/GuiQt/UserInputModeViewContextMenu.h @@ -34,6 +34,7 @@ class QAction; namespace caret { class Brain; + class BrainOpenGLViewportContent; class BrainOpenGLWidget; class BrowserTabContent; class CaretMappableDataFile; @@ -49,8 +50,8 @@ namespace caret { Q_OBJECT public: - UserInputModeViewContextMenu(SelectionManager* selectionManager, - BrowserTabContent* browserTabContent, + UserInputModeViewContextMenu(BrainOpenGLViewportContent* viewportContent, + SelectionManager* selectionManager, BrainOpenGLWidget* parentOpenGLWidget); virtual ~UserInputModeViewContextMenu(); @@ -90,6 +91,8 @@ namespace caret { void borderDataSeriesSelected(); + void editChartLabelSelected(); + private: class ParcelConnectivity { public: @@ -144,10 +147,15 @@ namespace caret { void addBorderRegionOfInterestActions(); + void addChartActions(); + void addFociActions(); void addLabelRegionOfInterestActions(); + void addSubMenuToMenu(QMenu* menu, + const bool addSeparatorBeforeMenu); + void addActionsToMenu(QList& actionsToAdd, const bool addSeparatorBeforeActions); @@ -155,6 +163,8 @@ namespace caret { std::vector parcelConnectivities; + BrainOpenGLViewportContent* viewportContent; + SelectionManager* selectionManager; BrowserTabContent* browserTabContent; diff --git a/src/GuiQt/UserInputModeVolumeEdit.cxx b/src/GuiQt/UserInputModeVolumeEdit.cxx index 0401ba66dcc8e7bc96d3e033b2b620cd0e610650..f841e095e3c369acbff65bae4dbad2f9630d094c 100644 --- a/src/GuiQt/UserInputModeVolumeEdit.cxx +++ b/src/GuiQt/UserInputModeVolumeEdit.cxx @@ -28,7 +28,7 @@ #include "BrainOpenGLViewportContent.h" #include "BrowserTabContent.h" #include "CaretAssert.h" -#include "EventBrowserWindowContentGet.h" +#include "EventBrowserWindowDrawingContent.h" #include "EventGraphicsUpdateAllWindows.h" #include "EventManager.h" #include "EventUserInterfaceUpdate.h" @@ -270,9 +270,7 @@ UserInputModeVolumeEdit::updateGraphicsAfterEditing(VolumeFile* volumeFile, CaretAssert((mapIndex >= 0) && (mapIndex < volumeFile->getNumberOfMaps())); volumeFile->clearVoxelColoringForMap(mapIndex); - PaletteFile* paletteFile = GuiManager::get()->getBrain()->getPaletteFile(); - volumeFile->updateScalarColoringForMap(mapIndex, - paletteFile); + volumeFile->updateScalarColoringForMap(mapIndex); EventManager::get()->sendEvent(EventGraphicsUpdateAllWindows().getPointer()); } @@ -328,7 +326,7 @@ UserInputModeVolumeEdit::getVolumeEditInfo(VolumeEditInfo& volumeEditInfo) volumeEditInfo.m_obliqueRotationMatrix.identity(); // volumeEditInfo.m_modelVolume = NULL; - EventBrowserWindowContentGet windowEvent(m_windowIndex); + EventBrowserWindowDrawingContent windowEvent(m_windowIndex); EventManager::get()->sendEvent(windowEvent.getPointer()); BrowserTabContent* tabContent = windowEvent.getSelectedBrowserTabContent(); diff --git a/src/GuiQt/UserInputModeVolumeEditWidget.cxx b/src/GuiQt/UserInputModeVolumeEditWidget.cxx index 104ecc83ace6eb6ae4d7ed7fa3283919e44329ee..cf2a18e7d8ee4cfb7a57ef62e3e883ed592a219c 100644 --- a/src/GuiQt/UserInputModeVolumeEditWidget.cxx +++ b/src/GuiQt/UserInputModeVolumeEditWidget.cxx @@ -236,6 +236,7 @@ UserInputModeVolumeEditWidget::createSelectionToolBar() "Create a new volume file that will become the top-most overlay", this, this, SLOT(newFileActionTriggered()))); + WuQtUtilities::setToolButtonStyleForQt5Mac(m_newFileToolButton); m_addMapsToolButton = new QToolButton(); m_addMapsToolButton->setDefaultAction(WuQtUtilities::createAction("Add", @@ -243,6 +244,7 @@ UserInputModeVolumeEditWidget::createSelectionToolBar() "First new map will become the top-most overlay."), this, this, SLOT(addMapsActionTriggered()))); + WuQtUtilities::setToolButtonStyleForQt5Mac(m_addMapsToolButton); m_lockAction = WuQtUtilities::createAction("Lock", "Lock/unlock volume file to disallow/allow editing", @@ -251,6 +253,7 @@ UserInputModeVolumeEditWidget::createSelectionToolBar() m_lockAction->setCheckable(true); QToolButton* lockFileToolButton = new QToolButton(); lockFileToolButton->setDefaultAction(m_lockAction); + WuQtUtilities::setToolButtonStyleForQt5Mac(lockFileToolButton); QLabel* editLabel = new QLabel("Edit:"); @@ -260,6 +263,7 @@ UserInputModeVolumeEditWidget::createSelectionToolBar() "Undo the last volume edit", this, this, SLOT(undoActionTriggered()))); + WuQtUtilities::setToolButtonStyleForQt5Mac(undoToolButton); QToolButton* redoToolButton = new QToolButton(); @@ -267,6 +271,7 @@ UserInputModeVolumeEditWidget::createSelectionToolBar() "Redo (or is it undo) the last undo", this, this, SLOT(redoActionTriggered()))); + WuQtUtilities::setToolButtonStyleForQt5Mac(redoToolButton); QToolButton* resetToolButton = new QToolButton(); @@ -274,6 +279,7 @@ UserInputModeVolumeEditWidget::createSelectionToolBar() "Reset all edting of the volume", this, this, SLOT(resetActionTriggered()))); + WuQtUtilities::setToolButtonStyleForQt5Mac(resetToolButton); QLabel* brushSizeLabel = new QLabel("Brush Size:"); const int MIN_BRUSH_SIZE = 1; @@ -313,6 +319,7 @@ UserInputModeVolumeEditWidget::createSelectionToolBar() this, SLOT(labelValueActionTriggered())); m_voxelLabelValueToolButton = new QToolButton(); m_voxelLabelValueToolButton->setDefaultAction(m_voxelLabelValueAction); + WuQtUtilities::setToolButtonStyleForQt5Mac(m_voxelLabelValueToolButton); const int SPACE = 10; QWidget* widget = new QWidget(); @@ -386,6 +393,7 @@ UserInputModeVolumeEditWidget::createModeToolBar() QToolButton* toolButton = new QToolButton(); toolButton->setDefaultAction(action); + WuQtUtilities::setToolButtonStyleForQt5Mac(toolButton); layout->addWidget(toolButton); } diff --git a/src/GuiQt/UserInputTileTabsContextMenu.cxx b/src/GuiQt/UserInputTileTabsContextMenu.cxx new file mode 100644 index 0000000000000000000000000000000000000000..051dfd164c10fbe8a5d5d9cc06459a7b68e78320 --- /dev/null +++ b/src/GuiQt/UserInputTileTabsContextMenu.cxx @@ -0,0 +1,135 @@ + +/*LICENSE_START*/ +/* + * Copyright (C) 2017 Washington University School of Medicine + * + * 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 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ +/*LICENSE_END*/ + +#define __USER_INPUT_TILE_TABS_CONTEXT_MENU_DECLARE__ +#include "UserInputTileTabsContextMenu.h" +#undef __USER_INPUT_TILE_TABS_CONTEXT_MENU_DECLARE__ + +#include "BrainBrowserWindow.h" +#include "BrainOpenGLViewportContent.h" +#include "BrowserTabContent.h" +#include "CaretAssert.h" +#include "EventBrowserTabGet.h" +#include "EventBrowserWindowTileTabOperation.h" +#include "EventManager.h" +#include "GuiManager.h" + +using namespace caret; + + + +/** + * \class caret::UserInputTileTabsContextMenu + * \brief Menu for tile tabs operations in a window context menu + * \ingroup GuiQt + */ + +/** + * Constructor. + * + * @param parentWidget + * Parent widget on which error message dialogs are displayed + * @param viewportContent + * Content of the viewport under the mouse. + */ +UserInputTileTabsContextMenu::UserInputTileTabsContextMenu(QWidget* parentWidget, + BrainOpenGLViewportContent* viewportContent) +: QMenu("Tabs"), +m_parentWidget(parentWidget), +m_windowIndex(viewportContent->getWindowIndex()), +m_tabIndex(viewportContent->getTabIndex()) +{ + BrainBrowserWindow* bbw = GuiManager::get()->getBrowserWindowByWindowIndex(m_windowIndex); + CaretAssert(bbw); + CaretAssert(m_windowIndex >= 0); + CaretAssert(m_tabIndex >= 0); + + EventBrowserTabGet tabContentEvent(m_tabIndex); + EventManager::get()->sendEvent(tabContentEvent.getPointer()); + BrowserTabContent* tabContent = tabContentEvent.getBrowserTab(); + if (tabContent != NULL) { + const AString tabNumberText(AString::number(m_tabIndex + 1)); + + if (bbw->isTileTabsSelected()) { + m_createNewTabBeforeAction = addAction("Create New Tab Before This Tab"); + m_createNewTabAfterAction = addAction("Create New Tab After This Tab"); + m_selectTabAction = addAction("Select This Tab"); + } + + QObject::connect(this, &QMenu::triggered, + this, &UserInputTileTabsContextMenu::actionTriggered); + } +} + +/** + * Destructor. + */ +UserInputTileTabsContextMenu::~UserInputTileTabsContextMenu() +{ +} + +/** + * @return Is this menu valid? + */ +bool +UserInputTileTabsContextMenu::isValid() const +{ + return (actions().size() > 0); +} + + +/** + * Gets called when an action is selected from the menu. + * + * @param action + * Action selected by user. + */ +void +UserInputTileTabsContextMenu::actionTriggered(QAction* action) +{ + EventBrowserWindowTileTabOperation::Operation operation = EventBrowserWindowTileTabOperation::OPERATION_SELECT_TAB; + bool validOperationFlag = false; + + if (action == m_createNewTabBeforeAction) { + operation = EventBrowserWindowTileTabOperation::OPERATION_NEW_TAB_BEFORE; + validOperationFlag = true; + } + else if (action == m_createNewTabAfterAction) { + operation = EventBrowserWindowTileTabOperation::OPERATION_NEW_TAB_AFTER; + validOperationFlag = true; + } + else if (action == m_selectTabAction) { + operation = EventBrowserWindowTileTabOperation::OPERATION_SELECT_TAB; + validOperationFlag = true; + } + else if (action != NULL) { + CaretAssertMessage(0, "Invalid menu action. Has a new menu action been added?"); + } + + if (validOperationFlag) { + EventBrowserWindowTileTabOperation tileTabOperation(operation, + m_parentWidget, + m_windowIndex, + m_tabIndex); + EventManager::get()->sendEvent(tileTabOperation.getPointer()); + } +} + diff --git a/src/GuiQt/UserInputTileTabsContextMenu.h b/src/GuiQt/UserInputTileTabsContextMenu.h new file mode 100644 index 0000000000000000000000000000000000000000..bcb533891a9064d5019ba20d988aca88b6db1d23 --- /dev/null +++ b/src/GuiQt/UserInputTileTabsContextMenu.h @@ -0,0 +1,79 @@ +#ifndef __USER_INPUT_TILE_TABS_CONTEXT_MENU_H__ +#define __USER_INPUT_TILE_TABS_CONTEXT_MENU_H__ + +/*LICENSE_START*/ +/* + * Copyright (C) 2017 Washington University School of Medicine + * + * 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 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ +/*LICENSE_END*/ + + + +#include + +#include + +class QWidget; + +namespace caret { + + class BrainOpenGLViewportContent; + + class UserInputTileTabsContextMenu : public QMenu { + + Q_OBJECT + + public: + UserInputTileTabsContextMenu(QWidget* parentWidget, + BrainOpenGLViewportContent* viewportContent); + + virtual ~UserInputTileTabsContextMenu(); + + bool isValid() const; + + // ADD_NEW_METHODS_HERE + + private slots: + void actionTriggered(QAction* action); + + private: + UserInputTileTabsContextMenu(const UserInputTileTabsContextMenu&); + + UserInputTileTabsContextMenu& operator=(const UserInputTileTabsContextMenu&); + + QWidget* m_parentWidget; + + const int32_t m_windowIndex; + + const int32_t m_tabIndex; + + QAction* m_createNewTabBeforeAction = nullptr; + + QAction* m_createNewTabAfterAction = nullptr; + + QAction* m_selectTabAction = nullptr; + + // ADD_NEW_MEMBERS_HERE + + }; + +#ifdef __USER_INPUT_TILE_TABS_CONTEXT_MENU_DECLARE__ + // +#endif // __USER_INPUT_TILE_TABS_CONTEXT_MENU_DECLARE__ + +} // namespace +#endif //__USER_INPUT_TILE_TABS_CONTEXT_MENU_H__ diff --git a/src/GuiQt/VolumeFileCreateDialog.cxx b/src/GuiQt/VolumeFileCreateDialog.cxx index 7a7815e6577b4c6a4bee46eedbdd9048f5129d9f..c80217177185cec4e72b360b3ead7771bc504f24 100644 --- a/src/GuiQt/VolumeFileCreateDialog.cxx +++ b/src/GuiQt/VolumeFileCreateDialog.cxx @@ -485,7 +485,7 @@ VolumeFileCreateDialog::okButtonClicked() } } m_volumeFile->setValueAllVoxels(defaultValue); - m_volumeFile->updateScalarColoringForAllMaps(GuiManager::get()->getBrain()->getPaletteFile()); + m_volumeFile->updateScalarColoringForAllMaps(); for (int32_t iMap = 0; iMap < numMaps; iMap++) { CaretAssertVectorIndex(m_mapNameLineEdits, iMap); diff --git a/src/GuiQt/VolumeSurfaceOutlineViewController.cxx b/src/GuiQt/VolumeSurfaceOutlineViewController.cxx index fb87225f7b3f01dbc05741840fc85343a791bbbb..cd54c9d8ebc06e2ba794257775956c14a271fd3e 100644 --- a/src/GuiQt/VolumeSurfaceOutlineViewController.cxx +++ b/src/GuiQt/VolumeSurfaceOutlineViewController.cxx @@ -23,7 +23,6 @@ #include #include -#include #include #include #include @@ -38,6 +37,7 @@ #include "SurfaceSelectionViewController.h" #include "VolumeSurfaceOutlineColorOrTabViewController.h" #include "VolumeSurfaceOutlineModel.h" +#include "WuQDoubleSpinBox.h" #include "WuQFactory.h" #include "WuQGridLayoutGroup.h" #include "WuQtUtilities.h" @@ -78,17 +78,13 @@ VolumeSurfaceOutlineViewController::VolumeSurfaceOutlineViewController(const Qt: this->colorOrTabSelectionControl->getWidget()->setToolTip("Select coloring for surface outline.\n" "If tab, coloring assigned to selected surface\n" "in the selected tab is used.\n"); - const float minLineWidth = 0.1; - const float maxLineWidth = 100.0; - const float stepSize = 0.5; - this->thicknessSpinBox = WuQFactory::newDoubleSpinBox(); - this->thicknessSpinBox->setRange(minLineWidth, - maxLineWidth); - this->thicknessSpinBox->setSingleStep(stepSize); - this->thicknessSpinBox->setFixedWidth(100); - QObject::connect(this->thicknessSpinBox, SIGNAL(valueChanged(double)), - this, SLOT(thicknessSpinBoxValueChanged(double))); - this->thicknessSpinBox->setToolTip("Thickness of surface outline"); + this->thicknessSpinBox = new WuQDoubleSpinBox(this); + this->thicknessSpinBox->setRange(0.0, 100.0); + this->thicknessSpinBox->setSingleStep(0.10); + this->thicknessSpinBox->setSuffix("%"); + QObject::connect(this->thicknessSpinBox, static_cast(&WuQDoubleSpinBox::valueChanged), + this, &VolumeSurfaceOutlineViewController::thicknessSpinBoxValueChanged); + this->thicknessSpinBox->setToolTip("Thickness of surface outline as percentage of viewport height"); if (orientation == Qt::Horizontal) { @@ -97,7 +93,7 @@ VolumeSurfaceOutlineViewController::VolumeSurfaceOutlineViewController(const Qt: int row = this->gridLayoutGroup->rowCount(); this->gridLayoutGroup->addWidget(this->enabledCheckBox, row, 0); this->gridLayoutGroup->addWidget(this->colorOrTabSelectionControl->getWidget(), row, 1); - this->gridLayoutGroup->addWidget(this->thicknessSpinBox, row, 2); + this->gridLayoutGroup->addWidget(this->thicknessSpinBox->getWidget(), row, 2); this->gridLayoutGroup->addWidget(this->surfaceSelectionViewController->getWidget(), row, 3); } else { @@ -113,7 +109,7 @@ VolumeSurfaceOutlineViewController::VolumeSurfaceOutlineViewController(const Qt: this->gridLayoutGroup->addWidget(this->surfaceSelectionViewController->getWidget(), row, 1, 1, 2); row++; this->gridLayoutGroup->addWidget(this->colorOrTabSelectionControl->getWidget(), row, 1); - this->gridLayoutGroup->addWidget(this->thicknessSpinBox, row, 2, Qt::AlignLeft); + this->gridLayoutGroup->addWidget(this->thicknessSpinBox->getWidget(), row, 2, Qt::AlignLeft); row++; this->gridLayoutGroup->addWidget(bottomHorizontalLineWidget, row, 0, 1, -1); } @@ -185,7 +181,7 @@ void VolumeSurfaceOutlineViewController::thicknessSpinBoxValueChanged(double value) { if (this->outlineModel != NULL) { - this->outlineModel->setThickness(value); + this->outlineModel->setThicknessPercentageViewportHeight(value); } this->updateGraphics(); } @@ -208,10 +204,14 @@ VolumeSurfaceOutlineViewController::updateViewController(VolumeSurfaceOutlineMod this->enabledCheckBox->setCheckState(state); this->thicknessSpinBox->blockSignals(true); - this->thicknessSpinBox->setValue(outlineModel->getThickness()); + float thickness = outlineModel->getThicknessPercentageViewportHeight(); + if (thickness < 0.0f) { + /* old scenes will have negative for mm thickness */ + thickness = VolumeSurfaceOutlineModel::DEFAULT_LINE_THICKNESS_PERCENTAGE_VIEWPORT_HEIGHT; + } + this->thicknessSpinBox->setValue(thickness); this->thicknessSpinBox->blockSignals(false); this->surfaceSelectionViewController->updateControl(outlineModel->getSurfaceSelectionModel()); - //this->surfaceSelectionViewController->setSurface(outlineModel->getSurface()); this->colorOrTabSelectionControl->updateViewController(outlineModel->getColorOrTabModel()); } } diff --git a/src/GuiQt/VolumeSurfaceOutlineViewController.h b/src/GuiQt/VolumeSurfaceOutlineViewController.h index 30db74f0ae62a350b238b8d169154fe02bb6020f..74cb338850798da06208dab0ad9d492f6912d1f7 100644 --- a/src/GuiQt/VolumeSurfaceOutlineViewController.h +++ b/src/GuiQt/VolumeSurfaceOutlineViewController.h @@ -25,7 +25,6 @@ #include "VolumeSurfaceOutlineColorOrTabModel.h" class QCheckBox; -class QDoubleSpinBox; class QGridLayout; namespace caret { @@ -34,6 +33,7 @@ namespace caret { class SurfaceSelectionViewController; class VolumeSurfaceOutlineModel; class VolumeSurfaceOutlineColorOrTabViewController; + class WuQDoubleSpinBox; class WuQGridLayoutGroup; class VolumeSurfaceOutlineViewController : public QObject { @@ -75,7 +75,7 @@ namespace caret { VolumeSurfaceOutlineColorOrTabViewController* colorOrTabSelectionControl; - QDoubleSpinBox* thicknessSpinBox; + WuQDoubleSpinBox* thicknessSpinBox; SurfaceSelectionViewController* surfaceSelectionViewController; }; diff --git a/src/GuiQt/WuQDataEntryDialog.cxx b/src/GuiQt/WuQDataEntryDialog.cxx index e534a16bfce9503f4688aa5f5172f7f477c173f0..a1b557523d35d99fcb5aa5dc62c70d912dee3820 100644 --- a/src/GuiQt/WuQDataEntryDialog.cxx +++ b/src/GuiQt/WuQDataEntryDialog.cxx @@ -102,6 +102,16 @@ WuQDataEntryDialog::~WuQDataEntryDialog() { } +/** + * Hide the cancel button. + */ +void +WuQDataEntryDialog::hideCancelButton() +{ + setCancelButtonText(""); +} + + /** * Finish construction of the dialog. * @@ -287,7 +297,28 @@ WuQDataEntryDialog::addWidget(const QString& labelText, return widget; } - + +/** + * add widget that spans both the label and widget columns to next available row in the dialog. + */ +QWidget* +WuQDataEntryDialog::addWidget(QWidget* widget) +{ + // + // Keep pointer to widget + // + widgets.push_back(widget); + + // + // add widget to layout + // + const int rowNumber = widgetGridLayout->rowCount(); + widgetGridLayout->addWidget(widget, + rowNumber, 0, 1, 2); + + return widget; +} + /** * add a check box. */ diff --git a/src/GuiQt/WuQDataEntryDialog.h b/src/GuiQt/WuQDataEntryDialog.h index 0b1aaeb6cc854514eb4bd6f661fc9aa414586dec..30a14d2cd1fab6cfbff303220a47283ad481a6a2 100644 --- a/src/GuiQt/WuQDataEntryDialog.h +++ b/src/GuiQt/WuQDataEntryDialog.h @@ -64,6 +64,8 @@ namespace caret { const WuQDialog::ScrollAreaStatus scrollBarStatus, Qt::WindowFlags f = 0); + void hideCancelButton(); + // destructor ~WuQDataEntryDialog(); @@ -71,6 +73,8 @@ namespace caret { QWidget* addWidget(const QString& labelText, QWidget* widget); + QWidget* addWidget(QWidget* widget); + // add widgets to the next available row in the dialog void addWidgetsToNextRow(QWidget* leftColumnWidget, QWidget* rightColumnWidget); diff --git a/src/GuiQt/WuQDialog.cxx b/src/GuiQt/WuQDialog.cxx index df43c2d6144af60ff247378ce195412bc9f594db..34f2fe43925da7373a109fcada29258eeefe41d0 100644 --- a/src/GuiQt/WuQDialog.cxx +++ b/src/GuiQt/WuQDialog.cxx @@ -231,6 +231,29 @@ WuQDialog::setStandardButtonText(QDialogButtonBox::StandardButton button, } } +/** + * Set the enabled status of a standard button. + * If the button is invalid, a sever message is logged. + * + * @param button + * Standard button enum identifying button. + * @param enabled + * New enabled status for button. + */ +void +WuQDialog::setStandardButtonEnabled(QDialogButtonBox::StandardButton button, + const bool enabled) +{ + QPushButton* pushButton = this->buttonBox->button(button); + if (pushButton != NULL) { + pushButton->setEnabled(enabled); + } + else { + CaretLogSevere("PROGRAM ERROR: Attempted to set enabled status for an invalid Dialog Standard Button, id=" + + AString::number((int)button)); + } +} + /** * called to capture image after timeout so nothing obscures window. */ @@ -322,7 +345,7 @@ WuQDialog::keyPressEvent(QKeyEvent* e) // Calls reject() if Escape is pressed. Simulates a button // click for the default button if Enter is pressed. Move focus // for the arrow keys. Ignore the rest. -#ifdef Q_WS_MAC +#ifdef Q_OS_MAC if(e->modifiers() == Qt::ControlModifier && e->key() == Qt::Key_Period) { } else #endif @@ -549,8 +572,16 @@ WuQDialog::helpButtonClicked() * The focus event. */ void -WuQDialog::focusInEvent(QFocusEvent* /*event*/) +WuQDialog::focusInEvent(QFocusEvent* event) { + if (event->reason() == Qt::PopupFocusReason) { + /* + * This occurs when a combo box is popped up Mac, + * causes a problem, and can be ignored. + */ + return; + } + focusGained(); } diff --git a/src/GuiQt/WuQDialog.h b/src/GuiQt/WuQDialog.h index e4552c873215807733a1f9fbe83cdd11400802f8..ed96020465a062e46e1f042dc2a903548ca4e4df 100644 --- a/src/GuiQt/WuQDialog.h +++ b/src/GuiQt/WuQDialog.h @@ -83,6 +83,9 @@ namespace caret { void setStandardButtonText(QDialogButtonBox::StandardButton button, const AString& text); + void setStandardButtonEnabled(QDialogButtonBox::StandardButton button, + const bool enabled); + QPushButton* addUserPushButton(const AString& text, const QDialogButtonBox::ButtonRole buttonRole); diff --git a/src/GuiQt/WuQDialogModal.cxx b/src/GuiQt/WuQDialogModal.cxx index 2347462892a8e60dd91a3544977046888efeaac0..855ee3bad7191d528b3f6f5ae37689c0cc035b2b 100644 --- a/src/GuiQt/WuQDialogModal.cxx +++ b/src/GuiQt/WuQDialogModal.cxx @@ -130,6 +130,19 @@ WuQDialogModal::setOkButtonText(const AString& text) */ } +/** + * Set the enabled status for the OK button. + * + * @param enabled + * New enabled status for the button. + */ +void +WuQDialogModal::setOkButtonEnabled(const bool enabled) +{ + this->setStandardButtonEnabled(QDialogButtonBox::Ok, + enabled); +} + /** * Set the Cancel button to the given text. If the text * is zero length, the Cancel button is removed. diff --git a/src/GuiQt/WuQDialogModal.h b/src/GuiQt/WuQDialogModal.h index 91d90f92ed77fe4f1837b8a052e5545f959fbe85..a55e25c4c2f27459f6a139328deb4538b6223c8a 100644 --- a/src/GuiQt/WuQDialogModal.h +++ b/src/GuiQt/WuQDialogModal.h @@ -43,6 +43,8 @@ namespace caret { void setOkButtonText(const AString& text); + void setOkButtonEnabled(const bool enabled); + void setCancelButtonText(const AString& text); void setSaveWindowPositionForNextTime(const AString& savePositionName = ""); diff --git a/src/GuiQt/WuQDialogNonModal.cxx b/src/GuiQt/WuQDialogNonModal.cxx index 4887ebe3aec15bacd8166d372cf22260bcf5a91a..d86fc2768be86322d49750e96a7a657553ec4a8b 100644 --- a/src/GuiQt/WuQDialogNonModal.cxx +++ b/src/GuiQt/WuQDialogNonModal.cxx @@ -79,6 +79,39 @@ WuQDialogNonModal::~WuQDialogNonModal() } +/** + * Show the dialog. + * + * There are a number of methods for 'showing' a Qt dialog. + * Some of them will show the dialog for the first time. + * If the dialog is 'behind' the main window, not all + * of the methods put the dialog in front of the window. + * This methods uses several of the Qt methods to ensure + * that the dialog is moved in front of a main window. + */ +void +WuQDialogNonModal::showDialog() +{ + this->setVisible(true); + this->show(); + this->activateWindow(); + this->raise(); +} + + +/** + * May be called requesting the dialog to update its content + * Subclasses should override. + */ +void +WuQDialogNonModal::updateDialog() +{ +#ifdef DEBUG + QString text(getObjectName() + " should override updateDialog"); + CaretLogWarning(text); +#endif +} + /** * Gets called when the dialog is closing. * Overriden so that position of dialog diff --git a/src/GuiQt/WuQDialogNonModal.h b/src/GuiQt/WuQDialogNonModal.h index c380ccfaa704505749c30722a55b50b2de12bf24..247f963e08b09463cfeb4ed2127ed987713a45ff 100644 --- a/src/GuiQt/WuQDialogNonModal.h +++ b/src/GuiQt/WuQDialogNonModal.h @@ -39,8 +39,10 @@ namespace caret { void setCloseButtonText(const AString& text); - /** May be called requesting the dialog to update its content */ - virtual void updateDialog() = 0; + void showDialog(); + + virtual void updateDialog(); + //virtual void updateDialog() = 0; void setSaveWindowPositionForNextTime(const bool saveIt); diff --git a/src/GuiQt/WuQDoubleSlider.cxx b/src/GuiQt/WuQDoubleSlider.cxx index 58111a1d1a71ffbf775dbb7c0dfa988270e10d9b..43f9adff9364ed501fafa65d66cc927b027a6e46 100644 --- a/src/GuiQt/WuQDoubleSlider.cxx +++ b/src/GuiQt/WuQDoubleSlider.cxx @@ -114,6 +114,25 @@ WuQDoubleSlider::setRange(double minValue, double maxValue) this->updateSlider(); } +/** + * @return The minimum value of the slider. + */ +double +WuQDoubleSlider::minimumValue() const +{ + return this->minimum;; +} + +/** + * @return The maximum value of the slider. + */ +double +WuQDoubleSlider::maximumValue() const +{ + return this->maximum;; +} + + /** * @return The value of the slider. */ @@ -158,7 +177,7 @@ WuQDoubleSlider::updateSlider() const double parametricValue = (this->sliderValue - this->minimum) / dRange; const int dSlider = this->slider->maximum() - this->slider->minimum(); - const int qSliderValue = static_cast(dSlider * parametricValue) + this->minimum; + const int qSliderValue = static_cast(dSlider * parametricValue);;// + this->minimum; this->slider->blockSignals(true); this->slider->setValue(qSliderValue); this->slider->blockSignals(false); diff --git a/src/GuiQt/WuQDoubleSlider.h b/src/GuiQt/WuQDoubleSlider.h index 1ceffccfb551dfeb5297c5848be063f92473a19c..a4fb0ec6e380b928e5a706f577e19c34fbc7915d 100644 --- a/src/GuiQt/WuQDoubleSlider.h +++ b/src/GuiQt/WuQDoubleSlider.h @@ -41,6 +41,10 @@ namespace caret { void setRange(double minValue, double maxValue); + double minimumValue() const; + + double maximumValue() const; + double value() const; signals: diff --git a/src/GuiQt/WuQDoubleSpinBox.cxx b/src/GuiQt/WuQDoubleSpinBox.cxx new file mode 100644 index 0000000000000000000000000000000000000000..8b8f390222160ecd0c0146c0a363eda16884ac65 --- /dev/null +++ b/src/GuiQt/WuQDoubleSpinBox.cxx @@ -0,0 +1,811 @@ + +/*LICENSE_START*/ +/* + * Copyright (C) 2017 Washington University School of Medicine + * + * 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 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ +/*LICENSE_END*/ + +#define __WU_Q_DOUBLE_SPIN_BOX_DECLARE__ +#include "WuQDoubleSpinBox.h" +#undef __WU_Q_DOUBLE_SPIN_BOX_DECLARE__ + +#include +#include + +#include + +#include "AString.h" +#include "CaretAssert.h" +#include "NumericTextFormatting.h" + +using namespace caret; + +/** + * \class caret::WuQDoubleSpinBox + * \brief Customizable double spin box + * \ingroup GuiQt + * + * An 'extension' of QDoubleSpinBox. There are methods in + * QDoubleSpinBox that we would like to override but the methods + * are not virtual. So, extend WuQWidget (which extends QObject), + * encapsulate QDoubleSpinbox, and implement needed methods. + * + * Added features are: + * (1) Ability to automatically compute digits right of decimal + * based upon the range of the data. + * + * (2) Ablity to set increment/decrement values as a percentage + * of the range of data. + * + * (3) Ability to set an "exceedable" range. A QDoubleSpinBox + * will NOT emit a 'valueChanged' signal if the user tries to + * exceed the minimum or maximum value. 'setRangeExceedable()' + * will allow the minimum and maximum values to be exceeded + * but any auto formatting (decimals, step size) is based + * only on the minimum and maximum values specified. + * + * (4) if 'QDoubleSpinBox::setValue()' is called, it will + * emit the 'valueChanged()' signal which can be problematic + * when updating the value in the spin box. This implementation + * DOES NOT emit the 'valueChanged()' signal when 'setValue()' + * is called. + */ + +/** + * Constructs a spin box with 0.0 as minimum value and 99.99 as maximum value, + * a step value of 1.0 and a precision of 2 decimal places. + * The value is initially set to 0.00. The spin box has the given parent. + * + * @param parent + * The parent widget. + */ +WuQDoubleSpinBox::WuQDoubleSpinBox(QObject* parent) +: WuQWidget(parent) +{ + /* + * Create the spin box and initialize it + * Note that most members are initialized in the header file + */ + m_spinBox = new QDoubleSpinBox(); + m_spinBox->setRange(m_minimumValue, m_maximumValue); + m_spinBox->setSingleStep(1.0); + m_spinBox->setDecimals(2); + m_spinBox->setValue(0.0); + + QObject::connect(m_spinBox, static_cast(&QDoubleSpinBox::valueChanged), + this, &WuQDoubleSpinBox::valueChangedPrivate); +} + +/** + * Destructor. + */ +WuQDoubleSpinBox::~WuQDoubleSpinBox() +{ +} + +/** + * @return The encapsulated widget. Usually used to + * add the widget to a layout. + */ +QWidget* +WuQDoubleSpinBox::getWidget() +{ + return m_spinBox; +} + +///** +// * Copy all setting from the given spin box +// * +// * @param copyFromSpinBox +// * Spinbox from which settings are copied to 'this' instance +// */ +//void +//WuQDoubleSpinBox::copySettings(const WuQDoubleSpinBox* copyFromSpinBox) +//{ +// CaretAssert(copyFromSpinBox); +// +// switch (m_rangeMode) { +// case RangeMode::EXCEEDABLE: +// setRangeExceedable(m_minimumValue, m_maximumValue, m_exceedRangeMultiplier); +// break; +// case RangeMode::INCLUSIVE: +// setRange(m_minimumValue, m_maximumValue); +// } +// m_decimalsMode = copyFromSpinBox->m_decimalsMode; +// switch (copyFromSpinBox->m_decimalsMode) { +// case DecimalsMode::AUTO: +// setDecimalsModeAuto(); +// break; +// case DecimalsMode::FIXED: +// setDecimals(copyFromSpinBox->decimals()); +// break; +// } +// +// setPrefix(copyFromSpinBox->prefix()); +// setSuffix(copyFromSpinBox->suffix()); +// +// setSingleStep(copyFromSpinBox->singleStep()); +// m_singleStepPercentage = copyFromSpinBox->m_singleStepPercentage; +// m_singleStepMode = copyFromSpinBox->m_singleStepMode; +// switch (m_singleStepMode) { +// case SingleStepMode::FIXED: +// break; +// case SingleStepMode::PERCENTAGE: +// setSingleStepPercentage(copyFromSpinBox->singleStepPercentage()); +// break; +// } +//} + + +///** +// * Explicit conversion to QWidget +// */ +//WuQDoubleSpinBox::operator +//QWidget*() const +//{ +// return m_spinBox; +//} + +/** + * @return The decimals mode. + */ +WuQDoubleSpinBox::DecimalsMode +WuQDoubleSpinBox::decimalsMode() const +{ + return m_decimalsMode; +} + +/** + * Set the decimals mode to auto. To exit auto use setDecimals(int). + */ +void +WuQDoubleSpinBox::setDecimalsModeAuto() +{ + m_decimalsMode = DecimalsMode::AUTO; + updateDecimalsForAutoMode(); +} + +/** + * @return Digits right of decimal point + */ +int +WuQDoubleSpinBox::decimals() const +{ + return m_spinBox->decimals(); +} + +/** + * Sets fixed number of decimals the spinbox will use for displaying and interpreting doubles. + * + * Also sets the DecimalMode to a fixed number of digits right of the decimal. + * + * @param prec + * Number of digits right of decimal + */ +void +WuQDoubleSpinBox::setDecimals(int prec) +{ + m_decimalsMode = DecimalsMode::FIXED; + m_spinBox->setDecimals(prec); +} + +/** + * Compute the number of digits right of the decimal point for + * the given range of data. + * + * @param range + * Range of the data. + * @return + * Number of digits right of decimal point. + */ +int32_t +WuQDoubleSpinBox::computeDigitsRightOfDecimal(const double dataRange) +{ + const double range = ((dataRange >=0) ? dataRange : -dataRange); + + int32_t numDecimals = 3; + if (range > 0.0) { + int32_t logValue = static_cast(std::log10(range)); + + if (logValue == 0) { + numDecimals = 3; + } + else if (logValue > 0) { // range > 1 + numDecimals = 4 - logValue; + } + else if (logValue < 0) { // 0 < range < 1 + numDecimals = 3 - logValue; + } + + if (numDecimals < 1) { + numDecimals = 1; + } + else if (numDecimals > 15) { + numDecimals = 15; + } + } + + return numDecimals; +} + +/** + * Compute a reasonable 'exceed range' which is used + * for setting the acceptable values in the spin box + * to the range of data plus a reasonable amount + * above and below. + * + * @param minValue + * The minimum data value. + * @param maxValue + * The maximum data value. + * @param + * Exceed amount for increased range. + */ +double +WuQDoubleSpinBox::computeExceedRange(const double minValue, + const double maxValue) +{ + double exceedRange = 10.0; + + const double range = maxValue - minValue; + if (range >= 1.0) { + const double logVal = std::log10(range); + const double exponent = logVal + 2; + exceedRange = std::pow(10.0, exponent); + } + + return exceedRange; +} + +/** + * Testing of the exceed ragne. + */ +void +WuQDoubleSpinBox::testExceedRange() +{ + { + const double value = 0.0; + const double exceedRange = computeExceedRange(0.0, value); + AString s = QString("Range: %1 execeedRange: %2").arg(value, 10).arg(exceedRange); + std::cout << s << std::endl; + } + + for (int32_t exponent = -10; exponent <= 10; exponent++) { + const double value = std::pow(10.0, exponent); + const double exceedRange = computeExceedRange(0.0, value); + AString s = QString("Range: %1 exceedRange: %2 lo10: %3").arg(value, 10).arg(exceedRange).arg((int)std::log10(value)); + std::cout << s << std::endl; + } + std::cout << std::endl << std::endl; +} + + +/** + * Used for testing computation of digits right of decimal point. + */ +void +WuQDoubleSpinBox::testDigitsRightOfDecimal() +{ + { + const double value = 0.0; + const int32_t decimals = computeDigitsRightOfDecimal(value); + AString s = QString("Range: %1 decimals: %2").arg(value, 10).arg(decimals, 5); + std::cout << s << std::endl; + } + + for (int32_t exponent = -10; exponent <= 10; exponent++) { + const double value = std::pow(10.0, exponent); + const int32_t decimals = computeDigitsRightOfDecimal(value); + AString s = QString("Range: %1 decimals: %2 lo10: %3").arg(value, 10).arg(decimals, 5).arg((int)std::log10(value)); + std::cout << s << std::endl; + } + std::cout << std::endl << std::endl; +} + +/** + * Update the decimals for AUTO mode + */ +void +WuQDoubleSpinBox::updateDecimalsForAutoMode() +{ + static bool decimalsTestFlag = false; + if (decimalsTestFlag) { + testDigitsRightOfDecimal(); + testExceedRange(); + decimalsTestFlag = false; + } + + switch (m_decimalsMode) { + case DecimalsMode::AUTO: + m_spinBox->setDecimals(computeDigitsRightOfDecimal(m_maximumValue + - m_minimumValue)); + break; + case DecimalsMode::FIXED: + break; + } +} + +/** + * @return Minimum DISPLAYED value. + */ +double +WuQDoubleSpinBox::minimum() const +{ + return m_spinBox->minimum(); +} + +/** + * @return Maximum DISPLAYED value. + */ +double +WuQDoubleSpinBox::maximum() const +{ + return m_spinBox->maximum(); +} + +/** + * @return Prefix prepended to start of displayed value. + */ +QString +WuQDoubleSpinBox::prefix() const +{ + return m_spinBox->prefix(); +} + +/** + * Set the prefix is prepended to the start of the displayed value. + * Typical use is to display a unit of measurement or a currency symbol. + * + * @param prefix + * New prefix + */ +void +WuQDoubleSpinBox::setPrefix(const QString &prefix) +{ + m_spinBox->setPrefix(prefix); +} + +/** + * Set the minimum and maximum values for this spin box. + * + * @param minimum + * New minimum value. + * @param maximum + * New maximum value. + */ +void +WuQDoubleSpinBox::setRange(double minimum, + double maximum) +{ + m_minimumValue = minimum; + m_maximumValue = maximum; + m_rangeMode = RangeMode::INCLUSIVE; + m_spinBox->setRange(m_minimumValue, m_maximumValue); + updateDecimalsForAutoMode(); + updateSingleStepPercentage(); + setDataRangeToolTip(); +} + +/** + * Set range with an automatically calculated 'exceedAmount', typically + * 100 times the range and a power of 10. + * + * Any automatic formatting or decimals excludes the "exceed amount". + * + * @param minimum + * New minimum value. + * @param maximum + * New maximum value. + */ +void +WuQDoubleSpinBox::setRangeExceedable(double minimum, + double maximum) +{ + const double exceedAmount = computeExceedRange(minimum, maximum); + const double exceedMin = makePowerOfTen(minimum - exceedAmount); + const double exceedMax = makePowerOfTen(maximum + exceedAmount); + + setRangeExceedable(minimum, + maximum, + exceedMin, + exceedMax); +} + +/** + * Set the range of the data and the spin box. + + * All formating is based upon the data minimum and maximum. + * + * @param dataMinimum + * Data minimum value. + * @param dataMaximum + * Data maximum value. + * @param spinBoxMinimum + * Spinbox minimum value. + * @param spinBoxMaximum + * Spinbox maximum value. + */ +void +WuQDoubleSpinBox::setRangeExceedable(const double dataMinimum, + const double dataMaximum, + const double spinBoxMinimum, + const double spinBoxMaximum) +{ + m_minimumValue = dataMinimum; + m_maximumValue = dataMaximum; + m_rangeMode = RangeMode::EXCEEDABLE; + + m_spinBox->setRange(spinBoxMinimum, + spinBoxMaximum); + + updateDecimalsForAutoMode(); + updateSingleStepPercentage(); + setDataRangeToolTip(); +} + +/** + * Increase the value to the next power of ten. + * Example: 8500 becomes 10000 + * + * @param value + * The value. + * @return + * Value increased to next power of ten. + */ +double +WuQDoubleSpinBox::makePowerOfTen(const double value) const +{ + const bool negativeFlag = (value < 0.0); + double valuePositive = std::fabs(value); + const int64_t logVal = static_cast(std::log10(valuePositive) + 1); + double powerTenValue = std::pow(10.0, logVal); + if (negativeFlag) { + powerTenValue = -powerTenValue; + } + return powerTenValue; +} + + +/** + * Set range with a specified 'exceedAmount'. + * Selected value is allowed to exceed minimum and maximum by exceedAmount. + * Any automatic formatting or decimals excludes the "exceed amount". + * + * @param minimum + * New minimum value. + * @param maximum + * New maximum value. + * @param exceedAmount + * The fixed exceed amount. + */ +void +WuQDoubleSpinBox::setRangeExceedable(double minimum, + double maximum, + double exceedAmount) +{ + setRangeExceedable(minimum, + maximum, + minimum - exceedAmount, + maximum + exceedAmount); +} + +/** + * Set range for percentage values. Percentage range + * [0.0, 100], with 2 digits right of decimal, + * a single step of 0.1, and a '%' suffix. + * + * @param minimumPercentage + * Minimum percentage (must be in range [0.0, 100.0] + * @param maximumPercentage + * Maximum percentage (must be in range [0.0, 100.0] + */ +void +WuQDoubleSpinBox::setRangePercentage(const double minimumPercentage, + const double maximumPercentage) +{ + setRange(minimumPercentage, + maximumPercentage); + setDecimals(2); + setSingleStepPercentage(0.1); + setSuffix("%"); + setDataRangeToolTip(); +} + +/** + * Set the step value. When the user uses the arrows to change the + * spin box's value the value will be incremented/decremented by the + * amount of the given value. + * + * NOTE: When this method is called, the SingleStepMode is set + * to FIXED + * + * @param value + * New step value. + */ +void +WuQDoubleSpinBox::setSingleStep(double value) +{ + CaretAssert(value >= 0.0); + + m_spinBox->setSingleStep(value); + m_singleStepMode = SingleStepMode::FIXED; +} + +/** + * Set the step value. When the user uses the arrows to change the + * spin box's value the value will be incremented/decremented by a + * percentage of the minimum and maximum values. + * + * NOTE: When this method is called, the SingleStepMode is set + * to PERCENTAGE. + * + * @param percentage + * The step percentage that ranges (0.0, 100.0]. + */ +void +WuQDoubleSpinBox::setSingleStepPercentage(double percentage) +{ + CaretAssert(percentage > 0.0); + CaretAssert(percentage <= 100.0); + + m_singleStepPercentage = percentage; + m_singleStepMode = SingleStepMode::PERCENTAGE; + + updateSingleStepPercentage(); +} + +/** + * Update the single step percentage when the minimum value, + * maximum value, or step percentage is changed. + */ +void +WuQDoubleSpinBox::updateSingleStepPercentage() +{ + switch (m_singleStepMode) { + case SingleStepMode::FIXED: + break; + case SingleStepMode::PERCENTAGE: + { + const double minValue = m_minimumValue; + const double maxValue = m_maximumValue; + const double dataRange = maxValue - minValue; + + double stepValue = 0.0; + if (dataRange > 0.0) { + stepValue = dataRange * (m_singleStepPercentage / 100.0); + } + + m_spinBox->setSingleStep(stepValue); + } + break; + } +} + +/** + * Set the suffix is append to the start of the displayed value. + * Typical use is to display a unit of measurement or a currency symbol. + * + * @param suffix + * New suffix + */ +void +WuQDoubleSpinBox::setSuffix(const QString &suffix) +{ + m_spinBox->setSuffix(suffix); +} + +/** + * @return The single step value. + */ +double +WuQDoubleSpinBox::singleStep() const +{ + return m_spinBox->singleStep(); +} + +/** + * @return The single step mode. + */ +WuQDoubleSpinBox::SingleStepMode +WuQDoubleSpinBox::singleStepMode() const +{ + return m_singleStepMode; +} + +/** + * @return The single step percentage value. + */ +double +WuQDoubleSpinBox::singleStepPercentage() const +{ + return m_singleStepPercentage; +} + +/** + * @return The suffix. + */ +QString +WuQDoubleSpinBox::suffix() const +{ + return m_spinBox->suffix(); +} + +/** + * This virtual function is used by the spin box whenever it needs to + * display the given value. The default implementation returns a + * string containing value printed using QWidget::locale().toString(value, QLatin1Char('f'), + * decimals()) and will remove the thousand separator unless setGroupSeparatorShown() is set. + * Reimplementations may return anything. + * + * @return Text representation of current value. + */ +QString +WuQDoubleSpinBox::textFromValue(double value) const +{ + return m_spinBox->textFromValue(value); +} + +/** + * @return Current value as double. + */ +double +WuQDoubleSpinBox::value() const +{ + return m_spinBox->value(); +} + +/** + * This virtual function is used by the spin box whenever it needs to + * interpret text entered by the user as a value. + * Subclasses that need to display spin box values in a non-numeric way + * need to reimplement this function. + * + * @return double value from the given text value. + */ +double +WuQDoubleSpinBox::valueFromText(const QString &text) const +{ + return m_spinBox->valueFromText(text); +} + + +/** + * Slot to set the value. Unlike QDoubleSpinBox::setValue() + * this method DOES NOT cause the valueChanged() signal to + * be emitted. + * + * @param val + * New value for spin box. + */ +void WuQDoubleSpinBox::setValue(double val) +{ + if (m_blockValueUpdateFlag) { + return; + } + + QSignalBlocker blocker(m_spinBox); + m_spinBox->setValue(val); +} + +/** + * Called when the enapsulated spin box's value changed signal is emitted. + * Use 'this' instance of signal blocking to prevent a signal from + * being emitted. This allows user to use WuQDoubleSpinBox::blockSignals(). + * + * @param d + * Value contained in the spin box. + */ +void +WuQDoubleSpinBox::valueChangedPrivate(double d) +{ + if (m_spinBox->signalsBlocked()) { + return; + } + if (signalsBlocked()) { + return; + } + + /* + * Emitting the signal may cause a call to the 'setValue()' method. + * We need to prevent this. Otherwise, the spin box will get updated + * with only the first character that was typed by user. + * For example, without this, if the user tried to enter 9.3, + * setValue gets called with only 9 and the user cannot enter the .3. + */ + m_blockValueUpdateFlag = true; + emit valueChanged(d); + m_blockValueUpdateFlag = false; +} + +/** + * Set the tooltip for this spin box. After this + * tooltip, the range of data is displayed. + * + * @param tooltip + * Text for the tooltip. + */ +void +WuQDoubleSpinBox::setToolTip(const QString& tooltip) +{ + m_userToolTip = tooltip; + setDataRangeToolTip(); +} + +/** + * @return Text for a tooltip indicating range of data. + */ +void +WuQDoubleSpinBox::setDataRangeToolTip() const +{ + + AString s; + + if ( ! m_userToolTip.isEmpty()) { + s += (m_userToolTip + "\n"); + } + + s += QString("Data min/max: %1 %2").arg(doubleToString(m_minimumValue)).arg(doubleToString(m_maximumValue)); + + + switch (m_rangeMode) { + case RangeMode::EXCEEDABLE: + { + const double minVal = m_spinBox->minimum(); + const double maxVal = m_spinBox->maximum(); + s += (+ "\n" + + QString("Acceptable min/max: %1 %2").arg(doubleToString(minVal)).arg(doubleToString(maxVal))); + } break; + case RangeMode::INCLUSIVE: + break; + } + + m_spinBox->setToolTip(s); +} + +/** + * Convert the double to a string. If the value + * exceeds maximum float list as 'infinity'. + * + * @param value + * The double value. + * @return + * The string representation. + */ +QString +WuQDoubleSpinBox::doubleToString(const double value) const +{ + QString s; + + if (value >= std::numeric_limits::max()) { + s = "+infinity"; + } + else if (value <= -std::numeric_limits::max()) { + s = "-infinity"; + } + else { + s = QString("%1").arg(value, 0, 'f'); + s = NumericTextFormatting::cleanZerosInValueText(s); + s += m_spinBox->suffix(); + } + + return s; +} + + + +// ADD_NEW_METHODS_HERE + diff --git a/src/GuiQt/WuQDoubleSpinBox.h b/src/GuiQt/WuQDoubleSpinBox.h new file mode 100644 index 0000000000000000000000000000000000000000..53d752875a111aa814a0fe1ec6de9f109b5c680d --- /dev/null +++ b/src/GuiQt/WuQDoubleSpinBox.h @@ -0,0 +1,220 @@ +#ifndef __WU_Q_DOUBLE_SPIN_BOX_H__ +#define __WU_Q_DOUBLE_SPIN_BOX_H__ + +/*LICENSE_START*/ +/* + * Copyright (C) 2017 Washington University School of Medicine + * + * 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 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ +/*LICENSE_END*/ + + + +#include + +#include "WuQWidget.h" + +class QDoubleSpinBox; + +namespace caret { + + class WuQDoubleSpinBox : public WuQWidget { + + Q_OBJECT + + public: + /** + * Mode for decimals (digits right of decimal) + */ + enum class DecimalsMode { + /** + * Number of decimals is calculated from the range of data + */ + AUTO, + /** + * Number of decimals is fixed by calling setDecimals(int) + * (Normal spin box functionality) + */ + FIXED + }; + + /** + * Mode for single step (increment/decrement) arrows + */ + enum class SingleStepMode { + /** + * Step by a fixed amount (normal spin box functionality) + */ + FIXED, + /** + * Step by a percentage of data range + */ + PERCENTAGE + }; + + /** + * Mode for data range + */ + enum class RangeMode { + /** + * Normal spin box behavior, values limited inclusively to minimum and maximum + */ + INCLUSIVE, + /** + * May exceed minimum and maximum values. + * Formatted for specified minimum and maximum. + */ + EXCEEDABLE + }; + + WuQDoubleSpinBox(QObject* parent); + + virtual ~WuQDoubleSpinBox(); + + virtual QWidget* getWidget(); + + //void copySettings(const WuQDoubleSpinBox* copyFromSpinBox); + + QString cleanText() const; + + int decimals() const; + + double maximum() const; + + double minimum() const; + + QString prefix() const; + + DecimalsMode decimalsMode() const; + + void setDecimals(int prec); + + void setDecimalsModeAuto(); + + void setPrefix(const QString &prefix); + + void setRange(double minimum, double maximum); + + void setRangeExceedable(double minimum, + double maximum, + double exceedAmount); + + void setRangeExceedable(double minimum, + double maximum); + + void setRangePercentage(const double minimumPercentage, + const double maximumPercentage); + + void setSingleStep(double value); + + void setSingleStepPercentage(double percentage); + + void setSuffix(const QString &suffix); + + double singleStep() const; + + SingleStepMode singleStepMode() const; + + double singleStepPercentage() const; + + QString suffix() const; + + virtual QString textFromValue(double value) const; + + double value() const; + + virtual double valueFromText(const QString &text) const; + + virtual void setToolTip(const QString& tooltip); + + // ADD_NEW_METHODS_HERE + + public slots: + void setValue(double val); + + signals: + /** + * This signal is emitted when the value changes. + * + * Example of signal connection: + * QObject::connect(m_spinBox, static_cast(&QDoubleSpinBox::valueChanged), + * this, &WuQDoubleSpinBox::valueChanged); + * + * @param d + * New value. + */ + void valueChanged(double d); + + private slots: + void valueChangedPrivate(double d); + + private: + WuQDoubleSpinBox(const WuQDoubleSpinBox&); + + WuQDoubleSpinBox& operator=(const WuQDoubleSpinBox&); + + void updateSingleStepPercentage(); + + void updateDecimalsForAutoMode(); + + int32_t computeDigitsRightOfDecimal(const double dataRange); + + void testDigitsRightOfDecimal(); + + double computeExceedRange(const double minValue, + const double maxValue); + + QString doubleToString(const double value) const; + + void testExceedRange(); + + void setRangeExceedable(const double dataMinimum, + const double dataMaximum, + const double spinBoxMinimum, + const double spinBoxMaximum); + + double makePowerOfTen(const double value) const; + + void setDataRangeToolTip() const; + + QDoubleSpinBox* m_spinBox; + + RangeMode m_rangeMode = RangeMode::INCLUSIVE; + + DecimalsMode m_decimalsMode = DecimalsMode::FIXED; + + SingleStepMode m_singleStepMode = SingleStepMode::FIXED; + + double m_singleStepPercentage = 1.0; + + double m_minimumValue = 0.0; + + double m_maximumValue = 99.0; + + bool m_blockValueUpdateFlag = false; + + QString m_userToolTip; + + // ADD_NEW_MEMBERS_HERE + + }; + +#ifdef __WU_Q_DOUBLE_SPIN_BOX_DECLARE__ + // +#endif // __WU_Q_DOUBLE_SPIN_BOX_DECLARE__ + +} // namespace +#endif //__WU_Q_DOUBLE_SPIN_BOX_H__ diff --git a/src/GuiQt/WuQEventBlockingFilter.cxx b/src/GuiQt/WuQEventBlockingFilter.cxx index 6745bb8cdab1589cb1a82a0938e8dd43abfe708c..2a1dcfe1d618c94a5f572be31ab59a6854a3effe 100644 --- a/src/GuiQt/WuQEventBlockingFilter.cxx +++ b/src/GuiQt/WuQEventBlockingFilter.cxx @@ -69,10 +69,10 @@ WuQEventBlockingFilter::WuQEventBlockingFilter(QObject* parent) * @param comboBox * Combo box that has its wheel event blocked. */ +#ifdef CARET_OS_MACOSX void WuQEventBlockingFilter::blockMouseWheelEventInMacComboBox(QComboBox* comboBox) { -#ifdef CARET_OS_MACOSX /* * Attach an event filter that blocks wheel events in the combo box if Mac */ @@ -80,8 +80,10 @@ WuQEventBlockingFilter::blockMouseWheelEventInMacComboBox(QComboBox* comboBox) comboBoxWheelEventBlockingFilter->setEventBlocked(QEvent::Wheel, true); comboBox->installEventFilter(comboBoxWheelEventBlockingFilter); -#endif // CARET_OS_MACOSX } +#else + void WuQEventBlockingFilter::blockMouseWheelEventInMacComboBox(QComboBox*) { } +#endif // CARET_OS_MACOSX /** * Destructor. diff --git a/src/GuiQt/WuQMessageBox.cxx b/src/GuiQt/WuQMessageBox.cxx index 62881afa0af600d2905c1a7bca2f6593118bbf67..f86a06cfb72bf50b32b61c9cc12f3536e78b22d0 100644 --- a/src/GuiQt/WuQMessageBox.cxx +++ b/src/GuiQt/WuQMessageBox.cxx @@ -19,9 +19,14 @@ */ /*LICENSE_END*/ +#include + #include "CaretAssert.h" +#include "CaretLogger.h" +#define __WU_QMESSAGE_DEFINE__ #include "WuQMessageBox.h" +#undef __WU_QMESSAGE_DEFINE__ using namespace caret; @@ -270,6 +275,79 @@ WuQMessageBox::warningOk(QWidget* parent, msgBox.exec(); } +/** + * Display an error message box with the + * given text and an OK button and a checkbox with + * the text "Remember my choice and do not show this dialog". + * + * If the user closes the dialog with the "Do not show + * again" box checked, the next time this function is + * called with the dialog will not be displayed. + * + * @param parent + * Parent on which message box is displayed. + * @param uniqueIdentifier + * Unique identifier used for associating of + * "do not show" status + * @param text + * Message that is displayed. + */ +void +WuQMessageBox::warningOkWithDoNotShowAgain(QWidget* parent, + const QString& uniqueIdentifier, + const QString& text) +{ + /* + * See if user previously selected "do not show again". + * If so, do not display dialog and return. + */ + auto doNotShowStatus = s_doNotShowAgainButtonSelection.find(uniqueIdentifier); + if (doNotShowStatus != s_doNotShowAgainButtonSelection.end()) { + return; + } + + QMessageBox msgBox(parent); + msgBox.setIcon(QMessageBox::Warning); + msgBox.setWindowTitle(""); + msgBox.setText(text); + msgBox.addButton(QMessageBox::Ok); + msgBox.setDefaultButton(QMessageBox::Ok); + msgBox.setEscapeButton(QMessageBox::Ok); + + QCheckBox* checkBox = addDoNotShowAgainCheckBox(msgBox); + + msgBox.exec(); + + /* + * If the "do not show again" checkbox was checked, + * save to do not show status. + */ + const bool doNotShowAgainFlag = checkBox->isChecked(); + if (doNotShowAgainFlag) { + s_doNotShowAgainButtonSelection.insert(std::make_pair(uniqueIdentifier, + QMessageBox::NoButton)); + } +} + +/** + * Add a "do not show again" checkbox to the given message checkbox. + * + * @param messageBox + * Message box to which "do not show again" checkbox is added. + * @return + * Pointer to the checkbox (do not destroy as dialog closing + * will handle checkbox destruction). + */ +QCheckBox* +WuQMessageBox::addDoNotShowAgainCheckBox(QMessageBox& messageBox) +{ + QCheckBox* checkBox = new QCheckBox("Remember my choice and do not show this dialog again"); + messageBox.setCheckBox(checkBox); + checkBox->setToolTip("Do not show again for this session (until wb_view is exited)"); + return checkBox; +} + + /** * Display a warning message box with Ok and Cancel * buttons. Pressing the enter key is the equivalent @@ -290,6 +368,364 @@ WuQMessageBox::warningOkCancel(QWidget* parent, return WuQMessageBox::warningOkCancel(parent, text, ""); } +/** + * Display a warning message box with Ok and Cancel + * buttons and a checkbox. Pressing the enter key is + * the equivalent of pressing the Ok button. + * + * @param parent + * Parent on which message box is displayed. + * @param text + * Message that is displayed. + * @param checkBoxText + * Text for display in the checkbox. + * @param checkBoxStatusInOut + * Default status for checkbox and status of + * checkbox upon exit + * @return + * true if the Ok button was pressed else false + * if the cancel button was pressed. + */ +bool +WuQMessageBox::warningYesNoWithDoNotShowAgain(QWidget* parent, + const QString& uniqueIdentifier, + const QString& text) +{ + return WuQMessageBox::warningYesNoWithDoNotShowAgain(parent, + uniqueIdentifier, + text, + ""); +} + +/** + * Display a warning message box with Yes and No + * buttons and a checkbox with the text + * "Remember my choice and do not show this dialog". + * + * If the user closes the dialog with the "Do not show + * again" box checked, the next time this function is + * called with the same "uniqueIdentifier" the dialog + * will not be displayed and return the selection made + * when the user last closed the dialog. + * + * @param parent + * Parent on which message box is displayed. + * @param uniqueIdentifier + * Unique identifier used for associating of + * "do not show" status + * @param text + * Message that is displayed. + * @param informativeText + * Displayed below 'text' if this is not empty. + * @return + * true if the Yes button was pressed false if + * the NO button was pressed. + */ +bool +WuQMessageBox::warningYesNoWithDoNotShowAgain(QWidget* parent, + const QString& uniqueIdentifier, + const QString& text, + const QString& informativeText) +{ + /* + * See if user previously selected "do not show again". + * If so, return the button that the user last clicked. + */ + QMessageBox::StandardButton doNotShowAgainButton = QMessageBox::NoButton; + auto doNotShowStatus = s_doNotShowAgainButtonSelection.find(uniqueIdentifier); + if (doNotShowStatus != s_doNotShowAgainButtonSelection.end()) { + doNotShowAgainButton = doNotShowStatus->second; + + bool yesPressedFlag = false; + switch (doNotShowAgainButton) { + case QMessageBox::Yes: + yesPressedFlag = true; + break; + case QMessageBox::No: + yesPressedFlag = false; + break; + default: + CaretAssert(0); + break; + + } + + return yesPressedFlag; + } + + /* + * Display a message box dialog to the user + */ + QMessageBox msgBox(parent); + msgBox.setIcon(QMessageBox::Warning); + msgBox.setWindowTitle(""); + msgBox.setText(text); + if (informativeText.isEmpty() == false) { + msgBox.setInformativeText(informativeText); + } + msgBox.addButton(QMessageBox::Yes); + msgBox.addButton(QMessageBox::No); + msgBox.setDefaultButton(QMessageBox::No); + msgBox.setEscapeButton(QMessageBox::No); + + QCheckBox* checkBox = addDoNotShowAgainCheckBox(msgBox); + + const QMessageBox::StandardButton buttonClicked = + static_cast(msgBox.exec()); + + bool yesPressedFlag = false; + + switch (buttonClicked) { + case QMessageBox::Yes: + yesPressedFlag = true; + break; + case QMessageBox::No: + break; + default: + CaretAssert(0); + break; + } + + /* + * If the "do not show again" checkbox was checked, + * save the button that the user clicked. + */ + const bool doNotShowAgainFlag = checkBox->isChecked(); + if (doNotShowAgainFlag) { + if (doNotShowStatus != s_doNotShowAgainButtonSelection.end()) { + doNotShowStatus->second = buttonClicked; + } + else { + s_doNotShowAgainButtonSelection.insert(std::make_pair(uniqueIdentifier, + buttonClicked)); + } + } + + return yesPressedFlag; +} + +/** + * Display an information dialog with two buttons, both of which + * will close the dialog. + * + * @param parent + * The parent dialog. + * @param text + * Text for the dialog. + * @param buttonOneText + * Text for the first button. + * @param buttonTwoText + * Text for the second button. + * @return + * 1 if button one clicked, 2 if button two clicked. + */ +int32_t +WuQMessageBox::informationTwoButtons(QWidget* parent, + const QString& text, + const QString& buttonOneText, + const QString& buttonTwoText) +{ + QMessageBox msgBox(parent); + msgBox.setIcon(QMessageBox::Information); + msgBox.setWindowTitle(""); + msgBox.setText(text); + msgBox.addButton(QMessageBox::YesToAll); + msgBox.addButton(QMessageBox::Yes); + msgBox.setDefaultButton(QMessageBox::Yes); + + WuQMessageBox::updateButtonText(msgBox, + QMessageBox::YesToAll, + buttonOneText); + WuQMessageBox::updateButtonText(msgBox, + QMessageBox::Yes, + buttonTwoText); + + + QMessageBox::StandardButton buttonPressed = + static_cast(msgBox.exec()); + + + int32_t buttonClicked = 0; + switch (buttonPressed) { + case QMessageBox::YesToAll: + buttonClicked = 1; + break; + case QMessageBox::Yes: + buttonClicked = 2; + break; + default: + CaretAssert(0); + break; + } + + return buttonClicked; +} + + +/** + * Display a Yes to All, Yes, No, button dialog. + * + * @param parent + * Parent on which message box is displayed. + * @param yesToAllButtonText + * Text for Yes to All button. If empty, + * "Yes to All" is displayed in button. + * @param yesButtonText + * Text for Yes button. If empty, + * "Yes" is displayed in button. + * @param noButtonText + * Text for No button. If empty, + * "No" is displayed in button. + * @param text + * Message that is displayed. + * @param informativeText + * Displayed below 'text' if this is not empty. + * @return + * One of QMessageBox::YesToAll + * QMessageBox::Yes + * QMessageBox::No + */ +WuQMessageBox::YesToAllYesNoResult +WuQMessageBox::warningYesToAllYesNo(QWidget* parent, + const QString& yesToAllButtonText, + const QString& yesButtonText, + const QString& noButtonText, + const QString& text, + const QString& informativeText) +{ + QMessageBox msgBox(parent); + msgBox.setIcon(QMessageBox::Warning); + msgBox.setWindowTitle(""); + msgBox.setText(text); + if (informativeText.isEmpty() == false) { + msgBox.setInformativeText(informativeText); + } + msgBox.addButton(QMessageBox::YesToAll); + msgBox.addButton(QMessageBox::Yes); + msgBox.addButton(QMessageBox::No); + msgBox.setDefaultButton(QMessageBox::Yes); + msgBox.setEscapeButton(QMessageBox::No); + + WuQMessageBox::updateButtonText(msgBox, + QMessageBox::YesToAll, + yesToAllButtonText); + WuQMessageBox::updateButtonText(msgBox, + QMessageBox::Yes, + yesButtonText); + WuQMessageBox::updateButtonText(msgBox, + QMessageBox::No, + noButtonText); + + + QMessageBox::StandardButton buttonPressed = + static_cast(msgBox.exec()); + + + YesToAllYesNoResult result = YesToAllYesNoResult::NO; + switch (buttonPressed) { + case QMessageBox::YesToAll: + result = YesToAllYesNoResult::YES_TO_ALL; + break; + case QMessageBox::Yes: + result = YesToAllYesNoResult::YES; + break; + case QMessageBox::No: + result = YesToAllYesNoResult::NO; + break; + default: + CaretAssert(0); + break; + } + + return result; +} + +/** + * Display a warning message box with the given + * text and content for accept/reject buttons. + * Pressing the enter key is the equivalent of + * pressing the Ok button. + * + * @param parent + * Parent on which message box is displayed. + * @param text + * Message that is displayed. + * @param acceptButtonText + * Text for "accept" button. + * @param rejectButtonText + * Text for "reject" button. + * @return + * true if the "accept" button was pressed or false + * if the "reject" button was pressed. + */ +bool +WuQMessageBox::warningAcceptReject(QWidget* parent, + const QString& text, + const QString& acceptButtonText, + const QString& rejectButtonText) +{ + QMessageBox msgBox(parent); + msgBox.setIcon(QMessageBox::Warning); + msgBox.setWindowTitle(""); + msgBox.setText(text); + msgBox.addButton(QMessageBox::Ok); + msgBox.addButton(QMessageBox::Cancel); + msgBox.setDefaultButton(QMessageBox::Ok); + msgBox.setEscapeButton(QMessageBox::Cancel); + + updateButtonText(msgBox, QMessageBox::Ok, acceptButtonText); + updateButtonText(msgBox, QMessageBox::Cancel, rejectButtonText); + + QMessageBox::StandardButton buttonPressed = + static_cast(msgBox.exec()); + + bool acceptPressed = false; + + switch (buttonPressed) { + case QMessageBox::Ok: + acceptPressed = true; + break; + case QMessageBox::Cancel: + break; + default: + CaretAssert(0); + break; + } + + return acceptPressed; +} + +/** + * Update the text for the standard button in the given message box. + * + * @param messageBox + * The message box. + * @param standardButton + * The standard button type. + * @param text + * Text for the standard button. If empty, no action is taken. + */ + +void +WuQMessageBox::updateButtonText(QMessageBox& messageBox, + const QMessageBox::StandardButton standardButton, + const QString& text) +{ + if (text.isEmpty()) { + return; + } + + QAbstractButton* button = messageBox.button(standardButton); + CaretAssert(button); + if (button == NULL) { + CaretLogSevere("Invalid QMessageBox::StandardButton integer value: " + + QString::number(static_cast(standardButton))); + return; + } + + button->setText(text); +} + /** * Display a warning message box with Yes and No * buttons. Pressing the enter key is the equivalent diff --git a/src/GuiQt/WuQMessageBox.h b/src/GuiQt/WuQMessageBox.h index 2498f39dc72d28c0ead63aaceb3312563f8681f4..93033f1478d9785190cebe73da7b21f04699e4ea 100644 --- a/src/GuiQt/WuQMessageBox.h +++ b/src/GuiQt/WuQMessageBox.h @@ -22,8 +22,12 @@ */ /*LICENSE_END*/ +#include + #include +class QCheckBox; + namespace caret { class WuQMessageBox : public QMessageBox { @@ -36,25 +40,49 @@ namespace caret { RESULT_CANCEL }; + enum class YesToAllYesNoResult { + YES_TO_ALL, + YES, + NO + }; + static void errorOk(QWidget* parent, const QString& text); static void informationOk(QWidget* parent, const QString& text); + static int32_t informationTwoButtons(QWidget* parent, + const QString& text, + const QString& buttonOneText, + const QString& buttonTwoText); + static void warningOk(QWidget* parent, const QString& text); + static void warningOkWithDoNotShowAgain(QWidget* parent, + const QString& uniqueIdentifier, + const QString& text); + static bool warningOkCancel(QWidget* parent, const QString& text); + static bool warningYesNoWithDoNotShowAgain(QWidget* parent, + const QString& uniqueIdentifier, + const QString& text); + static bool warningYesNo(QWidget* parent, - const QString& text); + const QString& text); static bool warningOkCancel(QWidget* parent, const QString& text, const QString& informativeText); + static bool warningYesNoWithDoNotShowAgain(QWidget* parent, + const QString& uniqueIdentifier, + const QString& text, + const QString& informativeText); + static bool warningYesNo(QWidget* parent, const QString& text, const QString& informativeText); @@ -71,6 +99,18 @@ namespace caret { const QString& text, const QString& informativeText); + static YesToAllYesNoResult warningYesToAllYesNo(QWidget* parent, + const QString& yesToAllButtonText, + const QString& yesButtonText, + const QString& noButtonText, + const QString& text, + const QString& informativeText); + static bool warningAcceptReject(QWidget* parent, + const QString& text, + const QString& acceptButtonText, + const QString& rejectButtonText); + + private: WuQMessageBox(QWidget* parent = 0); ~WuQMessageBox(); @@ -78,7 +118,26 @@ namespace caret { WuQMessageBox(const WuQMessageBox&); WuQMessageBox& operator=(const WuQMessageBox&); + static QCheckBox* addDoNotShowAgainCheckBox(QMessageBox& messageBox); + + static void updateButtonText(QMessageBox& messageBox, + const QMessageBox::StandardButton standardButton, + const QString& text); + + /* + * Maintains "do not show again selections. + * Key is unique identifier supplied by caller to a dialog. + * Value is the button that was clicked. + */ + static std::map s_doNotShowAgainButtonSelection; + }; + +#ifdef __WU_QMESSAGE_DEFINE__ + std::map WuQMessageBox::s_doNotShowAgainButtonSelection; +#endif // __WU_QMESSAGE_DEFINE__ + } + #endif // __WU_QMESSAGE_DIALOG_H__ diff --git a/src/GuiQt/WuQTextEditorDialog.cxx b/src/GuiQt/WuQTextEditorDialog.cxx new file mode 100644 index 0000000000000000000000000000000000000000..3a08e03009f1f330fce11d7ebcd86455eb517a32 --- /dev/null +++ b/src/GuiQt/WuQTextEditorDialog.cxx @@ -0,0 +1,177 @@ + +/*LICENSE_START*/ +/* + * Copyright (C) 2018 Washington University School of Medicine + * + * 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 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ +/*LICENSE_END*/ + +#define __WU_Q_TEXT_EDITOR_DIALOG_DECLARE__ +#include "WuQTextEditorDialog.h" +#undef __WU_Q_TEXT_EDITOR_DIALOG_DECLARE__ + +#include +#include +#include +#include + + +#include "CaretAssert.h" +#include "WuQtUtilities.h" + +using namespace caret; + + + +/** + * \class caret::WuQTextEditorDialog + * \brief Dialog that shows a text editor. + * \ingroup GuiQt + */ + +/** + * Run the dialog in non-modal (non-blocking) mode. + * Since non-modal and no way to retrieve text, the + * text is not editable. + * + * @param dialogTitle + * Title for dialog. + * @param text + * Text displayed in text editor. + * @param textMode + * Text mode (html or plain) + * @param wrapMode + * Text wrapping (no, yes) + * @param parent + * The parent widget on which dialog is displayed. + */ +void +WuQTextEditorDialog::runNonModal(const QString& dialogTitle, + const QString& text, + const TextMode textMode, + const WrapMode wrapMode, + QWidget* parent) +{ + WuQTextEditorDialog* dialog = new WuQTextEditorDialog(dialogTitle, + text, + TextReadOnlyMode::YES, + textMode, + wrapMode, + parent); + + /* + * Since dialog is non-modal, setting this attribute + * will destroy the dialog when the dialog is closed + * and prevent a memory leak. + */ + dialog->setAttribute(Qt::WA_DeleteOnClose); + + dialog->setVisible(true); + dialog->show(); + dialog->activateWindow(); + dialog->raise(); +} + +/** + * Constructor. + * + * @param dialogTitle + * Title for dialog. + * @param text + * Text displayed in text editor. + * @param readOnlyMode + * Read only mode (no, yes) + * @param textMode + * Text mode (html or plain) + * @param wrapMode + * Text wrapping (no, yes) + * @param parent + * The parent widget on which dialog is displayed. + */ +WuQTextEditorDialog::WuQTextEditorDialog(const QString& dialogTitle, + const QString& text, + const TextReadOnlyMode readOnlyMode, + const TextMode textMode, + const WrapMode wrapMode, + QWidget* parent) +: QDialog(parent) +{ + setWindowTitle(dialogTitle); + + m_textEdit = new QTextEdit(); + switch (textMode) { + case TextMode::HTML: + m_textEdit->setHtml(text); + break; + case TextMode::PLAIN: + m_textEdit->setPlainText(text); + break; + } + + switch (readOnlyMode) { + case TextReadOnlyMode::NO: + m_textEdit->setReadOnly(false); + break; + case TextReadOnlyMode::YES: + m_textEdit->setReadOnly(true); + break; + } + + QPushButton* closeButton = new QPushButton("Close"); + QObject::connect(closeButton, &QPushButton::clicked, + this, &QDialog::accept); + + QVBoxLayout* layout = new QVBoxLayout(this); + layout->addWidget(m_textEdit, 100); + layout->addWidget(closeButton, 0, Qt::AlignCenter); + + + QTextDocument* document = m_textEdit->document(); + + QTextOption textOption = document->defaultTextOption(); + switch (wrapMode) { + case WrapMode::NO: + textOption.setWrapMode(QTextOption::NoWrap); + break; + case WrapMode::YES: + textOption.setWrapMode(QTextOption::WordWrap); + break; + } + + document->setDefaultTextOption(textOption); + + document->adjustSize(); + m_textEdit->adjustSize(); + + int32_t dialogWidth = document->size().width() + 70; + if (dialogWidth < 300) { + dialogWidth = 300; + } + const int32_t dialogHeight = document->size().height() + 100; + + WuQtUtilities::resizeWindow(this, + dialogWidth, + dialogHeight); + +} + +/** + * Destructor. + */ +WuQTextEditorDialog::~WuQTextEditorDialog() +{ +} + diff --git a/src/GuiQt/WuQTextEditorDialog.h b/src/GuiQt/WuQTextEditorDialog.h new file mode 100644 index 0000000000000000000000000000000000000000..ef8802db51ea5af2ef1218b484826268f825a287 --- /dev/null +++ b/src/GuiQt/WuQTextEditorDialog.h @@ -0,0 +1,88 @@ +#ifndef __WU_Q_TEXT_EDITOR_DIALOG_H__ +#define __WU_Q_TEXT_EDITOR_DIALOG_H__ + +/*LICENSE_START*/ +/* + * Copyright (C) 2018 Washington University School of Medicine + * + * 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 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ +/*LICENSE_END*/ + + + +#include + +#include + +class QTextEdit; + + +namespace caret { + class WuQTextEditorDialog : public QDialog { + + Q_OBJECT + + public: + enum class TextMode { + HTML, + PLAIN + }; + + enum class WrapMode { + NO, + YES + }; + + enum class TextReadOnlyMode { + NO, + YES + }; + + static void runNonModal(const QString& dialogTitle, + const QString& text, + const TextMode textMode, + const WrapMode wrapMode, + QWidget* parent); + + WuQTextEditorDialog(const QString& dialogTitle, + const QString& text, + const TextReadOnlyMode readOnlyMode, + const TextMode textMode, + const WrapMode wrapMode, + QWidget* parent = 0); + + virtual ~WuQTextEditorDialog(); + + + // ADD_NEW_METHODS_HERE + + private: + WuQTextEditorDialog(const WuQTextEditorDialog&); + + WuQTextEditorDialog& operator=(const WuQTextEditorDialog&); + + QTextEdit* m_textEdit; + + // ADD_NEW_MEMBERS_HERE + + }; + +#ifdef __WU_Q_TEXT_EDITOR_DIALOG_DECLARE__ + // +#endif // __WU_Q_TEXT_EDITOR_DIALOG_DECLARE__ + +} // namespace +#endif //__WU_Q_TEXT_EDITOR_DIALOG_H__ diff --git a/src/GuiQt/WuQTimedMessageDisplay.cxx b/src/GuiQt/WuQTimedMessageDisplay.cxx index b9cdd9531d157aabb16506233ad246d32edc7c31..b9b6754c6177ef7b2fd08f33059c68e653cdb8a6 100644 --- a/src/GuiQt/WuQTimedMessageDisplay.cxx +++ b/src/GuiQt/WuQTimedMessageDisplay.cxx @@ -55,10 +55,13 @@ using namespace caret; * Message is displayed for this amount of time, in milliseconds. * @param message * Message that is displayed. + * @param modalFlag + * If true dialog is modal. */ WuQTimedMessageDisplay::WuQTimedMessageDisplay(QWidget* parent, const float displayForSeconds, - const QString& message) + const QString& message, + const bool modalFlag) : QDialog(parent, Qt::FramelessWindowHint) { @@ -68,7 +71,7 @@ WuQTimedMessageDisplay::WuQTimedMessageDisplay(QWidget* parent, /* * Modal so it blocks until done. */ - setModal(true); + setModal(modalFlag); /* * Delete self when done. @@ -94,8 +97,14 @@ WuQTimedMessageDisplay::WuQTimedMessageDisplay(QWidget* parent, */ QTimer* timer = new QTimer(this); timer->setSingleShot(true); - QObject::connect(timer, SIGNAL(timeout()), - this, SLOT(accept())); + if (modalFlag) { + QObject::connect(timer, SIGNAL(timeout()), + this, SLOT(accept())); + } + else { + QObject::connect(timer, SIGNAL(timeout()), + this, SLOT(close())); + } timer->start(displayForSeconds * 1000.0); /* @@ -117,16 +126,42 @@ WuQTimedMessageDisplay::WuQTimedMessageDisplay(QWidget* parent, * Message that is displayed. */ void -WuQTimedMessageDisplay::show(QWidget* parent, - const float displayForSeconds, - const QString& message) +WuQTimedMessageDisplay::showModal(QWidget* parent, + const float displayForSeconds, + const QString& message) { WuQTimedMessageDisplay* md = new WuQTimedMessageDisplay(parent, displayForSeconds, - message); + message, + true); md->exec(); } +/** + * Display a message containing the given message for the given amount + * of time. This method will return IMMEDIATELY and the message will + * delete its self when closed. + * + * @param parent + * Parent on which message is displayed. + * @param displayForSeconds + * Message is displayed for this amount of time, in milliseconds. + * @param message + * Message that is displayed. + */ +void +WuQTimedMessageDisplay::showNonModal(QWidget* parent, + const float displayForSeconds, + const QString& message) +{ + WuQTimedMessageDisplay* md = new WuQTimedMessageDisplay(parent, + displayForSeconds, + message, + false); + md->show(); + md->raise(); +} + /** * Display a message containing the given message for the given amount * of time. This method will not return until the message window closes. @@ -143,15 +178,16 @@ WuQTimedMessageDisplay::show(QWidget* parent, * Message that is displayed. */ void -WuQTimedMessageDisplay::show(QWidget* parent, - const int32_t x, - const int32_t y, - const float displayForSeconds, - const QString& message) +WuQTimedMessageDisplay::showModal(QWidget* parent, + const int32_t x, + const int32_t y, + const float displayForSeconds, + const QString& message) { WuQTimedMessageDisplay* md = new WuQTimedMessageDisplay(parent, displayForSeconds, - message); + message, + true); const int32_t originAtTopWindowY = parent->height() - y; QPoint globalXY = parent->mapToGlobal(QPoint(x, originAtTopWindowY)); @@ -164,6 +200,5 @@ WuQTimedMessageDisplay::show(QWidget* parent, */ WuQTimedMessageDisplay::~WuQTimedMessageDisplay() { - } diff --git a/src/GuiQt/WuQTimedMessageDisplay.h b/src/GuiQt/WuQTimedMessageDisplay.h index 13c1f05764b09da4fb1c6730bf559036ceb3e6b6..2813763cfaeadf42899d86837b44857322eeef70 100644 --- a/src/GuiQt/WuQTimedMessageDisplay.h +++ b/src/GuiQt/WuQTimedMessageDisplay.h @@ -34,18 +34,23 @@ namespace caret { private: WuQTimedMessageDisplay(QWidget* parent, const float displayForSeconds, - const QString& message); + const QString& message, + const bool modalFlag); public: - static void show(QWidget* parent, - const float displayForSeconds, - const QString& message); + static void showModal(QWidget* parent, + const float displayForSeconds, + const QString& message); - static void show(QWidget* parent, - const int32_t x, - const int32_t y, - const float displayForSeconds, - const QString& message); + static void showNonModal(QWidget* parent, + const float displayForSeconds, + const QString& message); + + static void showModal(QWidget* parent, + const int32_t x, + const int32_t y, + const float displayForSeconds, + const QString& message); virtual ~WuQTimedMessageDisplay(); @@ -59,7 +64,7 @@ namespace caret { // ADD_NEW_METHODS_HERE private: - + // ADD_NEW_MEMBERS_HERE }; diff --git a/src/GuiQt/WuQWidget.cxx b/src/GuiQt/WuQWidget.cxx index d49fb4b421abb47ff6aabfbd0b3ebfe530ac3d61..d6795956764fd73698f74e4cba52b1de36eba1dd 100644 --- a/src/GuiQt/WuQWidget.cxx +++ b/src/GuiQt/WuQWidget.cxx @@ -23,6 +23,8 @@ #include "WuQWidget.h" #undef __WU_Q_WIDGET_DECLARE__ +#include + #include "CaretAssert.h" using namespace caret; @@ -67,6 +69,7 @@ using namespace caret; * which will destroy it. */ + /** * Constructor. */ @@ -83,5 +86,34 @@ WuQWidget::~WuQWidget() { } +/** + * Sets both the minimum and maximum width of the widget to w without changing the heights. + * + * @param width + * Width value. + */ +void +WuQWidget::setFixedWidth(int width) +{ + QWidget* widget = getWidget(); + CaretAssert(widget); + widget->setFixedWidth(width); +} + + +/** + * Set the tooltip for this spin box. + * + * @param tooltip + * Text for the tooltip. + */ +void +WuQWidget::setToolTip(const QString& tooltip) +{ + QWidget* widget = getWidget(); + CaretAssert(widget); + widget->setToolTip(tooltip); +} + diff --git a/src/GuiQt/WuQWidget.h b/src/GuiQt/WuQWidget.h index bec83b30f777a4b70bec4ce6302558042fc736bf..5ffe7fffdbe7c20e2a3346176f52dcbb07b1f76e 100644 --- a/src/GuiQt/WuQWidget.h +++ b/src/GuiQt/WuQWidget.h @@ -39,6 +39,10 @@ namespace caret { virtual QWidget* getWidget() = 0; + virtual void setToolTip(const QString& tooltip); + + void setFixedWidth(int w); + private: WuQWidget(const WuQWidget&); diff --git a/src/GuiQt/WuQWidgetDisabler.cxx b/src/GuiQt/WuQWidgetDisabler.cxx new file mode 100644 index 0000000000000000000000000000000000000000..a908261f4db974c1b4b3961f56ffedb1ed2d3c30 --- /dev/null +++ b/src/GuiQt/WuQWidgetDisabler.cxx @@ -0,0 +1,119 @@ + +/*LICENSE_START*/ +/* + * Copyright (C) 2017 Washington University School of Medicine + * + * 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 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ +/*LICENSE_END*/ + +#define __WU_Q_WIDGET_DISABLER_DECLARE__ +#include "WuQWidgetDisabler.h" +#undef __WU_Q_WIDGET_DISABLER_DECLARE__ + +#include + +#include "CaretAssert.h" + +using namespace caret; + + + +/** + * \class caret::WuQWidgetDisabler + * \brief Simplifies disabling of QWidget instance within a scope + * \ingroup GuiQt + * + * WuQWidgetDisabler can be used whereever you would otherwise + * use a pair of calls to setEnabled(). It disables the + * QWidget in its constructor and in the destructor it resets + * the enabled state to what it was before the constructor + * ran. + * + * An example for usage of this class is in the method + * that receives a QPushButton:clicked signal. User cannot + * push the button until the method completes. + * + * The design of this class is modeled after QSignalBlocker + * so Qt license is below. + * + */ + +/**************************************************************************** + ** + ** Copyright (C) 2016 The Qt Company Ltd. + ** Contact: https://www.qt.io/licensing/ + ** + ** This file is part of the QtWidgets module of the Qt Toolkit. + ** + ** $QT_BEGIN_LICENSE:LGPL$ + ** Commercial License Usage + ** Licensees holding valid commercial Qt licenses may use this file in + ** accordance with the commercial license agreement provided with the + ** Software or, alternatively, in accordance with the terms contained in + ** a written agreement between you and The Qt Company. For licensing terms + ** and conditions see https://www.qt.io/terms-conditions. For further + ** information use the contact form at https://www.qt.io/contact-us. + ** + ** GNU Lesser General Public License Usage + ** Alternatively, this file may be used under the terms of the GNU Lesser + ** General Public License version 3 as published by the Free Software + ** Foundation and appearing in the file LICENSE.LGPL3 included in the + ** packaging of this file. Please review the following information to + ** ensure the GNU Lesser General Public License version 3 requirements + ** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. + ** + ** GNU General Public License Usage + ** Alternatively, this file may be used under the terms of the GNU + ** General Public License version 2.0 or (at your option) the GNU General + ** Public license version 3 or any later version approved by the KDE Free + ** Qt Foundation. The licenses are as published by the Free Software + ** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 + ** included in the packaging of this file. Please review the following + ** information to ensure the GNU General Public License requirements will + ** be met: https://www.gnu.org/licenses/gpl-2.0.html and + ** https://www.gnu.org/licenses/gpl-3.0.html. + ** + ** $QT_END_LICENSE$ + ** + ****************************************************************************/ + + +/** + * Constructor. + * Calls widget->setEnabled(false) + * + * @param widget + * The QWidget + */ +WuQWidgetDisabler::WuQWidgetDisabler(QWidget* widget) +: CaretObject(), +m_widget(widget) +{ + CaretAssert(m_widget); + + m_savedEnabledState = m_widget->isEnabled(); + m_widget->setEnabled(false); +} + +/** + * Destructor. Restores enabled status of the QObject + * to what it was before the constructor rang. + */ +WuQWidgetDisabler::~WuQWidgetDisabler() +{ + m_widget->setEnabled(m_savedEnabledState); +} + diff --git a/src/Common/CaretJsonObject.h b/src/GuiQt/WuQWidgetDisabler.h similarity index 53% rename from src/Common/CaretJsonObject.h rename to src/GuiQt/WuQWidgetDisabler.h index 92404281033d18cbfc910ba5009a5a50eb22486e..e8d3d963bc891bb8e07b452ba71e326d528b473c 100644 --- a/src/Common/CaretJsonObject.h +++ b/src/GuiQt/WuQWidgetDisabler.h @@ -1,9 +1,9 @@ -#ifndef __CARET_JSON_OBJECT_H__ -#define __CARET_JSON_OBJECT_H__ +#ifndef __WU_Q_WIDGET_DISABLER_H__ +#define __WU_Q_WIDGET_DISABLER_H__ /*LICENSE_START*/ /* - * Copyright (C) 2016 Washington University School of Medicine + * Copyright (C) 2017 Washington University School of Medicine * * 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 @@ -21,51 +21,39 @@ */ /*LICENSE_END*/ -#include #include "CaretObject.h" - +class QWidget; namespace caret { - class CaretJsonObject : public CaretObject { + class WuQWidgetDisabler : public CaretObject { public: - CaretJsonObject(const AString& jsonText); + WuQWidgetDisabler(QWidget* widget); - virtual ~CaretJsonObject(); - - int32_t count() const; + virtual ~WuQWidgetDisabler(); - bool empty() const; - std::vector keys() const; - - bool hasKey(const AString& key) const; - - AString value(const AString& key) const; - // ADD_NEW_METHODS_HERE - + private: - CaretJsonObject(const CaretJsonObject&); + WuQWidgetDisabler(const WuQWidgetDisabler&); - CaretJsonObject& operator=(const CaretJsonObject&); - - void processNameValueString(const AString& nameValueString); + WuQWidgetDisabler& operator=(const WuQWidgetDisabler&); - void trimLeadingTrailingDoubleQuotes(AString& text); + QWidget* m_widget; - std::map m_data; + bool m_savedEnabledState = true; // ADD_NEW_MEMBERS_HERE }; -#ifdef __CARET_JSON_OBJECT_DECLARE__ +#ifdef __WU_Q_WIDGET_DISABLER_DECLARE__ // -#endif // __CARET_JSON_OBJECT_DECLARE__ +#endif // __WU_Q_WIDGET_DISABLER_DECLARE__ } // namespace -#endif //__CARET_JSON_OBJECT_H__ +#endif //__WU_Q_WIDGET_DISABLER_H__ diff --git a/src/GuiQt/WuQtUtilities.cxx b/src/GuiQt/WuQtUtilities.cxx index cd9c37f57013b788ba9dddaeb06c68790437551b..1acec9a6c13642c093020f8eb9b1077a4f3ea5db 100644 --- a/src/GuiQt/WuQtUtilities.cxx +++ b/src/GuiQt/WuQtUtilities.cxx @@ -35,10 +35,11 @@ #include #include #include -#include +//#include #include #include #include +#include #include "CaretAssert.h" #include "CaretLogger.h" @@ -389,7 +390,18 @@ WuQtUtilities::moveAndSizeWindow(QWidget* window, */ QPoint pXY(x, y); +#if QT_VERSION >= 0x050000 + const QRect availableRect = dw->availableGeometry(); +#else + /* + * Note 23 September 2016: + * Calling geometry is likely the WRONG method to call + * but since it is not causing a problem in in Qt 4.x + * we will continuing using to avoid the risk of + * breaking scenes. + */ const QRect availableRect = dw->screen()->geometry(); +#endif const int32_t screenSizeX = availableRect.width(); const int32_t screenSizeY = availableRect.height(); @@ -515,6 +527,57 @@ WuQtUtilities::resizeWindow(QWidget* window, } } +/** + * Limit a window's size to a percentage of the maximum size. + * + * @param window + * Window that is resized. + * @param widthMaximumPercentage + * Maximum width percentage [0, 100] + * @param heightMaximumPercentage + * Maximum height percentage [0, 100] + */ +void +WuQtUtilities::limitWindowSizePercentageOfMaximum(QWidget* window, + const float widthMaximumPercentage, + const float heightMaximumPercentage) +{ + AString message; + if ((widthMaximumPercentage < 0.0) + || (widthMaximumPercentage > 100.0)) { + message.appendWithNewLine("Percentage width must range [0, 100]: value -> " + + AString::number(widthMaximumPercentage)); + } + if ((heightMaximumPercentage < 0.0) + || (heightMaximumPercentage > 100.0)) { + message.appendWithNewLine("Percentage height must range [0, 100]: value -> " + + AString::number(heightMaximumPercentage)); + } + + if ( ! message.isEmpty()) { + CaretLogSevere(message); + return; + } + + QDesktopWidget* dw = QApplication::desktop(); + QPoint pXY(window->x(), + window->y()); + const int32_t nearestScreen = dw->screenNumber(pXY); + if (nearestScreen >= 0) { + const QRect screenRect = dw->availableGeometry(nearestScreen); + const int32_t screenWidth = screenRect.width(); + const int32_t screenHeight = screenRect.height(); + + const float maxWidth = ((widthMaximumPercentage / 100.0) * screenWidth); + const float maxHeight = ((heightMaximumPercentage / 100.0) * screenHeight); + + window->setMaximumSize(maxWidth, + maxHeight); + window->adjustSize(); + } +} + + /** * Table widget has a default size of 640 x 480. * Estimate the size of the dialog with the table fully expanded. @@ -1024,33 +1087,33 @@ WuQtUtilities::getLayoutContentDescription(QLayout* layout) return s; } -/** - * Play a sound file. The sound file MUST be in the distribution's - * "resources/sounds" directory. - * - * Note that sound files, as of Qt 4.8, do not support Qt's resource - * system. - * - * @param soundFileName - * Name of sound file (with no path, just the filename). - */ -void -WuQtUtilities::playSound(const QString& soundFileName) -{ - const QString workbenchDir = SystemUtilities::getWorkbenchHome(); - const QString soundFilePath = (workbenchDir - + "/../resources/sounds/" - + soundFileName); - - if (QFile::exists(soundFilePath)) { - QSound::play(soundFilePath); - } - else { - CaretLogSevere("Sound file \"" - + soundFilePath - + "\" does not exist."); - } -} +///** +// * Play a sound file. The sound file MUST be in the distribution's +// * "resources/sounds" directory. +// * +// * Note that sound files, as of Qt 4.8, do not support Qt's resource +// * system. +// * +// * @param soundFileName +// * Name of sound file (with no path, just the filename). +// */ +//void +//WuQtUtilities::playSound(const QString& soundFileName) +//{ +// const QString workbenchDir = SystemUtilities::getWorkbenchHome(); +// const QString soundFilePath = (workbenchDir +// + "/../resources/sounds/" +// + soundFileName); +// +// if (QFile::exists(soundFilePath)) { +// QSound::play(soundFilePath); +// } +// else { +// CaretLogSevere("Sound file \"" +// + soundFilePath +// + "\" does not exist."); +// } +//} /** * Create the text for a tooltip so that long lines are @@ -1100,6 +1163,27 @@ WuQtUtilities::setWordWrappedToolTip(QWidget* widget, widget->setToolTip(createWordWrappedToolTipText(tooltipText)); } +/** + * Set the text for a tooltip so that long lines are + * wrapped and the tooltip is not one giant line + * that is the width of the display. + * + * This is accomplished by placing the text into a + * QTextDocument and then retrieving the text with + * HTML formatting. + * + * @param action + * Action on which tooltip is set. + * @param tooltipText + * Text for the widget's tooltip. + */ +void +WuQtUtilities::setWordWrappedToolTip(QAction* action, + const QString& tooltipText) +{ + action->setToolTip(createWordWrappedToolTipText(tooltipText)); +} + /** * Convert a Qt::CheckState to a boolean value. * @@ -1198,7 +1282,7 @@ WuQtUtilities::createCaretColorEnumPixmap(const QWidget* widget, case CaretColorEnum::TEAL: case CaretColorEnum::WHITE: case CaretColorEnum::YELLOW: - CaretColorEnum::toRGBFloat(caretColor, + CaretColorEnum::toRGBAFloat(caretColor, colorRGBA); colorRGBA[3] = 1.0; validColorFlag = true; @@ -1291,6 +1375,45 @@ WuQtUtilities::createPixmapWidgetPainterOriginCenter100x100(const QWidget* widge return painter; } +/** + * Create a painter for the given pixmap that will be placed + * into the given widget. The pixmap's background is painted + * with the widget's background color, the painter's pen is set + * to the widget's foreground color, and then the painter is + * returned. + * + * Origin of the painter will be in the center with the + * coordinates, both X and Y, ranging -100 to 100. + * + * @param widget + * Widget used for coloring. + * @param pixmap + * The Pixmap must be square (width == height). + * @return + * Shared pointer containing QPainter for drawing to the pixmap. + */ +QSharedPointer +WuQtUtilities::createPixmapWidgetPainterOriginCenter(const QWidget* widget, + QPixmap& pixmap) +{ + CaretAssert(pixmap.width() == pixmap.height()); + + QSharedPointer painter = createPixmapWidgetPainter(widget, + pixmap); + + /* + * Note: QPainter has its origin at the top left. + * Using a negative for the Y-scale value will + * move the origin to the bottom. + */ + painter->translate(pixmap.width() / 2.0, + pixmap.height() / 2.0); + painter->scale(1.0, + -1.0); + + return painter; +} + /** * Create a painter for the given pixmap that will be placed * into the given widget. The pixmap's background is painted @@ -1379,5 +1502,95 @@ WuQtUtilities::createPixmapWidgetPainter(const QWidget* widget, return painter; } +/** + * With Qt5, a toolbutton placed into a toolbar uses the + * background of the toolbar with no border and appears + * similar to a label. Use a stylesheet so that + * the button appears similar to Qt4. + * + * @param toolButton + * toolButton ToolButton that needs style updated. + */ +#ifdef CARET_OS_MACOSX +#if QT_VERSION >= 0x050000 +void +WuQtUtilities::setToolButtonStyleForQt5Mac(QToolButton* toolButton) +{ + CaretAssert(toolButton); + + bool hasMenuFlag = false; + bool hasCheckableFlag = false; + QAction* action = toolButton->defaultAction(); + if (action != NULL) { + if (action->menu() != NULL) { + hasMenuFlag = true; + } + if (action->isCheckable()) { + hasCheckableFlag = true; + } + } + + const QPalette palette = toolButton->palette(); + const QPalette::ColorRole backgroundRole = toolButton->backgroundRole(); + const QBrush backgroundBrush = palette.brush(backgroundRole); + const QColor backgroundColor = backgroundBrush.color(); + const QColor lighterColor = backgroundColor.lighter(100); + const QColor darkerColor = backgroundColor.darker(125); + const QColor slightlyDarkerColor = backgroundColor.darker(115); + + /* + * Use a stylesheet to: + * (1) Make the background of the button lighter + * (2) Add a border around the button that is slightly + * darker than the background. + */ + QString toolButtonStyleSheet(" QToolButton { " + " background: " + lighterColor.name() + "; "); + if (hasMenuFlag) { + // toolButtonStyleSheet.append(" border-style: solid; " + // " border-width: 1px; " + // " border-color: " + darkerColor.name() + "; " + // " padding-top: 6px; " + // " padding-bottom: 6px; " + // " padding-right: 4px; " + // " padding-left: 3px; "); + } + else { + toolButtonStyleSheet.append(" border-style: solid; " + " border-width: 1px; " + " border-color: " + darkerColor.name() + "; " + " padding-top: 2px; " + " padding-bottom: 2px; " + " padding-right: 3px; " + " padding-left: 3px; "); + } + toolButtonStyleSheet.append(" } "); + + if (hasCheckableFlag) { + /* + * Background color when button is "checked" + */ + toolButtonStyleSheet.append(" QToolButton:checked { " + " background-color: " + slightlyDarkerColor.name() + "; " + " } "); + } + else { + /* + * Background color when button is "pressed" + */ + toolButtonStyleSheet.append(" QToolButton:pressed { " + " background-color: " + slightlyDarkerColor.name() + "; " + " } "); + } + + toolButton->setStyleSheet(toolButtonStyleSheet); +} +#else + void WuQtUtilities::setToolButtonStyleForQt5Mac(QToolButton*) { } +#endif +#else + void WuQtUtilities::setToolButtonStyleForQt5Mac(QToolButton*) { } +#endif + diff --git a/src/GuiQt/WuQtUtilities.h b/src/GuiQt/WuQtUtilities.h index d5d106ceb70eaa4c969190e033927a2f7dac0274..73a53a6ff2339f3a760ee04c88433c0a933e9c97 100644 --- a/src/GuiQt/WuQtUtilities.h +++ b/src/GuiQt/WuQtUtilities.h @@ -41,6 +41,7 @@ class QPixmap; class QPushButton; class QString; class QTableWidget; +class QToolButton; class QWidget; namespace caret { @@ -82,11 +83,11 @@ namespace caret { static QWidget* createHorizontalLineWidget(); - static QPixmap createCaretColorEnumPixmap(const QWidget* widgetForPixmap, + static QPixmap createCaretColorEnumPixmap(const QWidget* widget, const int32_t pixmapWidth, const int32_t pixmapHeight, const CaretColorEnum::Enum caretColor, - const float rgba[4], + const float customColorRGBA[4], const bool outlineFlag); static QSharedPointer createPixmapWidgetPainter(const QWidget* widget, @@ -95,6 +96,9 @@ namespace caret { static QSharedPointer createPixmapWidgetPainterOriginBottomLeft(const QWidget* widget, QPixmap& pixmap); + static QSharedPointer createPixmapWidgetPainterOriginCenter(const QWidget* widget, + QPixmap& pixmap); + static QSharedPointer createPixmapWidgetPainterOriginCenter100x100(const QWidget* widget, QPixmap& pixmap); @@ -133,6 +137,10 @@ namespace caret { const int32_t width, const int32_t height); + static void limitWindowSizePercentageOfMaximum(QWidget* window, + const float widthMaximumPercentage, + const float heightMaximumPercentage); + static int getMaximumWidgetHeight(QWidget* w1, QWidget* w2, QWidget* w3 = 0, @@ -181,17 +189,22 @@ namespace caret { static QString getLayoutContentDescription(QLayout* layout); - static void playSound(const QString& soundFileName); + //static void playSound(const QString& soundFileName); static QString createWordWrappedToolTipText(const QString& tooltipText); static void setWordWrappedToolTip(QWidget* widget, const QString& tooltipText); + static void setWordWrappedToolTip(QAction* action, + const QString& tooltipText); + static bool checkStateToBool(const Qt::CheckState checkState); static Qt::CheckState boolToCheckState(const bool value); + static void setToolButtonStyleForQt5Mac(QToolButton* toolButton); + private: WuQtUtilities(); ~WuQtUtilities(); diff --git a/src/GuiQt/ZipSceneFileDialog.cxx b/src/GuiQt/ZipSceneFileDialog.cxx index 675baf50040a8d5cdb62bcdc6c50a534e1259baf..c135161be162f3cb318391b9870e28135dd85787 100644 --- a/src/GuiQt/ZipSceneFileDialog.cxx +++ b/src/GuiQt/ZipSceneFileDialog.cxx @@ -24,9 +24,13 @@ #undef __ZIP_SCENE_FILE_DIALOG_DECLARE__ #include +#include #include #include #include +#include +#include +#include #include "BalsaDatabaseManager.h" #include "Brain.h" @@ -34,8 +38,10 @@ #include "CaretFileDialog.h" #include "CaretLogger.h" #include "CursorDisplayScoped.h" +#include "DataFileException.h" #include "FileInformation.h" #include "GuiManager.h" +#include "SceneBasePathWidget.h" #include "SceneFile.h" #include "SystemUtilities.h" #include "WuQMessageBox.h" @@ -56,7 +62,7 @@ using namespace caret; * @param parent * Parent of this dialog. */ -ZipSceneFileDialog::ZipSceneFileDialog(const SceneFile* sceneFile, +ZipSceneFileDialog::ZipSceneFileDialog(SceneFile* sceneFile, QWidget* parent) : WuQDialogModal("Zip Scene File", parent), @@ -65,7 +71,7 @@ m_sceneFile(sceneFile) const int minimumLineEditWidth = 400; - AString extractToDirectoryName("ext_dir"); + AString defaultExtractToDirectoryName("ext_dir"); AString zipFileName("file.zip"); FileInformation fileInfo(sceneFile->getFileName()); AString sceneFileDirectory = fileInfo.getAbsolutePath(); @@ -75,7 +81,7 @@ m_sceneFile(sceneFile) const AString dirName = dir.dirName(); if ( ! dirName.isEmpty()) { if (dirName != ".") { - extractToDirectoryName = dirName; + defaultExtractToDirectoryName = dirName; } } } @@ -90,38 +96,74 @@ m_sceneFile(sceneFile) "zip"); } - QLabel* zipFileNameLabel = new QLabel("Zip File Name"); + /* + * ZIP file + */ + m_zipFileNameLabel = new QLabel("Zip File Name"); m_zipFileNameLineEdit = new QLineEdit; + m_zipFileNameLineEdit->setToolTip("Name of the ZIP file"); m_zipFileNameLineEdit->setMinimumWidth(minimumLineEditWidth); m_zipFileNameLineEdit->setText(zipFileName); + m_zipFileNameLineEdit->setValidator(createValidator(LabelName::ZIP_FILE)); + QObject::connect(m_zipFileNameLineEdit, &QLineEdit::textEdited, + this, [=] { this->validateData(); }); - QPushButton* chooseZipFileButton = new QPushButton("Choose..."); + /* + * Zip file button + */ + QPushButton* chooseZipFileButton = new QPushButton("Browse..."); QObject::connect(chooseZipFileButton, SIGNAL(clicked()), this, SLOT(chooseZipFileButtonClicked())); - QLabel* extractDirectoryLabel = new QLabel("Extract to Directory"); + /* + * Extract to directory + */ + m_extractDirectoryLabel = new QLabel("Extract to Directory"); m_extractDirectoryNameLineEdit = new QLineEdit(); + m_extractDirectoryNameLineEdit->setToolTip("Directory that is created when user unzips the ZIP file"); m_extractDirectoryNameLineEdit->setMinimumWidth(minimumLineEditWidth); - m_extractDirectoryNameLineEdit->setText(extractToDirectoryName); + m_extractDirectoryNameLineEdit->setValidator(createValidator(LabelName::EXTRACT_DIRECTORY)); + QObject::connect(m_extractDirectoryNameLineEdit, &QLineEdit::textEdited, + this, [=] { this->validateData(); }); + /* + * Base Directory + */ + m_basePathWidget = new SceneBasePathWidget(); - QWidget* dialogWidget = new QWidget(); - QGridLayout* gridLayout = new QGridLayout(dialogWidget); - gridLayout->setColumnStretch(0, 0); - gridLayout->setColumnStretch(1, 100); - gridLayout->setColumnStretch(2, 0); + QGroupBox* zipFileGroupBox = new QGroupBox("Zip File"); + QGridLayout* zipFileLayout = new QGridLayout(zipFileGroupBox); + zipFileLayout->setSpacing(2); + zipFileLayout->setColumnStretch(0, 0); + zipFileLayout->setColumnStretch(1, 100); + zipFileLayout->setColumnStretch(2, 0); int row = 0; - gridLayout->addWidget(zipFileNameLabel, row, 0); - gridLayout->addWidget(m_zipFileNameLineEdit, row, 1); - gridLayout->addWidget(chooseZipFileButton, row, 2); + zipFileLayout->addWidget(m_zipFileNameLabel, row, 0); + zipFileLayout->addWidget(m_zipFileNameLineEdit, row, 1); + zipFileLayout->addWidget(chooseZipFileButton, row, 2); row++; - gridLayout->addWidget(extractDirectoryLabel, row, 0); - gridLayout->addWidget(m_extractDirectoryNameLineEdit, row, 1); + zipFileLayout->addWidget(m_extractDirectoryLabel, row, 0); + zipFileLayout->addWidget(m_extractDirectoryNameLineEdit, row, 1); + row++; + + QWidget* dialogWidget = new QWidget(); + QVBoxLayout* dialogLayout = new QVBoxLayout(dialogWidget); + dialogLayout->addWidget(zipFileGroupBox); + dialogLayout->addWidget(m_basePathWidget); row++; + AString extractDirectory = m_sceneFile->getBalsaExtractToDirectoryName(); + if (extractDirectory.isEmpty()) { + extractDirectory = m_sceneFile->getDefaultExtractToDirectoryName(); + } + m_extractDirectoryNameLineEdit->setText(extractDirectory); setCentralWidget(dialogWidget, WuQDialogModal::SCROLL_AREA_NEVER); + + m_basePathWidget->updateWithSceneFile(m_sceneFile); + + validateData(); } /** @@ -131,6 +173,13 @@ ZipSceneFileDialog::~ZipSceneFileDialog() { } +void +ZipSceneFileDialog::validateData() +{ + setLabelText(LabelName::EXTRACT_DIRECTORY); + setLabelText(LabelName::ZIP_FILE); +} + /** * Choose the zip file name with a file browser dialog. */ @@ -168,11 +217,36 @@ ZipSceneFileDialog::okButtonClicked() if (zipFileName.isEmpty()) { errorMessage.appendWithNewLine("Zip file name is missing"); } - if (extractToDirectoryName.isEmpty()) { - errorMessage.appendWithNewLine("Extract to directory is missing."); + if ( ! m_extractDirectoryNameLineEdit->hasAcceptableInput()) { + errorMessage.appendWithNewLine("Extract to Directory is invalid.

    "); + } + AString basePathErrorMessage; + if ( ! m_basePathWidget->isValid(basePathErrorMessage)) { + errorMessage.appendWithNewLine(basePathErrorMessage); } if (errorMessage.isEmpty()) { + m_sceneFile->setBalsaExtractToDirectoryName(extractToDirectoryName); + + if (m_sceneFile->isModified()) { + const QString msg("The scene file is modified and must be saved before continuing. Would you like " + "to save the scene file using its current name and continue?"); + if (WuQMessageBox::warningYesNo(this, msg)) { + try { + Brain* brain = GuiManager::get()->getBrain(); + brain->writeDataFile(m_sceneFile); + } + catch (const DataFileException& e) { + WuQMessageBox::errorOk(this, e.whatString()); + return; + } + } + else { + return; + } + } + + CursorDisplayScoped cursor; cursor.showWaitCursor(); @@ -202,3 +276,88 @@ ZipSceneFileDialog::okButtonClicked() WuQDialogModal::okButtonClicked(); } +/** + * Create a regular expression validatory for the give label/data. + * + * @param labelName + * 'Name' of label. + */ +QRegularExpressionValidator* +ZipSceneFileDialog::createValidator(const LabelName labelName) +{ + QRegularExpression regEx; + + switch (labelName) { + case LabelName::EXTRACT_DIRECTORY: + regEx.setPattern(".+"); + break; + case LabelName::ZIP_FILE: + regEx.setPattern(".+\\.zip$"); + break; + } + CaretAssert(regEx.isValid()); + + QRegularExpressionValidator* validator = new QRegularExpressionValidator(regEx, + this); + return validator; +} + +/** + * Set the label's text. + * + * @param label + * The label + * @param labelName + * 'Name' of the label + */ +void +ZipSceneFileDialog::setLabelText(const LabelName labelName) +{ + QLabel* label = NULL; + AString labelText; + bool validFlag = false; + switch (labelName) { + case LabelName::EXTRACT_DIRECTORY: + label = m_extractDirectoryLabel; + labelText = "Extract to Directory"; + validFlag = m_extractDirectoryNameLineEdit->hasAcceptableInput(); + break; + case LabelName::ZIP_FILE: + label = m_zipFileNameLabel; + labelText = "Zip File Name"; + validFlag = m_zipFileNameLineEdit->hasAcceptableInput(); + break; + } + + const bool textRedIfInvalid = true; + + AString coloredText; + if (validFlag) { + if (textRedIfInvalid) { + coloredText = (labelText + + ": "); + } + else { + coloredText = (" " + + labelText + + ": "); + } + } + else { + if (textRedIfInvalid) { + coloredText = ("" + + labelText + + ": "); + } + else { + coloredText = ("" + + labelText + + "*" + ": "); + } + } + + CaretAssert(label); + label->setText(coloredText); +} + diff --git a/src/GuiQt/ZipSceneFileDialog.h b/src/GuiQt/ZipSceneFileDialog.h index 5615f196daa81c2a75a7958f4e22b254aebee86e..963458f65f050b057ec9fdcbc8a3bf21a32db183 100644 --- a/src/GuiQt/ZipSceneFileDialog.h +++ b/src/GuiQt/ZipSceneFileDialog.h @@ -24,20 +24,22 @@ #include "WuQDialogModal.h" +class QLabel; class QLineEdit; - - +class QPushButton; +class QRegularExpressionValidator; namespace caret { class SceneFile; + class SceneBasePathWidget; class ZipSceneFileDialog : public WuQDialogModal { Q_OBJECT public: - ZipSceneFileDialog(const SceneFile* sceneFile, + ZipSceneFileDialog(SceneFile* sceneFile, QWidget* parent); virtual ~ZipSceneFileDialog(); @@ -52,17 +54,34 @@ namespace caret { private slots: void chooseZipFileButtonClicked(); + void validateData(); + private: + enum class LabelName { + EXTRACT_DIRECTORY, + ZIP_FILE + }; + ZipSceneFileDialog(const ZipSceneFileDialog&); ZipSceneFileDialog& operator=(const ZipSceneFileDialog&); - const SceneFile* m_sceneFile; + QRegularExpressionValidator* createValidator(const LabelName labelName); + + void setLabelText(const LabelName labelName); + + SceneFile* m_sceneFile; + + QLabel* m_zipFileNameLabel; QLineEdit* m_zipFileNameLineEdit; + QLabel* m_extractDirectoryLabel; + QLineEdit* m_extractDirectoryNameLineEdit; + SceneBasePathWidget* m_basePathWidget; + // ADD_NEW_MEMBERS_HERE }; diff --git a/src/Nifti/CMakeLists.txt b/src/Nifti/CMakeLists.txt index 198fda58f5ea00884d4ea328d9abe01f123b6259..3001f57abfe8e277d688fd36355563c04dba91a1 100644 --- a/src/Nifti/CMakeLists.txt +++ b/src/Nifti/CMakeLists.txt @@ -6,12 +6,17 @@ project (Nifti) # # Need XML from Qt # -SET(QT_DONT_USE_QTGUI) # # Add QT for includes # -INCLUDE (${QT_USE_FILE}) +if(Qt5_FOUND) + include_directories(${Qt5Core_INCLUDE_DIRS}) +endif() +IF (QT4_FOUND) + SET(QT_DONT_USE_QTGUI) + INCLUDE(${QT_USE_FILE}) +ENDIF () # @@ -29,6 +34,8 @@ NiftiHeader.cxx NiftiIO.cxx ) +TARGET_LINK_LIBRARIES(Nifti ${CARET_QT5_LINK}) + # # Find Headers # diff --git a/src/Nifti/Matrix4x4.cxx b/src/Nifti/Matrix4x4.cxx index 00f6648578c5a34be548d7f8a3925285c7e1f448..a4c4f86b4e768e670dcf4e0c188e2630ccf89569 100644 --- a/src/Nifti/Matrix4x4.cxx +++ b/src/Nifti/Matrix4x4.cxx @@ -895,11 +895,9 @@ Matrix4x4::rotate( m.matrix[0][2] = (xz + wy)*2; m.matrix[1][2] = (yz - wx)*2; m.matrix[2][2] = zz*2.0 + s; - m.fixNumericalError(); postmultiply(m); - this->fixNumericalError(); this->setModified(); } @@ -1206,7 +1204,6 @@ Matrix4x4::premultiply(const Matrix4x4& tm) + matrix[row][3] * tm.matrix[3][3]; } setMatrix(matrixOut); - this->fixNumericalError(); this->setModified(); } @@ -1239,7 +1236,6 @@ Matrix4x4::postmultiply(const Matrix4x4& tm) + tm.matrix[row][3] * matrix[3][3]; } setMatrix(matrixOut); - this->fixNumericalError(); this->setModified(); } diff --git a/src/Nifti/NiftiHeader.cxx b/src/Nifti/NiftiHeader.cxx index f1ef7633a98ce4b5c88fe93a2994873783f31cb7..21256e37594d043fe1707aecf78e4fd13b8c29c2 100644 --- a/src/Nifti/NiftiHeader.cxx +++ b/src/Nifti/NiftiHeader.cxx @@ -30,6 +30,7 @@ #include #include #include +#include "stdint.h" using namespace std; using namespace caret; @@ -244,7 +245,7 @@ vector > NiftiHeader::getSForm() const { for (int j = 0; j < 3; ++j) { - rotmat[i][j] *= m_header.pixdim[i + 1]; + rotmat[i][j] *= m_header.pixdim[j + 1]; } } if (m_header.pixdim[0] < 0.0f)//left handed coordinate system, flip the kvec @@ -264,7 +265,7 @@ vector > NiftiHeader::getSForm() const ret[1][3] = m_header.qoffset_y; ret[2][3] = m_header.qoffset_z; } else { - CaretLogWarning("found quaternion with length greater than 1 in nifti header"); + CaretLogWarning("found quaternion with length greater than 1 in nifti header, using ANALYZE coordinates!"); ret[0][0] = m_header.pixdim[1]; ret[1][1] = m_header.pixdim[2]; ret[2][2] = m_header.pixdim[3]; @@ -351,6 +352,16 @@ QString NiftiHeader::toString() const ret += "qoffset_y: " + QString::number(m_header.qoffset_y) + "\n"; ret += "qoffset_z: " + QString::number(m_header.qoffset_z) + "\n"; } + ret += "effective sform:\n"; + vector > tempSform = getSForm(); + for (int i = 0; i < 3; ++i) + { + for (int j = 0; j < 4; ++j) + { + ret += " " + QString::number(tempSform[i][j]); + } + ret += "\n"; + } ret += "xyzt_units: " + QString::number(m_header.xyzt_units) + "\n"; ret += "intent_code: " + QString::number(m_header.intent_code) + "\n"; ret += "intent_name: " + QByteArray(m_header.intent_name, 16);//same quirk @@ -438,9 +449,9 @@ void NiftiHeader::setSForm(const vector >& sForm) kvec = -kvec;//because to nifti, "left handed" apparently means "up is down", not "left is right" } float rotmat[3][3]; - rotmat[0][0] = ivec[0]; rotmat[1][0] = jvec[0]; rotmat[2][0] = kvec[0]; - rotmat[0][1] = ivec[1]; rotmat[1][1] = jvec[1]; rotmat[2][1] = kvec[1]; - rotmat[0][2] = ivec[2]; rotmat[1][2] = jvec[2]; rotmat[2][2] = kvec[2]; + rotmat[0][0] = ivec[0]; rotmat[1][0] = ivec[1]; rotmat[2][0] = ivec[2]; + rotmat[0][1] = jvec[0]; rotmat[1][1] = jvec[1]; rotmat[2][1] = jvec[2]; + rotmat[0][2] = kvec[0]; rotmat[1][2] = kvec[1]; rotmat[2][2] = kvec[2]; float quat[4]; if (!MathFunctions::matrixToQuatern(rotmat, quat)) { @@ -474,6 +485,145 @@ void NiftiHeader::setDataScaling(const double& mult, const double& offset) m_header.scl_inter = offset; } +namespace +{ + template + struct Scaling + { + double mult, offset; + Scaling(const double& minval, const double& maxval) + { + typedef std::numeric_limits mylimits; + double mymin = mylimits::lowest(); + mult = (maxval - minval) / ((double)mylimits::max() - mymin);//multiplying is the first step of decoding (after byteswap), so start with the range + offset = minval - mymin * mult;//offset is added after multiplying the encoded value by mult + } + }; +} + +void NiftiHeader::setDataTypeAndScaleRange(const int16_t& type, const double& minval, const double& maxval) +{ + setDataType(type); + switch (type) + { + case NIFTI_TYPE_RGB24: + clearDataScaling();//RGB ignores scaling fields + break; + case DT_BINARY://currently not supported in read/write functions anyway + setDataScaling(maxval - minval, minval);//make the two possible decoded values equal to the min and max + break; + case NIFTI_TYPE_INT8: + { + Scaling myscale(minval, maxval); + setDataScaling(myscale.mult, myscale.offset); + break; + } + case NIFTI_TYPE_UINT8: + { + Scaling myscale(minval, maxval); + setDataScaling(myscale.mult, myscale.offset); + break; + } + case NIFTI_TYPE_INT16: + { + Scaling myscale(minval, maxval); + setDataScaling(myscale.mult, myscale.offset); + break; + } + case NIFTI_TYPE_UINT16: + { + Scaling myscale(minval, maxval); + setDataScaling(myscale.mult, myscale.offset); + break; + } + case NIFTI_TYPE_INT32: + { + Scaling myscale(minval, maxval); + setDataScaling(myscale.mult, myscale.offset); + break; + } + case NIFTI_TYPE_UINT32: + { + Scaling myscale(minval, maxval); + setDataScaling(myscale.mult, myscale.offset); + break; + } + case NIFTI_TYPE_INT64: + { + Scaling myscale(minval, maxval); + setDataScaling(myscale.mult, myscale.offset); + break; + } + case NIFTI_TYPE_UINT64: + { + Scaling myscale(minval, maxval); + setDataScaling(myscale.mult, myscale.offset); + break; + } + case NIFTI_TYPE_FLOAT32: + case NIFTI_TYPE_COMPLEX64: + { + Scaling myscale(minval, maxval); + setDataScaling(myscale.mult, myscale.offset); + break; + } + case NIFTI_TYPE_FLOAT64: + case NIFTI_TYPE_COMPLEX128: + { + Scaling myscale(minval, maxval); + setDataScaling(myscale.mult, myscale.offset); + break; + } + case NIFTI_TYPE_FLOAT128: + case NIFTI_TYPE_COMPLEX256: + { + Scaling myscale(minval, maxval); + setDataScaling(myscale.mult, myscale.offset); + break; + } + default: + CaretAssert(0); + throw CaretException("internal error, report what you did to the developers"); + } +} + +int NiftiHeader::getNumComponents() const +{ + switch (getDataType()) + { + case NIFTI_TYPE_RGB24: + return 3; + break; + case NIFTI_TYPE_COMPLEX64: + case NIFTI_TYPE_COMPLEX128: + case NIFTI_TYPE_COMPLEX256: + return 2; + break; + case DT_BINARY: + case NIFTI_TYPE_INT8: + case NIFTI_TYPE_UINT8: + case NIFTI_TYPE_INT16: + case NIFTI_TYPE_UINT16: + case NIFTI_TYPE_INT32: + case NIFTI_TYPE_UINT32: + case NIFTI_TYPE_FLOAT32: + case NIFTI_TYPE_INT64: + case NIFTI_TYPE_UINT64: + case NIFTI_TYPE_FLOAT64: + case NIFTI_TYPE_FLOAT128: + return 1; + break; + default: + CaretAssert(0); + throw CaretException("internal error, report what you did to the developers"); + } +} + +bool NiftiHeader::hasGoodSpatialInformation() const +{ + return (m_header.sform_code != 0 || m_header.qform_code != 0); +} + void NiftiHeader::read(CaretBinaryFile& inFile) { nifti_1_header buffer1; @@ -481,6 +631,7 @@ void NiftiHeader::read(CaretBinaryFile& inFile) inFile.read(&buffer1, sizeof(nifti_1_header)); int version = NIFTI2_VERSION(buffer1); bool swapped = false; + Quirks myquirks; try { if (version == 2) @@ -492,14 +643,14 @@ void NiftiHeader::read(CaretBinaryFile& inFile) swapped = true; swapHeaderBytes(buffer2); } - setupFrom(buffer2); + myquirks = setupFrom(buffer2, inFile.getFilename()); } else if (version == 1) { if (NIFTI2_NEEDS_SWAP(buffer1))//yes, this works on nifti-1 also { swapped = true; swapHeaderBytes(buffer1); } - setupFrom(buffer1); + myquirks = setupFrom(buffer1, inFile.getFilename()); } else { throw DataFileException(inFile.getFilename() + " is not a valid NIfTI file"); } @@ -507,58 +658,74 @@ void NiftiHeader::read(CaretBinaryFile& inFile) throw DataFileException("error reading NIfTI file " + inFile.getFilename() + ": " + e.whatString()); } m_extensions.clear(); - char extender[4]; - inFile.read(extender, 4); - int extensions = 0;//if it has extensions in a format we don't know about, don't try to read them - if (version == 1 && extender[0] != 0) extensions = 1;//sadly, this is the only thing nifti-1 says about the extender bytes - if (version == 2 && extender[0] == 1 && extender[1] == 0 && extender[2] == 0 && extender[3] == 0) extensions = 1;//from http://nifti.nimh.nih.gov/nifti-2 as of 4/4/2014: - if (extensions == 1)//"extentions match those of NIfTI-1.1 when the extender bytes are 1 0 0 0" - { - int64_t extStart; - if (version == 1) - { - extStart = 352; - } else { - CaretAssert(version == 2); - extStart = 544; - } - CaretAssert(inFile.pos() == extStart); - while(extStart + 2 * sizeof(int32_t) <= (size_t)m_header.vox_offset) + if (myquirks.no_extender) + { + int min_offset = 352; + if (version == 2) min_offset = 544; + CaretLogWarning("in file '" + inFile.getFilename() + "', vox_offset is " + AString::number(m_header.vox_offset) + + ", nifti standard specifies that it should be at least " + AString::number(min_offset) + ", assuming malformed file with no extender"); + } else { + char extender[4]; + inFile.read(extender, 4); + int extensions = 0;//if it has extensions in a format we don't know about, don't try to read them + if (version == 1 && extender[0] != 0) extensions = 1;//sadly, this is the only thing nifti-1 says about the extender bytes + if (version == 2 && extender[0] == 1 && extender[1] == 0 && extender[2] == 0 && extender[3] == 0) extensions = 1;//from http://nifti.nimh.nih.gov/nifti-2 as of 4/4/2014: + if (extensions == 1)//"extentions match those of NIfTI-1.1 when the extender bytes are 1 0 0 0" { - int32_t esize, ecode; - inFile.read(&esize, sizeof(int32_t)); - if (swapped) ByteSwapping::swap(esize); - inFile.read(&ecode, sizeof(int32_t)); - if (swapped) ByteSwapping::swap(ecode); - if (esize < 8 || esize + extStart > m_header.vox_offset) break; - CaretPointer tempExtension(new NiftiExtension()); - if ((size_t)esize > 2 * sizeof(int32_t))//don't try to read 0 bytes + int64_t extStart; + if (version == 1) { - tempExtension->m_bytes.resize(esize - 2 * sizeof(int32_t)); - inFile.read(tempExtension->m_bytes.data(), esize - 2 * sizeof(int32_t)); + extStart = 352; + } else { + CaretAssert(version == 2); + extStart = 544; + } + CaretAssert(inFile.pos() == extStart); + while(extStart + 2 * sizeof(int32_t) <= (size_t)m_header.vox_offset) + { + int32_t esize, ecode; + inFile.read(&esize, sizeof(int32_t)); + if (swapped) ByteSwapping::swap(esize); + inFile.read(&ecode, sizeof(int32_t)); + if (swapped) ByteSwapping::swap(ecode); + if (esize < 8 || esize + extStart > m_header.vox_offset) break; + CaretPointer tempExtension(new NiftiExtension()); + if ((size_t)esize > 2 * sizeof(int32_t))//don't try to read 0 bytes + { + tempExtension->m_bytes.resize(esize - 2 * sizeof(int32_t)); + inFile.read(tempExtension->m_bytes.data(), esize - 2 * sizeof(int32_t)); + } + tempExtension->m_ecode = ecode; + m_extensions.push_back(tempExtension); + extStart += esize;//esize includes the two int32_ts } - tempExtension->m_ecode = ecode; - m_extensions.push_back(tempExtension); - extStart += esize;//esize includes the two int32_ts } } m_isSwapped = swapped;//now that we know there were no errors (because they throw), complete the internal state m_version = version; } -void NiftiHeader::setupFrom(const nifti_1_header& header) +NiftiHeader::Quirks NiftiHeader::setupFrom(const nifti_1_header& header, const AString& filename) { - if (header.sizeof_hdr != sizeof(nifti_1_header)) throw DataFileException("incorrect sizeof_hdr"); + Quirks ret; + if (header.sizeof_hdr != sizeof(nifti_1_header)) throw DataFileException(filename, "incorrect sizeof_hdr"); const char magic[] = "n+1\0";//only support single-file nifti - if (strncmp(header.magic, magic, 4) != 0) throw DataFileException("incorrect magic"); - if (header.dim[0] < 1 || header.dim[0] > 7) throw DataFileException("incorrect dim[0]"); + if (strncmp(header.magic, magic, 4) != 0) throw DataFileException(filename, "incorrect magic"); + if (header.dim[0] < 1 || header.dim[0] > 7) throw DataFileException(filename, "incorrect dim[0]"); for (int i = 0; i < header.dim[0]; ++i) { - if (header.dim[i + 1] < 1) throw DataFileException("dim[" + QString::number(i + 1) + "] < 1"); + if (header.dim[i + 1] < 1) throw DataFileException(filename, "dim[" + QString::number(i + 1) + "] < 1"); + } + if (header.vox_offset < 352) + { + if (header.vox_offset < 348) + { + throw DataFileException(filename, "invalid vox_offset: " + AString::number(header.vox_offset)); + } + ret.no_extender = true; } - if (header.vox_offset < 352) throw DataFileException("incorrect vox_offset"); int numBits = typeToNumBits(header.datatype); - if (header.bitpix != numBits) CaretLogWarning("datatype disagrees with bitpix"); + if (header.bitpix != numBits) CaretLogWarning("datatype disagrees with bitpix in file '" + filename + "'"); m_header.sizeof_hdr = header.sizeof_hdr;//copy in everything, so we don't have to fake anything to print the header as read for (int i = 0; i < 4; ++i)//mostly using nifti-2 field order to make it easier to find if things are missed { @@ -601,24 +768,27 @@ void NiftiHeader::setupFrom(const nifti_1_header& header) m_header.intent_code = header.intent_code; for (int i = 0; i < 16; ++i) m_header.intent_name[i] = header.intent_name[i]; m_header.dim_info = header.dim_info; + return ret; } -void NiftiHeader::setupFrom(const nifti_2_header& header) +NiftiHeader::Quirks NiftiHeader::setupFrom(const nifti_2_header& header, const AString& filename) { - if (header.sizeof_hdr != sizeof(nifti_2_header)) throw DataFileException("incorrect sizeof_hdr"); + Quirks ret; + if (header.sizeof_hdr != sizeof(nifti_2_header)) throw DataFileException(filename, "incorrect sizeof_hdr"); const char magic[] = "n+2\0\r\n\032\n";//only support single-file nifti for (int i = 0; i < 8; ++i) { - if (header.magic[i] != magic[i]) throw DataFileException("incorrect magic"); + if (header.magic[i] != magic[i]) throw DataFileException(filename, "incorrect magic"); } - if (header.dim[0] < 1 || header.dim[0] > 7) throw DataFileException("incorrect dim[0]"); + if (header.dim[0] < 1 || header.dim[0] > 7) throw DataFileException(filename, "incorrect dim[0]"); for (int i = 0; i < header.dim[0]; ++i) { - if (header.dim[i + 1] < 1) throw DataFileException("dim[" + QString::number(i + 1) + "] < 1"); + if (header.dim[i + 1] < 1) throw DataFileException(filename, "dim[" + QString::number(i + 1) + "] < 1"); } - if (header.vox_offset < 352) throw DataFileException("incorrect vox_offset"); - if (header.bitpix != typeToNumBits(header.datatype)) CaretLogWarning("datatype disagrees with bitpix"); + if (header.vox_offset < 544) throw DataFileException(filename, "incorrect vox_offset");//haven't noticed any nifti-2 with bad vox_offset yet, and all cifti files have a big extension, so they have to have used it correctly + if (header.bitpix != typeToNumBits(header.datatype)) CaretLogWarning("datatype disagrees with bitpix in file '" + filename + "'"); memcpy(&m_header, &header, sizeof(nifti_2_header)); + return ret; } int NiftiHeader::typeToNumBits(const int64_t& type) @@ -658,7 +828,7 @@ int NiftiHeader::typeToNumBits(const int64_t& type) return 256; break; default: - throw DataFileException("incorrect datatype code"); + throw DataFileException("incorrect nifti datatype code"); } } @@ -733,6 +903,12 @@ void NiftiHeader::swapHeaderBytes(nifti_2_header& header) void NiftiHeader::write(CaretBinaryFile& outFile, const int& version, const bool& swapEndian) { if (!canWriteVersion(version)) throw DataFileException("unable to write NIfTI version " + QString::number(version) + " for file " + outFile.getFilename()); + double junk1, junk2; + int16_t datatype = getDataType(); + if (getDataScaling(junk1, junk2) && ((datatype & 0x70) > 0 || datatype >= 1536)) + {//that hacky expression is to detect 16, 32, 64, 1536, 1792, and 2048 + CaretLogWarning("writing nifti file with scaling factor and floating point datatype"); + } const char padding[16] = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }; int64_t voxOffset; if (version == 2) diff --git a/src/Nifti/NiftiHeader.h b/src/Nifti/NiftiHeader.h index 228bdec18509730da0f10a5ed5900d0ca6c97969..cf733e8ec84e6042fae4f0ef9cd4158cef699b2c 100644 --- a/src/Nifti/NiftiHeader.h +++ b/src/Nifti/NiftiHeader.h @@ -60,6 +60,8 @@ namespace caret int32_t getIntentCode() const { return m_header.intent_code; } const char* getIntentName() const { return m_header.intent_name; }//NOTE: 16 BYTES, MAY NOT HAVE A NULL TERMINATOR bool getDataScaling(double& mult, double& offset) const;//returns false if scaling not needed + int getNumComponents() const; + bool hasGoodSpatialInformation() const; QString toString() const; void setDimensions(const std::vector& dimsIn); @@ -68,12 +70,19 @@ namespace caret void setDataType(const int16_t& type); void clearDataScaling(); void setDataScaling(const double& mult, const double& offset); + void setDataTypeAndScaleRange(const int16_t& type, const double& minval, const double& maxval); + ///get the FSL "scale" space std::vector > getFSLSpace() const; bool operator==(const NiftiHeader& rhs) const;//for testing purposes bool operator!=(const NiftiHeader& rhs) const { return !((*this) == rhs); } private: + struct Quirks + { + bool no_extender; + Quirks() { no_extender = false; } + }; nifti_2_header m_header;//storage for header values regardless of version int m_version; bool m_isSwapped; @@ -81,8 +90,8 @@ namespace caret static void swapHeaderBytes(nifti_2_header &header); void prepareHeader(nifti_1_header& header) const;//transform internal state into ready to write header struct void prepareHeader(nifti_2_header& header) const; - void setupFrom(const nifti_1_header& header);//error check provided header, and populate members from it - void setupFrom(const nifti_2_header& header); + Quirks setupFrom(const nifti_1_header& header, const AString& filename);//error check provided header, and populate members from it + Quirks setupFrom(const nifti_2_header& header, const AString& filename); static int typeToNumBits(const int64_t& type); int64_t computeVoxOffset(const int& version) const; }; diff --git a/src/Nifti/NiftiIO.cxx b/src/Nifti/NiftiIO.cxx index edf496b7b29338ebab73679352ee00fe2f9532d1..0d422f75529d74ce91078a43de423c71449f5b74 100644 --- a/src/Nifti/NiftiIO.cxx +++ b/src/Nifti/NiftiIO.cxx @@ -34,6 +34,16 @@ void NiftiIO::openRead(const QString& filename) throw DataFileException("file uses the binary datatype, which is unsupported: " + filename); } m_dims = m_header.getDimensions(); + int64_t filesize = m_file.size();//returns -1 if it can't efficiently determine size + int64_t elemCount = getNumComponents(); + for (int i = 0; i < (int)m_dims.size(); ++i) + { + elemCount *= m_dims[i]; + } + if (filesize >= 0 && filesize < m_header.getDataOffset() + numBytesPerElem() * elemCount) + { + throw DataFileException("nifti file is truncated: " + filename); + } } void NiftiIO::writeNew(const QString& filename, const NiftiHeader& header, const int& version, const bool& withRead, const bool& swapEndian) @@ -61,33 +71,7 @@ void NiftiIO::close() int NiftiIO::getNumComponents() const { - switch (m_header.getDataType()) - { - case NIFTI_TYPE_RGB24: - return 3; - break; - case NIFTI_TYPE_COMPLEX64: - case NIFTI_TYPE_COMPLEX128: - case NIFTI_TYPE_COMPLEX256: - return 2; - break; - case NIFTI_TYPE_INT8: - case NIFTI_TYPE_UINT8: - case NIFTI_TYPE_INT16: - case NIFTI_TYPE_UINT16: - case NIFTI_TYPE_INT32: - case NIFTI_TYPE_UINT32: - case NIFTI_TYPE_FLOAT32: - case NIFTI_TYPE_INT64: - case NIFTI_TYPE_UINT64: - case NIFTI_TYPE_FLOAT64: - case NIFTI_TYPE_FLOAT128: - return 1; - break; - default: - CaretAssert(0); - throw DataFileException("internal error, report what you did to the developers"); - } + return m_header.getNumComponents(); } int NiftiIO::numBytesPerElem() diff --git a/src/Nifti/NiftiIO.h b/src/Nifti/NiftiIO.h index cca7ddacf45d1728ced7fe75072a8f480db0a893..732a8b77bfd160d0ca7904c69a86e04bcb31b691 100644 --- a/src/Nifti/NiftiIO.h +++ b/src/Nifti/NiftiIO.h @@ -49,6 +49,8 @@ namespace caret void convertRead(TO* out, FROM* in, const int64_t& count);//for reading from file template void convertWrite(TO* out, const FROM* in, const int64_t& count);//for writing to file + template + static TO clamp(const FROM& in);//deal with integer cast being undefined when converting from outside range public: void openRead(const QString& filename); void writeNew(const QString& filename, const NiftiHeader& header, const int& version = 1, const bool& withRead = false, const bool& swapEndian = false); @@ -223,12 +225,12 @@ namespace caret { for (int64_t i = 0; i < count; ++i) { - out[i] = (TO)floor(0.5 + offset + mult * (long double)in[i]);//we don't always need that much precision, but it will still be faster than hard drives + out[i] = clamp(floor(0.5l + offset + mult * (long double)in[i]));//we don't always need that much precision, but it will still be faster than hard drives } } else { for (int64_t i = 0; i < count; ++i) { - out[i] = (TO)floor(0.5 + in[i]); + out[i] = clamp(floor(0.5 + in[i])); } } } else { @@ -253,17 +255,17 @@ namespace caret double mult, offset; bool doScale = m_header.getDataScaling(mult, offset); if (std::numeric_limits::is_integer)//do round to nearest when integer output type - { + {//TODO: what about NaN? if (doScale) { for (int64_t i = 0; i < count; ++i) { - out[i] = (TO)floor(0.5 + ((long double)in[i] - offset) / mult);//we don't always need that much precision, but it will still be faster than hard drives + out[i] = clamp(floor(0.5l + ((long double)in[i] - offset) / mult));//we don't always need that much precision, but it will still be faster than hard drives } } else { for (int64_t i = 0; i < count; ++i) { - out[i] = (TO)floor(0.5 + in[i]); + out[i] = clamp(floor(0.5 + in[i])); } } } else { @@ -283,6 +285,20 @@ namespace caret if (m_header.isSwapped()) ByteSwapping::swapArray(out, count); } + template + TO NiftiIO::clamp(const FROM& in) + { + typedef std::numeric_limits mylimits; + if (mylimits::max() < in) return mylimits::max(); + if (mylimits::lowest() > in) return mylimits::lowest(); + /*if (mylimits::is_integer)//here is a c++03 solution to missing ::lowest + { + if (mylimits::min() > in) return mylimits::min(); + } else { + if (-mylimits::max() > in) return -mylimits::max(); + }//*/ + return (TO)in; + } } #endif //__NIFTI_IO_H__ diff --git a/src/OSMesaDummy/CMakeLists.txt b/src/OSMesaDummy/CMakeLists.txt index 79438232b14b0b921ddabb3cb2b0a7fb272109d1..96ec583913b2338f4ce4f37f7df73ec60bb5ffd0 100644 --- a/src/OSMesaDummy/CMakeLists.txt +++ b/src/OSMesaDummy/CMakeLists.txt @@ -7,7 +7,9 @@ PROJECT(OSMesaDummy) # # Need Qt for reading from resource file. # -INCLUDE(${QT_USE_FILE}) +IF (QT4_FOUND) + INCLUDE(${QT_USE_FILE}) +ENDIF () # # Create a library @@ -17,10 +19,12 @@ OSMesaDummy.h OSMesaDummy.c ) +TARGET_LINK_LIBRARIES(OSMesaDummy ${CARET_QT5_LINK}) + # # Include directories # INCLUDE_DIRECTORIES( -${CMAKE_SOURCE_DIR}/Brain +${CMAKE_SOURCE_DIR}/Graphics ${CMAKE_SOURCE_DIR}/OSMesaDummy ) diff --git a/src/Operations/CMakeLists.txt b/src/Operations/CMakeLists.txt index 4cbebb8b066530a3d4fb70e9f2b963e3ccd359a3..e70d576ad0f8ee0488ae3d99678dab60f21b436f 100644 --- a/src/Operations/CMakeLists.txt +++ b/src/Operations/CMakeLists.txt @@ -3,15 +3,17 @@ # PROJECT (Operations) -# -# Need XML from Qt -# -SET(QT_DONT_USE_QTGUI) - # # Add QT for includes # -INCLUDE (${QT_USE_FILE}) +if(Qt5_FOUND) + include_directories(${Qt5Core_INCLUDE_DIRS}) + include_directories(${Qt5Gui_INCLUDE_DIRS}) +endif() +IF (QT4_FOUND) + SET(QT_DONT_USE_QTGUI) + INCLUDE(${QT_USE_FILE}) +ENDIF () # # Create the helper library @@ -88,6 +90,7 @@ OperationSurfaceGeodesicDistance.h OperationSurfaceGeodesicROIs.h OperationSurfaceInformation.h OperationSurfaceNormals.h +OperationSurfaceSetCoordinates.h OperationSurfaceVertexAreas.h OperationVolumeCapturePlane.h OperationVolumeCopyExtensions.h @@ -176,6 +179,7 @@ OperationSurfaceGeodesicDistance.cxx OperationSurfaceGeodesicROIs.cxx OperationSurfaceInformation.cxx OperationSurfaceNormals.cxx +OperationSurfaceSetCoordinates.cxx OperationSurfaceVertexAreas.cxx OperationVolumeCapturePlane.cxx OperationVolumeCopyExtensions.cxx @@ -194,6 +198,8 @@ OperationZipSceneFile.cxx OperationZipSpecFile.cxx ) +TARGET_LINK_LIBRARIES(Operations ${CARET_QT5_LINK}) + # # Find Headers # @@ -208,6 +214,7 @@ ${CMAKE_SOURCE_DIR}/Palette ${CMAKE_SOURCE_DIR}/FilesBase ${CMAKE_SOURCE_DIR}/Files ${CMAKE_SOURCE_DIR}/Gifti +${CMAKE_SOURCE_DIR}/Graphics ${CMAKE_SOURCE_DIR}/Cifti ${CMAKE_SOURCE_DIR}/Nifti ${QUAZIP_INCLUDE_DIRS} diff --git a/src/Operations/OperationCiftiConvert.cxx b/src/Operations/OperationCiftiConvert.cxx index b80b68000d0f53bd3fd58b11a13beb9f1d507870..be93392223cebc52fdb29ece01fd1b8635968271 100644 --- a/src/Operations/OperationCiftiConvert.cxx +++ b/src/Operations/OperationCiftiConvert.cxx @@ -75,6 +75,7 @@ OperationParameters* OperationCiftiConvert::getParameters() OptionalParameter* toNifti = ret->createOptionalParameter(3, "-to-nifti", "convert to NIFTI1"); toNifti->addCiftiParameter(1, "cifti-in", "the input cifti file"); toNifti->addVolumeOutputParameter(2, "nifti-out", "the output nifti file"); + toNifti->createOptionalParameter(3, "-smaller-file", "use better-fitting dimension lengths"); OptionalParameter* fromNifti = ret->createOptionalParameter(4, "-from-nifti", "convert a NIFTI (1 or 2) file made with this command back into CIFTI"); fromNifti->addVolumeParameter(1, "nifti-in", "the input nifti file"); @@ -293,20 +294,32 @@ void OperationCiftiConvert::useParameters(OperationParameters* myParams, Progres CiftiFile* myCiftiIn = toNifti->getCifti(1); if (myCiftiIn->getCiftiXML().getNumberOfDimensions() != 2) throw OperationException("conversion only supported for 2D cifti"); VolumeFile* myNiftiOut = toNifti->getOutputVolume(2); + bool betterDims = toNifti->getOptionalParameter(3)->m_present; vector outDims(4, 1); outDims[3] = myCiftiIn->getNumberOfColumns(); if (outDims[3] > numeric_limits::max()) throw OperationException("cifti rows are too long for nifti1, failing"); int64_t numRows = myCiftiIn->getNumberOfRows(); - int64_t temp = numRows; - int index = 0; - while (temp > numeric_limits::max()) - { - if (index > 1) throw OperationException("too many cifti rows for nifti1 spatial dimensions, failing"); - outDims[index] = numeric_limits::max(); - temp = (temp - 1) / numeric_limits::max() + 1;//round up - ++index; + const int64_t SHORTMAX = numeric_limits::max();//less ugly than writing it out every time + if (betterDims) + {//find the minimum needed third dimension, then compute from that how many elements the first two dimensions must encompass, etc + outDims[2] = (numRows - 1) / (SHORTMAX * SHORTMAX) + 1;//round up + if (outDims[2] > SHORTMAX) throw OperationException("too many cifti rows for nifti1 spatial dimensions, failing"); + int64_t temp = (numRows - 1) / outDims[2] + 1;//and again... + outDims[1] = (temp - 1) / SHORTMAX + 1; + outDims[0] = (temp - 1) / outDims[1] + 1; + } else { + int64_t temp = numRows; + int index = 0; + while (temp > SHORTMAX) + { + if (index > 1) throw OperationException("too many cifti rows for nifti1 spatial dimensions, failing"); + outDims[index] = SHORTMAX; + temp = (temp - 1) / SHORTMAX + 1;//round up + ++index; + } + outDims[index] = temp; } - outDims[index] = temp; + CaretAssert(outDims[0] * outDims[1] * outDims[2] >= numRows);//make sure we didn't screw up the math myNiftiOut->reinitialize(outDims, FloatMatrix::identity(4).getMatrix()); myNiftiOut->setValueAllVoxels(0.0f); int64_t ijk[3] = { 0, 0, 0 }; diff --git a/src/Operations/OperationCiftiCreateDenseFromTemplate.cxx b/src/Operations/OperationCiftiCreateDenseFromTemplate.cxx index 4fea76aab54d7129aeafa818f447cb849a3f9a17..f1b275990b915fcc90517d7ba0aee5f118ba17ee 100644 --- a/src/Operations/OperationCiftiCreateDenseFromTemplate.cxx +++ b/src/Operations/OperationCiftiCreateDenseFromTemplate.cxx @@ -262,15 +262,15 @@ void OperationCiftiCreateDenseFromTemplate::useParameters(OperationParameters* m { for (int whichStruct = 0; whichStruct < (int)thisVolStructs.size(); ++whichStruct) { - vector::const_iterator iter = std::find(volStructures.begin(), volStructures.end(), thisSurfStructs[whichStruct]); + vector::const_iterator iter = std::find(volStructures.begin(), volStructures.end(), thisVolStructs[whichStruct]); if (iter == volStructures.end()) { - cout << ("discarding volume structure " + StructureEnum::toName(thisSurfStructs[whichStruct]) + " from file '" + thisCifti->getFileName() + "'") << endl; + cout << ("discarding volume structure " + StructureEnum::toName(thisVolStructs[whichStruct]) + " from file '" + thisCifti->getFileName() + "'") << endl; } else { int outIndex = (int)(iter - volStructures.begin()); if (volInfo[outIndex].type != NONE) { - throw OperationException("cifti file '" + thisCifti->getFileName() + "' contains data for structure " + StructureEnum::toName(thisSurfStructs[whichStruct]) + + throw OperationException("cifti file '" + thisCifti->getFileName() + "' contains data for structure " + StructureEnum::toName(thisVolStructs[whichStruct]) + ", but data for this structure also exists in an another option's file"); } volInfo[outIndex].type = CIFTI; @@ -283,9 +283,10 @@ void OperationCiftiCreateDenseFromTemplate::useParameters(OperationParameters* m cout << ("discarding volume structures from file '" + thisCifti->getFileName() + "'") << endl; } } - if (ciftiNameFile == NULL && (isLabel || thisXML.getMappingType(CiftiXML::ALONG_ROW) == CiftiMappingType::SCALARS)) + if (ciftiNameFile == NULL && (isLabel || thisXML.getMappingType(CiftiXML::ALONG_ROW) == CiftiMappingType::SCALARS || + thisXML.getMappingType(CiftiXML::ALONG_ROW) == CiftiMappingType::PARCELS)) { - ciftiNameFile = thisCifti;//cifti trumps everything, if it has scalar or label + ciftiNameFile = thisCifti;//cifti trumps everything, if it has scalar or label or parcels } } const vector& metricInstances = *(myParams->getRepeatableParameterInstances(6)); @@ -477,10 +478,12 @@ void OperationCiftiCreateDenseFromTemplate::useParameters(OperationParameters* m outMap.setMapName(i, nameMap.getIndexName(i)); } } else { - CaretAssert(nameFile != NULL); - for (int64_t i = 0; i < numMaps; ++i) + if (nameFile != NULL)//if only input is cifti dtseries, there are no names to use { - outMap.setMapName(i, nameFile->getMapName(i)); + for (int64_t i = 0; i < numMaps; ++i) + { + outMap.setMapName(i, nameFile->getMapName(i)); + } } } outXML.setMap(CiftiXML::ALONG_ROW, outMap); @@ -495,10 +498,12 @@ void OperationCiftiCreateDenseFromTemplate::useParameters(OperationParameters* m outMap.setMapName(i, nameMap.getIndexName(i)); } } else { - CaretAssert(nameFile != NULL); - for (int64_t i = 0; i < numMaps; ++i) + if (nameFile != NULL) { - outMap.setMapName(i, nameFile->getMapName(i)); + for (int64_t i = 0; i < numMaps; ++i) + { + outMap.setMapName(i, nameFile->getMapName(i)); + } } } outXML.setMap(CiftiXML::ALONG_ROW, outMap); diff --git a/src/Operations/OperationCiftiPalette.cxx b/src/Operations/OperationCiftiPalette.cxx index 3de86ed371480ecb9d0eb394baf0a46ee09e1a42..28906df94abdded648b6db98f0bf48e63eeba1e9 100644 --- a/src/Operations/OperationCiftiPalette.cxx +++ b/src/Operations/OperationCiftiPalette.cxx @@ -87,6 +87,9 @@ OperationParameters* OperationCiftiPalette::getParameters() thresholdOpt->addDoubleParameter(3, "min", "lower threshold"); thresholdOpt->addDoubleParameter(4, "max", "upper threshold"); + OptionalParameter* inversionOpt = ret->createOptionalParameter(15, "-inversion", "specify palette inversion"); + inversionOpt->addStringParameter(1, "type", "the type of inversion"); + AString myText = AString("NOTE: The output file must be a different file than the input file.\n\n") + "For scalar maps, by default the palette is changed for every map, specify -column to change only one map. Palette settings not specified will be taken from the first column " + "for scalar maps, and from the existing file palette for other mapping types. " + @@ -118,6 +121,13 @@ OperationParameters* OperationCiftiPalette::getParameters() { myText += PaletteThresholdTestEnum::toName(myEnums3[i]) + "\n"; } + myText += "\nThe argument to -inversion must be one of the following:\n\n"; + vector myEnums4; + PaletteInvertModeEnum::getAllEnums(myEnums4); + for (int i = 0; i < (int)myEnums4.size(); ++i) + { + myText += PaletteInvertModeEnum::toName(myEnums4[i]) + "\n"; + } ret->setHelpText(myText); return ret; } @@ -243,6 +253,14 @@ void OperationCiftiPalette::useParameters(OperationParameters* myParams, Progres myMapping.setThresholdMinimum(mytype, thresholdOpt->getDouble(3)); myMapping.setThresholdMaximum(mytype, thresholdOpt->getDouble(4)); } + OptionalParameter* inversionOpt = myParams->getOptionalParameter(15); + if (inversionOpt->m_present) + { + bool ok = false; + PaletteInvertModeEnum::Enum inversionType = PaletteInvertModeEnum::fromName(inversionOpt->getString(1), &ok); + if (!ok) throw OperationException("unrecognized palette inversion type: " + inversionOpt->getString(1)); + myMapping.setInvertedMode(inversionType); + } if (myOutXML.getRowMappingType() == CIFTI_INDEX_TYPE_SCALARS) { if (myColumn == -1) diff --git a/src/Operations/OperationCiftiStats.cxx b/src/Operations/OperationCiftiStats.cxx index b62fb46556339bb6c7125a55b9f70cb4c9ce9a31..f3426b3349ffa35d601aa807f6460964c5d591f7 100644 --- a/src/Operations/OperationCiftiStats.cxx +++ b/src/Operations/OperationCiftiStats.cxx @@ -66,9 +66,9 @@ OperationParameters* OperationCiftiStats::getParameters() ret->createOptionalParameter(6, "-show-map-name", "print column index and name before each output"); ret->setHelpText( - AString("For each column of the input, a single number is printed, resulting from the specified reduction or percentile operation. ") + - "Use -column to only give output for a single column. " + - "Use -roi to consider only the data within a region. " + + AString("For each column of the input, a row of text is printed, resulting from the specified reduction or percentile operation. ") + + "If -roi is specified without -match-maps, then each row contains as many numbers as there are maps in the ROI file, separated by tab characters. " + + "Use -column to only give output for a single data column. " + "Exactly one of -reduce or -percentile must be specified.\n\n" + "The argument to the -reduce option must be one of the following:\n\n" + ReductionOperation::getHelpInfo()); @@ -166,6 +166,7 @@ void OperationCiftiStats::useParameters(OperationParameters* myParams, ProgressO vector roiData; bool matchColumnMode = false; CiftiFile* roiCifti = NULL; + int64_t numRois = 1;//trick: pretend we have 1 roi map when we don't have an roi file, for fewer special cases OptionalParameter* roiOpt = myParams->getOptionalParameter(5); if (roiOpt->m_present) { @@ -182,27 +183,34 @@ void OperationCiftiStats::useParameters(OperationParameters* myParams, ProgressO throw OperationException("-match-maps specified, but roi has different number of columns than input"); } matchColumnMode = true; - } else { - roiCifti->getColumn(roiData.data(), 0);//we are only getting one column, so go ahead and do it in on-disk mode } + numRois = roiCifti->getCiftiXML().getDimensionLength(CiftiXML::ALONG_ROW); } bool showMapName = myParams->getOptionalParameter(6)->m_present; const CiftiMappingType* rowMap = myXML.getMap(CiftiXML::ALONG_ROW); vector colScratch(colLength); + int64_t columnStart, columnEnd; if (useColumn == -1) { myInput->convertToInMemory();//we will be getting all columns, so read it all in first - if (matchColumnMode) + if (roiCifti != NULL) roiCifti->convertToInMemory();//ditto + columnStart = 0; + columnEnd = numCols; + } else { + if (roiCifti != NULL && !matchColumnMode) roiCifti->convertToInMemory();//matching maps with one column selected is the only time we don't need the whole ROI file + columnStart = useColumn; + columnEnd = useColumn + 1; + } + for (int i = columnStart; i < columnEnd; ++i) + { + myInput->getColumn(colScratch.data(), i); + if (showMapName) { - roiCifti->convertToInMemory();//ditto + cout << AString::number(i + 1) << ":\t" << rowMap->getIndexName(i) << ":\t"; } - for (int i = 0; i < numCols; ++i) - { - myInput->getColumn(colScratch.data(), i); - if (matchColumnMode) - { - roiCifti->getColumn(roiData.data(), i); - } + if (matchColumnMode) + {//trick: matchColumn is only true when we have an roi + roiCifti->getColumn(roiData.data(), i); float result; if (reduceOpt->m_present) { @@ -211,34 +219,27 @@ void OperationCiftiStats::useParameters(OperationParameters* myParams, ProgressO CaretAssert(percentileOpt->m_present); result = percentile(colScratch, percent, roiData); } - if (showMapName) - { - cout << AString::number(i + 1) << ": " << rowMap->getIndexName(i) << ": "; - } stringstream resultsstr; resultsstr << setprecision(7) << result; cout << resultsstr.str() << endl; - } - } else { - myInput->getColumn(colScratch.data(), useColumn); - if (matchColumnMode) - { - roiCifti->getColumn(roiData.data(), useColumn); - } - float result; - if (reduceOpt->m_present) - { - result = reduce(colScratch, myop, roiData); } else { - CaretAssert(percentileOpt->m_present); - result = percentile(colScratch, percent, roiData); - } - if (showMapName) - { - cout << AString::number(useColumn + 1) << ": " << rowMap->getIndexName(useColumn) << ": "; + for (int64_t j = 0; j < numRois; ++j) + { + if (roiCifti != NULL) roiCifti->getColumn(roiData.data(), j); + float result; + if (reduceOpt->m_present) + { + result = reduce(colScratch, myop, roiData); + } else { + CaretAssert(percentileOpt->m_present); + result = percentile(colScratch, percent, roiData); + } + stringstream resultsstr; + resultsstr << setprecision(7) << result; + if (j != 0) cout << "\t"; + cout << resultsstr.str(); + } + cout << endl; } - stringstream resultsstr; - resultsstr << setprecision(7) << result; - cout << resultsstr.str() << endl; } } diff --git a/src/Operations/OperationCiftiWeightedStats.cxx b/src/Operations/OperationCiftiWeightedStats.cxx index f9e5a9f1ee520b39dbf42a4019d486f20a19ee6a..9a9879740d03b5af7b84e504f24056d7dd43d539 100644 --- a/src/Operations/OperationCiftiWeightedStats.cxx +++ b/src/Operations/OperationCiftiWeightedStats.cxx @@ -332,6 +332,7 @@ void OperationCiftiWeightedStats::useParameters(OperationParameters* myParams, P vector roiData; bool matchColumnMode = false; CiftiFile* myRoi = NULL; + int64_t numRoiCols = -1; OptionalParameter* roiOpt = myParams->getOptionalParameter(5); if (roiOpt->m_present) { @@ -348,9 +349,8 @@ void OperationCiftiWeightedStats::useParameters(OperationParameters* myParams, P throw OperationException("-match-maps specified, but roi has different number of columns than input"); } matchColumnMode = true; - } else { - myRoi->getColumn(roiData.data(), 0);//again, while on disk if we are using only one column } + numRoiCols = myRoi->getCiftiXML().getDimensionLength(CiftiXML::ALONG_ROW); } bool haveOp = false; OperationType myop; @@ -391,182 +391,165 @@ void OperationCiftiWeightedStats::useParameters(OperationParameters* myParams, P bool showMapName = myParams->getOptionalParameter(10)->m_present; const CiftiMappingType* rowMap = myXML.getMap(CiftiXML::ALONG_ROW); vector inColumn(colLength); + int64_t columnStart, columnEnd; if (useColumn == -1) { myInput->convertToInMemory();//we will be getting all columns, so read it all in first - if (matchColumnMode) - { - myRoi->convertToInMemory();//ditto - } - if (myXML.getMappingType(CiftiXML::ALONG_COLUMN) == CiftiMappingType::BRAIN_MODELS) + if (myRoi != NULL) myRoi->convertToInMemory();//ditto + columnStart = 0; + columnEnd = numCols; + } else { + if (!matchColumnMode && myRoi != NULL) myRoi->convertToInMemory();//matching maps with one column selected is the only time we don't need the whole ROI file + columnStart = useColumn; + columnEnd = useColumn + 1; + } + if (myXML.getMappingType(CiftiXML::ALONG_COLUMN) == CiftiMappingType::BRAIN_MODELS) + { + const CiftiBrainModelsMap& myDenseMap = myXML.getBrainModelsMap(CiftiXML::ALONG_COLUMN); + vector myModels = myDenseMap.getModelInfo();//use this so that we get the start and end indices, so we don't have to use cifti separate + int numModels = (int)myModels.size(); + for (int64_t i = columnStart; i < columnEnd; ++i) { - const CiftiBrainModelsMap& myDenseMap = myXML.getBrainModelsMap(CiftiXML::ALONG_COLUMN); - vector myModels = myDenseMap.getModelInfo(); - int numModels = (int)myModels.size(); - for (int64_t i = 0; i < numCols; ++i) + myInput->getColumn(inColumn.data(), i); + if (showMapName) { - myInput->getColumn(inColumn.data(), i); - if (matchColumnMode) - { - myRoi->getColumn(roiData.data(), i); - } - if (showMapName) - { - cout << AString::number(i + 1) << ": " << rowMap->getIndexName(i) << ":" << endl; - } - for (int j = 0; j < numModels; ++j) + cout << AString::number(i + 1) << ":\t" << rowMap->getIndexName(i) << ":" << endl; + } + if (matchColumnMode) + { + myRoi->getColumn(roiData.data(), i);//retrieve the roi data once per map with match maps + } + for (int j = 0; j < numModels; ++j) + {//first, do the surfaces individually + if (myModels[j].m_type == CiftiBrainModelsMap::SURFACE) { - if (myModels[j].m_type == CiftiBrainModelsMap::SURFACE) + cout << StructureEnum::toName(myModels[j].m_structure) << ":\t"; + if (roiOpt->m_present) { - float result; - if (roiData.empty()) + if (matchColumnMode) { - result = doOperation(inColumn.data() + myModels[j].m_indexStart, - combinedWeights.data() + myModels[j].m_indexStart, - myModels[j].m_indexCount, - myop, - NULL, - argument); + float result = doOperation(inColumn.data() + myModels[j].m_indexStart, + combinedWeights.data() + myModels[j].m_indexStart, + myModels[j].m_indexCount, + myop, + roiData.data() + myModels[j].m_indexStart, + argument); + stringstream resultsstr; + resultsstr << setprecision(7) << result; + cout << resultsstr.str(); } else { - result = doOperation(inColumn.data() + myModels[j].m_indexStart, - combinedWeights.data() + myModels[j].m_indexStart, - myModels[j].m_indexCount, - myop, - roiData.data() + myModels[j].m_indexStart, - argument); + for (int k = 0; k < numRoiCols; ++k) + { + myRoi->getColumn(roiData.data(), k); + float result = doOperation(inColumn.data() + myModels[j].m_indexStart, + combinedWeights.data() + myModels[j].m_indexStart, + myModels[j].m_indexCount, + myop, + roiData.data() + myModels[j].m_indexStart, + argument); + stringstream resultsstr; + resultsstr << setprecision(7) << result; + if (k != 0) cout << "\t"; + cout << resultsstr.str(); + } } + } else { + float result = doOperation(inColumn.data() + myModels[j].m_indexStart, + combinedWeights.data() + myModels[j].m_indexStart, + myModels[j].m_indexCount, + myop, + NULL, + argument); stringstream resultsstr; resultsstr << setprecision(7) << result; - cout << StructureEnum::toName(myModels[j].m_structure) << ": " << resultsstr.str() << endl; + cout << resultsstr.str(); } + cout << endl; } - vector volMap = myDenseMap.getFullVolumeMap(); - int64_t mapSize = (int64_t)volMap.size(); - if (mapSize > 0) + }//now do volume as one chunk + vector volMap = myDenseMap.getFullVolumeMap(); + int64_t mapSize = (int64_t)volMap.size(); + if (mapSize > 0) + { + vector volData(mapSize), weightVolData(mapSize); + for (int64_t j = 0; j < mapSize; ++j) { - vector volData(mapSize), weightVolData(mapSize), roiVolData(mapSize); - for (int64_t j = 0; j < mapSize; ++j) + volData[j] = inColumn[volMap[j].m_ciftiIndex]; + weightVolData[j] = combinedWeights[volMap[j].m_ciftiIndex]; + } + cout << "VOLUME:\t"; + if (roiOpt->m_present) + { + vector roiVolData(mapSize); + if (matchColumnMode) { - volData[j] = inColumn[volMap[j].m_ciftiIndex]; - weightVolData[j] = combinedWeights[volMap[j].m_ciftiIndex]; - if (!roiData.empty()) + for (int64_t j = 0; j < mapSize; ++j) { roiVolData[j] = roiData[volMap[j].m_ciftiIndex]; } - } - float result; - if (roiData.empty()) - { - result = doOperation(volData.data(), weightVolData.data(), mapSize, myop, NULL, argument); + float result = doOperation(volData.data(), weightVolData.data(), mapSize, myop, roiVolData.data(), argument); + stringstream resultsstr; + resultsstr << setprecision(7) << result; + cout << resultsstr.str(); } else { - result = doOperation(volData.data(), weightVolData.data(), mapSize, myop, roiVolData.data(), argument); + for (int k = 0; k < numRoiCols; ++k) + { + myRoi->getColumn(roiData.data(), k); + for (int64_t j = 0; j < mapSize; ++j) + { + roiVolData[j] = roiData[volMap[j].m_ciftiIndex]; + } + float result = doOperation(volData.data(), weightVolData.data(), mapSize, myop, roiVolData.data(), argument); + stringstream resultsstr; + resultsstr << setprecision(7) << result; + if (k != 0) cout << "\t"; + cout << resultsstr.str(); + } } + } else { + float result = doOperation(volData.data(), weightVolData.data(), mapSize, myop, NULL, argument); stringstream resultsstr; resultsstr << setprecision(7) << result; - cout << "VOLUME: " << resultsstr.str() << endl; - } - } - } else { - for (int64_t i = 0; i < numCols; ++i) - { - myInput->getColumn(inColumn.data(), i); - float result; - if (roiData.empty()) - { - result = doOperation(inColumn.data(), combinedWeights.data(), colLength, myop, NULL, argument); - } else { - result = doOperation(inColumn.data(), combinedWeights.data(), colLength, myop, roiData.data(), argument); + cout << resultsstr.str(); } - if (showMapName) - { - cout << AString::number(i + 1) << ": " << rowMap->getIndexName(i) << ": "; - } - stringstream resultsstr; - resultsstr << setprecision(7) << result; - cout << resultsstr.str() << endl; + cout << endl; } } } else { - myInput->getColumn(inColumn.data(), useColumn); - if (matchColumnMode) - { - myRoi->getColumn(roiData.data(), useColumn); - } - if (myXML.getMappingType(CiftiXML::ALONG_COLUMN) == CiftiMappingType::BRAIN_MODELS) + for (int64_t i = columnStart; i < columnEnd; ++i) { - const CiftiBrainModelsMap& myDenseMap = myXML.getBrainModelsMap(CiftiXML::ALONG_COLUMN); - vector myModels = myDenseMap.getModelInfo(); - int numModels = (int)myModels.size(); + myInput->getColumn(inColumn.data(), i); if (showMapName) { - cout << AString::number(useColumn + 1) << ": " << rowMap->getIndexName(useColumn) << ":" << endl; + cout << AString::number(i + 1) << ":\t" << rowMap->getIndexName(i) << ":\t"; } - for (int j = 0; j < numModels; ++j) + if (roiOpt->m_present) { - if (myModels[j].m_type == CiftiBrainModelsMap::SURFACE) + if (matchColumnMode) { - float result; - if (roiData.empty()) - { - result = doOperation(inColumn.data() + myModels[j].m_indexStart, - combinedWeights.data() + myModels[j].m_indexStart, - myModels[j].m_indexCount, - myop, - NULL, - argument); - } else { - result = doOperation(inColumn.data() + myModels[j].m_indexStart, - combinedWeights.data() + myModels[j].m_indexStart, - myModels[j].m_indexCount, - myop, - roiData.data() + myModels[j].m_indexStart, - argument); - } + myRoi->getColumn(roiData.data(), i);//retrieve the roi data once per map with match maps + float result = doOperation(inColumn.data(), combinedWeights.data(), colLength, myop, roiData.data(), argument); stringstream resultsstr; resultsstr << setprecision(7) << result; - cout << StructureEnum::toName(myModels[j].m_structure) << ": " << resultsstr.str() << endl; - } - } - vector volMap = myDenseMap.getFullVolumeMap(); - int64_t mapSize = (int64_t)volMap.size(); - if (mapSize > 0) - { - vector volData(mapSize), weightVolData(mapSize), roiVolData(mapSize); - for (int64_t j = 0; j < mapSize; ++j) - { - volData[j] = inColumn[volMap[j].m_ciftiIndex]; - weightVolData[j] = combinedWeights[volMap[j].m_ciftiIndex]; - if (!roiData.empty()) + cout << resultsstr.str(); + } else { + for (int k = 0; k < numRoiCols; ++k) { - roiVolData[j] = roiData[volMap[j].m_ciftiIndex]; + myRoi->getColumn(roiData.data(), k); + float result = doOperation(inColumn.data(), combinedWeights.data(), colLength, myop, roiData.data(), argument); + stringstream resultsstr; + resultsstr << setprecision(7) << result; + if (k != 0) cout << "\t"; + cout << resultsstr.str(); } } - float result; - if (roiData.empty()) - { - result = doOperation(volData.data(), weightVolData.data(), mapSize, myop, NULL, argument); - } else { - result = doOperation(volData.data(), weightVolData.data(), mapSize, myop, roiVolData.data(), argument); - } + } else { + float result = doOperation(inColumn.data(), combinedWeights.data(), colLength, myop, NULL, argument); stringstream resultsstr; resultsstr << setprecision(7) << result; - cout << "VOLUME: " << resultsstr.str() << endl; - } - } else { - float result; - if (roiData.empty()) - { - result = doOperation(inColumn.data(), combinedWeights.data(), colLength, myop, NULL, argument); - } else { - result = doOperation(inColumn.data(), combinedWeights.data(), colLength, myop, roiData.data(), argument); - } - if (showMapName) - { - cout << AString::number(useColumn + 1) << ": " << rowMap->getIndexName(useColumn) << ": "; + cout << resultsstr.str(); } - stringstream resultsstr; - resultsstr << setprecision(7) << result; - cout << resultsstr.str() << endl; + cout << endl; } } } diff --git a/src/Operations/OperationConvertAffine.cxx b/src/Operations/OperationConvertAffine.cxx index 21076bed799e3d17a3af5e8e7674be5783c1e72e..7055b857b4f0de90507de20dab886336c3aabb60 100644 --- a/src/Operations/OperationConvertAffine.cxx +++ b/src/Operations/OperationConvertAffine.cxx @@ -41,6 +41,10 @@ OperationParameters* OperationConvertAffine::getParameters() OptionalParameter* fromWorld = ret->createOptionalParameter(1, "-from-world", "input is a NIFTI 'world' affine"); fromWorld->addStringParameter(1, "input", "the input affine"); + fromWorld->createOptionalParameter(2, "-inverse", "for files that use 'target to source' convention"); + + OptionalParameter* fromITK = ret->createOptionalParameter(5, "-from-itk", "input is an ITK matrix"); + fromITK->addStringParameter(1, "input", "the input affine"); OptionalParameter* fromFlirt = ret->createOptionalParameter(2, "-from-flirt", "input is a flirt matrix"); fromFlirt->addStringParameter(1, "input", "the input affine"); @@ -49,6 +53,10 @@ OperationParameters* OperationConvertAffine::getParameters() OptionalParameter* toWorld = ret->createOptionalParameter(3, "-to-world", "write output as a NIFTI 'world' affine"); toWorld->addStringParameter(1, "output", "output - the output affine");//HACK: fake the output formatting, since we don't have a parameter for affine file (hard to do due to multiple on-disk formats) + toWorld->createOptionalParameter(2, "-inverse", "write file using 'target to source' convention"); + + OptionalParameter* toITK = ret->createOptionalParameter(6, "-to-itk", "write output as an ITK affine"); + toITK->addStringParameter(1, "output", "output - the output affine"); ParameterComponent* toFlirt = ret->createRepeatableParameter(4, "-to-flirt", "write output as a flirt matrix"); toFlirt->addStringParameter(1, "output", "output - the output affine"); @@ -57,8 +65,11 @@ OperationParameters* OperationConvertAffine::getParameters() ret->setHelpText( AString("NIFTI world matrices can be used directly on mm coordinates via matrix multiplication, they use the NIFTI coordinate system, that is, ") + - "positive X is right, positive Y is anterior, and positive Z is superior.\n\n" + - "You must specify exactly one -from option, but you may specify multiple -to options, and any -to option that takes volumes may be specified more than once." + "positive X is right, positive Y is anterior, and positive Z is superior. " + + "Note that wb_command assumes that world matrices transform source coordinates to target coordinates, " + + "while other tools may use affines that transform target coordinates to source coordinates.\n\n" + + "The ITK format is used by ANTS.\n\n" + + "You must specify exactly one -from option, but you may specify multiple -to options, and -to-flirt may be specified more than once." ); return ret; } @@ -72,7 +83,7 @@ void OperationConvertAffine::useParameters(OperationParameters* myParams, Progre if (fromWorld->m_present) { haveInput = true; - myAffine.readWorld(fromWorld->getString(1)); + myAffine.readWorld(fromWorld->getString(1), fromWorld->getOptionalParameter(2)->m_present); } OptionalParameter* fromFlirt = myParams->getOptionalParameter(2); if (fromFlirt->m_present) @@ -81,11 +92,18 @@ void OperationConvertAffine::useParameters(OperationParameters* myParams, Progre haveInput = true; myAffine.readFlirt(fromFlirt->getString(1), fromFlirt->getString(2), fromFlirt->getString(3)); } + OptionalParameter* fromITK = myParams->getOptionalParameter(5); + if (fromITK->m_present) + { + if (haveInput) throw OperationException("only one -from option may be specified"); + haveInput = true; + myAffine.readITK(fromITK->getString(1)); + } if (!haveInput) throw OperationException("you must specify a -from option"); OptionalParameter* toWorld = myParams->getOptionalParameter(3); if (toWorld->m_present) { - myAffine.writeWorld(toWorld->getString(1)); + myAffine.writeWorld(toWorld->getString(1), toWorld->getOptionalParameter(2)->m_present); } const vector& toFlirt = *(myParams->getRepeatableParameterInstances(4));//the return of this is a pointer so that it can return NULL if the key is wrong, after asserting int numFlirt = (int)toFlirt.size();//so, dereference immediately since it should be caught in debug via assert @@ -93,4 +111,9 @@ void OperationConvertAffine::useParameters(OperationParameters* myParams, Progre { myAffine.writeFlirt(toFlirt[i]->getString(1), toFlirt[i]->getString(2), toFlirt[i]->getString(3)); } + OptionalParameter* toITK = myParams->getOptionalParameter(6); + if (toITK->m_present) + { + myAffine.writeITK(toITK->getString(1)); + } } diff --git a/src/Operations/OperationConvertMatrix4ToMatrix2.cxx b/src/Operations/OperationConvertMatrix4ToMatrix2.cxx index e276ea70452360406d53657367a644f1cb43ca47..2dbe3c845e7c2d0c53943b1318a8e8af7953b8f6 100644 --- a/src/Operations/OperationConvertMatrix4ToMatrix2.cxx +++ b/src/Operations/OperationConvertMatrix4ToMatrix2.cxx @@ -47,8 +47,14 @@ OperationParameters* OperationConvertMatrix4ToMatrix2::getParameters() OptionalParameter* distanceOpt = ret->createOptionalParameter(3, "-distances", "output average trajectory distance"); distanceOpt->addCiftiOutputParameter(1, "distance-out", "the distances, as a cifti file"); + OptionalParameter* fibersOpt = ret->createOptionalParameter(4, "-individual-fibers", "output files for each fiber direction"); + fibersOpt->addCiftiOutputParameter(1, "fiber-1", "output file for first fiber"); + fibersOpt->addCiftiOutputParameter(2, "fiber-2", "output file for second fiber"); + fibersOpt->addCiftiOutputParameter(3, "fiber-3", "output file for third fiber"); + ret->setHelpText( - AString("This command makes a cifti file from the fiber counts in a matrix4 wbsparse file, and optionally a second cifti file from the distances.") + AString("This command makes a cifti file from the fiber counts in a matrix4 wbsparse file, and optionally a second cifti file from the distances. ") + + "Note that while the total count is stored exactly, the per-fiber counts are stored as approximate fractions, so the output of -individual-fibers will contain nonintegers." ); return ret; } @@ -64,23 +70,51 @@ void OperationConvertMatrix4ToMatrix2::useParameters(OperationParameters* myPara { myDistOut = distanceOpt->getOutputCifti(1); } + CiftiFile* fiber1 = NULL, *fiber2 = NULL, *fiber3 = NULL; + OptionalParameter* fibersOpt = myParams->getOptionalParameter(4); + if (fibersOpt->m_present) + { + fiber1 = fibersOpt->getOutputCifti(1); + fiber2 = fibersOpt->getOutputCifti(2); + fiber3 = fibersOpt->getOutputCifti(3); + } CaretSparseFile matrix4(matrix4Name); const CiftiXML& myXML = matrix4.getCiftiXML(); myCountsOut->setCiftiXML(myXML); if (myDistOut != NULL) myDistOut->setCiftiXML(myXML); + if (fiber1 != NULL) + { + fiber1->setCiftiXML(myXML); + fiber2->setCiftiXML(myXML); + fiber3->setCiftiXML(myXML); + } int rowSize = myXML.getDimensionLength(CiftiXML::ALONG_ROW), numRows = myXML.getDimensionLength(CiftiXML::ALONG_COLUMN); - vector scratchRow(rowSize, 0.0f); - vector indices; - vector fibers; for (int i = 0; i < numRows; ++i) { + vector scratchRow(rowSize, 0.0f); + vector scratchFiber1 = scratchRow, scratchFiber2 = scratchRow, scratchFiber3 = scratchRow; + vector indices; + vector fibers; matrix4.getFibersRowSparse(i, indices, fibers); int numIndices = (int)indices.size(); for (int j = 0; j < numIndices; ++j) { scratchRow[indices[j]] = fibers[j].totalCount; + if (fiber1 != NULL) + { + CaretAssert(fibers[j].fiberFractions.size() == 3); + scratchFiber1[indices[j]] = fibers[j].totalCount * fibers[j].fiberFractions[0]; + scratchFiber2[indices[j]] = fibers[j].totalCount * fibers[j].fiberFractions[1]; + scratchFiber3[indices[j]] = fibers[j].totalCount * fibers[j].fiberFractions[2]; + } } myCountsOut->setRow(scratchRow.data(), i); + if (fiber1 != NULL) + { + fiber1->setRow(scratchFiber1.data(), i); + fiber2->setRow(scratchFiber2.data(), i); + fiber3->setRow(scratchFiber3.data(), i); + } if (myDistOut != NULL) { for (int j = 0; j < numIndices; ++j) @@ -89,9 +123,5 @@ void OperationConvertMatrix4ToMatrix2::useParameters(OperationParameters* myPara } myDistOut->setRow(scratchRow.data(), i); } - for (int j = 0; j < numIndices; ++j) - { - scratchRow[indices[j]] = 0.0f; - } } } diff --git a/src/Operations/OperationConvertWarpfield.cxx b/src/Operations/OperationConvertWarpfield.cxx index 4cae21659d8ba01b7ff4f3cce7fb816bf78e3eab..a606cf5ae7fb42d7a605ea2e98e198fe8e412e5e 100644 --- a/src/Operations/OperationConvertWarpfield.cxx +++ b/src/Operations/OperationConvertWarpfield.cxx @@ -42,22 +42,30 @@ OperationParameters* OperationConvertWarpfield::getParameters() OptionalParameter* fromWorld = ret->createOptionalParameter(1, "-from-world", "input is a NIFTI 'world' warpfield"); fromWorld->addStringParameter(1, "input", "the input warpfield"); + OptionalParameter* fromITK = ret->createOptionalParameter(5, "-from-itk", "input is an ITK warpfield"); + fromITK->addStringParameter(1, "input", "the input warpfield"); + OptionalParameter* fromFnirt = ret->createOptionalParameter(2, "-from-fnirt", "input is a fnirt warpfield"); fromFnirt->addStringParameter(1, "input", "the input warpfield"); fromFnirt->addStringParameter(2, "source-volume", "the source volume used when generating the input warpfield"); + fromFnirt->createOptionalParameter(3, "-absolute", "warpfield was written in absolute convention, rather than relative"); OptionalParameter* toWorld = ret->createOptionalParameter(3, "-to-world", "write output as a NIFTI 'world' warpfield"); toWorld->addStringParameter(1, "output", "output - the output warpfield");//HACK: fake the output formatting, since the warpfield uses separate calls for writing each output type, and by design doesn't give access to the raw with-quirks volume file + OptionalParameter* toITK = ret->createOptionalParameter(6, "-to-itk", "write output as an ITK warpfield"); + toITK->addStringParameter(1, "output", "output - the output warpfield"); + ParameterComponent* toFnirt = ret->createRepeatableParameter(4, "-to-fnirt", "write output as a fnirt warpfield"); toFnirt->addStringParameter(1, "output", "output - the output warpfield");//this argument order is different than other commands, yes - but don't change it, so scripts will still work toFnirt->addStringParameter(2, "source-volume", "the volume you want to apply the warpfield to"); ret->setHelpText( AString("NIFTI world warpfields can be used directly on mm coordinates via sampling the three subvolumes at the coordinate ") + - "and adding the sampled values to the coordinate vector, they use the NIFTI coordinate system, that is, " + - "X is left to right, Y is posterior to anterior, and Z is inferior to superior.\n\n" + + "and adding the sampled values to the coordinate vector. " + + "They use the NIFTI coordinate system, that is, X is left to right, Y is posterior to anterior, and Z is inferior to superior.\n\n" + "NOTE: this command does not invert the warpfield, and to warp a surface, you must use the inverse of the warpfield that warps the corresponding volume.\n\n" + + "The ITK format is used by ANTS.\n\n" + "You must specify exactly one -from option, but you may specify multiple -to options, and -to-fnirt may be specified more than once." ); return ret; @@ -69,11 +77,17 @@ void OperationConvertWarpfield::useParameters(OperationParameters* myParams, Pro WarpfieldFile myWarp; bool haveInput = false; OptionalParameter* fromWorld = myParams->getOptionalParameter(1); + OptionalParameter* fromITK = myParams->getOptionalParameter(5); OptionalParameter* fromFnirt = myParams->getOptionalParameter(2); if (fromWorld->m_present) { haveInput = true; } + if (fromITK->m_present) + { + if (haveInput) throw OperationException("only one -from option may be specified"); + haveInput = true; + } if (fromFnirt->m_present) { if (haveInput) throw OperationException("only one -from option may be specified"); @@ -84,15 +98,24 @@ void OperationConvertWarpfield::useParameters(OperationParameters* myParams, Pro { myWarp.readWorld(fromWorld->getString(1)); } + if (fromITK->m_present) + { + myWarp.readITK(fromITK->getString(1)); + } if (fromFnirt->m_present) { - myWarp.readFnirt(fromFnirt->getString(1), fromFnirt->getString(2)); + myWarp.readFnirt(fromFnirt->getString(1), fromFnirt->getString(2), fromFnirt->getOptionalParameter(3)->m_present); } OptionalParameter* toWorld = myParams->getOptionalParameter(3); + OptionalParameter* toITK = myParams->getOptionalParameter(6); if (toWorld->m_present) { myWarp.writeWorld(toWorld->getString(1)); } + if (toITK->m_present) + { + myWarp.writeITK(toITK->getString(1)); + } const vector& toFnirt = *(myParams->getRepeatableParameterInstances(4));//the return of this is a pointer so that it can return NULL if the key is wrong, after asserting int numFnirt = (int)toFnirt.size();//so, dereference immediately since it should be caught in debug via assert for (int i = 0; i < numFnirt; ++i) diff --git a/src/Operations/OperationFileInformation.cxx b/src/Operations/OperationFileInformation.cxx index 94f998885b137214ad8ec83b2f98aab650a872ec..d22a9770ec4b481a2f81eb847938b763930b5a94 100644 --- a/src/Operations/OperationFileInformation.cxx +++ b/src/Operations/OperationFileInformation.cxx @@ -27,6 +27,7 @@ #include "CaretMappableDataFile.h" #include "CaretPointer.h" #include "CiftiMappableDataFile.h" +#include "CiftiXML.h" #include "DataFileContentInformation.h" #include "DataFileException.h" #include "GiftiMetaData.h" @@ -81,6 +82,8 @@ OperationFileInformation::getParameters() OptionalParameter* mdKeyOpt = metadataOpt->createOptionalParameter(1, "-key", "only print the metadata for one key, with no formatting"); mdKeyOpt->addStringParameter(1, "key", "the metadata key"); + ret->createOptionalParameter(7, "-only-cifti-xml", "suppress normal output, print the cifti xml if the file type has it"); + AString helpText("List information about the content of a data file. " "Only one -only option may be specified. " "The information listed when no -only option is present is dependent upon the type of data file."); @@ -130,6 +133,9 @@ OperationFileInformation::useParameters(OperationParameters* myParams, } } + bool onlyCiftiXML = myParams->getOptionalParameter(7)->m_present; + if (onlyCiftiXML) ++countOnlys; + if (countOnlys > 1) throw OperationException("only one -only-* option may be specified"); bool preferOnDisk = (!showMapInformationFlag || countOnlys != 0); @@ -185,6 +191,15 @@ OperationFileInformation::useParameters(OperationParameters* myParams, cout << myMD->get(mdKey) << endl; } } + if (onlyCiftiXML) + { + CaretMappableDataFile* mappableFile = dynamic_cast(caretDataFile.getPointer()); + if (mappableFile != NULL && mappableFile->hasCiftiXML()) + { + const CiftiXML& myXML = mappableFile->getCiftiXML(); + cout << myXML.writeXMLToString(myXML.getParsedVersion()) << endl; + } + } if (countOnlys == 0) { DataFileContentInformation dataFileContentInformation; diff --git a/src/Operations/OperationLabelMask.cxx b/src/Operations/OperationLabelMask.cxx index 7e4818e228f7f7cfa3f3b94ccc6ef15233448139..6b163857d8cc407f06aee4cc064b4de53779044a 100644 --- a/src/Operations/OperationLabelMask.cxx +++ b/src/Operations/OperationLabelMask.cxx @@ -46,7 +46,7 @@ OperationParameters* OperationLabelMask::getParameters() OptionalParameter* columnSelect = ret->createOptionalParameter(4, "-column", "select a single column"); columnSelect->addStringParameter(1, "column", "the column number or name"); ret->setHelpText( - AString("By default, the output label is a copy of the input label, but with the 'unused' label wherever the mask metric is not positive. ") + + AString("By default, the output label is a copy of the input label, but with the 'unused' label wherever the mask metric is zero or negative. ") + "if -column is specified, the output contains only one column, the masked version of the specified input column." ); return ret; diff --git a/src/Operations/OperationMetricMask.cxx b/src/Operations/OperationMetricMask.cxx index 56ecaecd99ef63c2b4cfb28df449aff4ce8e9c5c..ae3042d238084831af177c0c15504deb9e2be0bc 100644 --- a/src/Operations/OperationMetricMask.cxx +++ b/src/Operations/OperationMetricMask.cxx @@ -45,7 +45,7 @@ OperationParameters* OperationMetricMask::getParameters() OptionalParameter* columnSelect = ret->createOptionalParameter(4, "-column", "select a single column"); columnSelect->addStringParameter(1, "column", "the column number or name"); ret->setHelpText( - AString("By default, the output metric is a copy of the input metric, but with zeros wherever the mask metric is not positive. ") + + AString("By default, the output metric is a copy of the input metric, but with zeros wherever the mask metric is zero or negative. ") + "if -column is specified, the output contains only one column, the masked version of the specified input column." ); return ret; diff --git a/src/Operations/OperationMetricMath.cxx b/src/Operations/OperationMetricMath.cxx index 0957b91394e5602e6fc2ca99c281b813f989e67b..6b0aac59df6f4b53594399cea2c95f169696e8e7 100644 --- a/src/Operations/OperationMetricMath.cxx +++ b/src/Operations/OperationMetricMath.cxx @@ -91,10 +91,11 @@ void OperationMetricMath::useParameters(OperationParameters* myParams, ProgressO } int numInputs = myVarOpts.size(); int numVars = myVarNames.size(); - vector varMetrics(numVars, (MetricFile*)NULL); + vector varMetrics(numVars, NULL); vector metricColumns(numVars, -1); if (numInputs == 0 && numVars == 0) throw OperationException("you must specify at least one input metric (-var), even if the expression doesn't use a variable"); - int numNodes; + const MetricFile* namefile = NULL; + int numNodes = -1; StructureEnum::Enum myStructure; int numColumns = -1; for (int i = 0; i < numInputs; ++i) @@ -137,6 +138,10 @@ void OperationMetricMath::useParameters(OperationParameters* myParams, ProgressO if (numColumns == -1)//then this is the first one that doesn't use -repeat { numColumns = thisColumns; + if (useColumn == -1) + { + namefile = thisMetric;//if it also doesn't use -column, take map names from it + } } else { if (numColumns != thisColumns) { @@ -182,6 +187,7 @@ void OperationMetricMath::useParameters(OperationParameters* myParams, ProgressO myMetricOut->setStructure(myStructure); for (int j = 0; j < numColumns; ++j) { + if (namefile != NULL) myMetricOut->setMapName(j, namefile->getMapName(j)); for (int v = 0; v < numVars; ++v) { if (metricColumns[v] == -1) diff --git a/src/Operations/OperationMetricPalette.cxx b/src/Operations/OperationMetricPalette.cxx index f33c03d274a3fd854c4b2057e524e90d24a47c9f..633f48606e8fa17ced5c9c857ad6578a29a81210 100644 --- a/src/Operations/OperationMetricPalette.cxx +++ b/src/Operations/OperationMetricPalette.cxx @@ -84,6 +84,9 @@ OperationParameters* OperationMetricPalette::getParameters() thresholdOpt->addDoubleParameter(3, "min", "lower threshold"); thresholdOpt->addDoubleParameter(4, "max", "upper threshold"); + OptionalParameter* inversionOpt = ret->createOptionalParameter(15, "-inversion", "specify palette inversion"); + inversionOpt->addStringParameter(1, "type", "the type of inversion"); + AString myText = AString("The original metric file is overwritten with the modified version. By default, all columns of the metric file are adjusted ") + "to the new settings, use the -column option to change only one column. Mapping settings not specified in options will be taken from the first column. " + "The argument must be one of the following:\n\n"; @@ -114,6 +117,13 @@ OperationParameters* OperationMetricPalette::getParameters() { myText += PaletteThresholdTestEnum::toName(myEnums3[i]) + "\n"; } + myText += "\nThe argument to -inversion must be one of the following:\n\n"; + vector myEnums4; + PaletteInvertModeEnum::getAllEnums(myEnums4); + for (int i = 0; i < (int)myEnums4.size(); ++i) + { + myText += PaletteInvertModeEnum::toName(myEnums4[i]) + "\n"; + } ret->setHelpText(myText); return ret; } @@ -205,6 +215,14 @@ void OperationMetricPalette::useParameters(OperationParameters* myParams, Progre myMapping.setThresholdMinimum(mytype, thresholdOpt->getDouble(3)); myMapping.setThresholdMaximum(mytype, thresholdOpt->getDouble(4)); } + OptionalParameter* inversionOpt = myParams->getOptionalParameter(15); + if (inversionOpt->m_present) + { + bool ok = false; + PaletteInvertModeEnum::Enum inversionType = PaletteInvertModeEnum::fromName(inversionOpt->getString(1), &ok); + if (!ok) throw OperationException("unrecognized palette inversion type: " + inversionOpt->getString(1)); + myMapping.setInvertedMode(inversionType); + } if (myColumn == -1) { for (int i = 0; i < myMetric.getNumberOfMaps(); ++i) diff --git a/src/Operations/OperationMetricStats.cxx b/src/Operations/OperationMetricStats.cxx index 974d2e93f1323a844afcfebe208a325b84a9f8fa..5dbbddc8efd67bcee6a69845d34c6f99ba759d28 100644 --- a/src/Operations/OperationMetricStats.cxx +++ b/src/Operations/OperationMetricStats.cxx @@ -159,75 +159,69 @@ void OperationMetricStats::useParameters(OperationParameters* myParams, Progress } bool matchColumnMode = false; MetricFile* myRoi = NULL; - const float* roiData = NULL; + int32_t numRoiCols = 1;//trick: pretend we have 1 roi map when we have no ROI file OptionalParameter* roiOpt = myParams->getOptionalParameter(5); if (roiOpt->m_present) { myRoi = roiOpt->getMetric(1); + numRoiCols = myRoi->getNumberOfColumns(); if (myRoi->getNumberOfNodes() != numNodes) throw OperationException("roi doesn't match input in number of vertices"); if (roiOpt->getOptionalParameter(2)->m_present) { - if (myRoi->getNumberOfColumns() != numCols) + if (numRoiCols != numCols) { throw OperationException("-match-maps specified, but roi has different number of columns than input"); } matchColumnMode = true; - } else { - roiData = myRoi->getValuePointerForColumn(0); } } bool showMapName = myParams->getOptionalParameter(6)->m_present; + int32_t columnStart, columnEnd; if (column == -1) { - if (reduceOpt->m_present) - { - for (int i = 0; i < numCols; ++i) - {//store result before printing anything, in case it throws while computing - if (matchColumnMode) - { - roiData = myRoi->getValuePointerForColumn(i); - } - const float result = reduce(input->getValuePointerForColumn(i), numNodes, myop, roiData); - if (showMapName) cout << AString::number(i + 1) << ": " << input->getMapName(i) << ": "; - stringstream resultsstr; - resultsstr << setprecision(7) << result; - cout << resultsstr.str() << endl; + columnStart = 0; + columnEnd = numCols; + } else { + CaretAssert(column >= 0 && column < numCols); + columnStart = column; + columnEnd = column + 1; + } + for (int i = columnStart; i < columnEnd; ++i) + { + if (showMapName) cout << AString::number(i + 1) << ":\t" << input->getMapName(i) << ":\t"; + if (matchColumnMode) + {//trick: matchColumn is only true when we have an roi + const float* roiData = myRoi->getValuePointerForColumn(i); + float result; + if (reduceOpt->m_present) + { + result = reduce(input->getValuePointerForColumn(i), numNodes, myop, roiData); + } else { + CaretAssert(percentileOpt->m_present); + result = percentile(input->getValuePointerForColumn(i), numNodes, percent, roiData); } + stringstream resultsstr; + resultsstr << setprecision(7) << result; + cout << resultsstr.str(); } else { - CaretAssert(percentileOpt->m_present); - for (int i = 0; i < numCols; ++i) - {//store result before printing anything, in case it throws while computing - if (matchColumnMode) + for (int j = 0; j < numRoiCols; ++j) + { + const float* roiData = NULL; + if (myRoi != NULL) roiData = myRoi->getValuePointerForColumn(j); + float result; + if (reduceOpt->m_present) { - roiData = myRoi->getValuePointerForColumn(i); + result = reduce(input->getValuePointerForColumn(i), numNodes, myop, roiData); + } else { + CaretAssert(percentileOpt->m_present); + result = percentile(input->getValuePointerForColumn(i), numNodes, percent, roiData); } - const float result = percentile(input->getValuePointerForColumn(i), numNodes, percent, roiData); - if (showMapName) cout << AString::number(i + 1) << ": " << input->getMapName(i) << ": "; stringstream resultsstr; resultsstr << setprecision(7) << result; - cout << resultsstr.str() << endl; + if (j != 0) cout << "\t"; + cout << resultsstr.str(); } } - } else { - CaretAssert(column >= 0 && column < numCols); - if (matchColumnMode) - { - roiData = myRoi->getValuePointerForColumn(column); - } - if (reduceOpt->m_present) - { - const float result = reduce(input->getValuePointerForColumn(column), numNodes, myop, roiData); - if (showMapName) cout << AString::number(column + 1) << ": " << input->getMapName(column) << ": "; - stringstream resultsstr; - resultsstr << setprecision(7) << result; - cout << resultsstr.str() << endl; - } else { - CaretAssert(percentileOpt->m_present); - const float result = percentile(input->getValuePointerForColumn(column), numNodes, percent, roiData); - if (showMapName) cout << AString::number(column + 1) << ": " << input->getMapName(column) << ": "; - stringstream resultsstr; - resultsstr << setprecision(7) << result; - cout << resultsstr.str() << endl; - } + cout << endl; } } diff --git a/src/Operations/OperationMetricWeightedStats.cxx b/src/Operations/OperationMetricWeightedStats.cxx index 0e43a439f23582afa7d3825ecfb3ce5c14ce8f3b..be9ce772b890c6b9724eb91191293dbd3966ba13 100644 --- a/src/Operations/OperationMetricWeightedStats.cxx +++ b/src/Operations/OperationMetricWeightedStats.cxx @@ -234,21 +234,20 @@ void OperationMetricWeightedStats::useParameters(OperationParameters* myParams, } bool matchColumnMode = false; MetricFile* myRoi = NULL; - const float* roiData = NULL; + int32_t numRoiCols = 1;//trick: pretend 1 roi column exists when there is no roi to get the inner loop to do the right thing OptionalParameter* roiOpt = myParams->getOptionalParameter(5); if (roiOpt->m_present) { myRoi = roiOpt->getMetric(1); + numRoiCols = myRoi->getNumberOfColumns(); if (myRoi->getNumberOfNodes() != numNodes) throw OperationException("roi doesn't match input in number of vertices"); if (roiOpt->getOptionalParameter(2)->m_present) { - if (myRoi->getNumberOfColumns() != numCols) + if (numRoiCols != numCols) { throw OperationException("-match-maps specified, but roi has different number of columns than input"); } matchColumnMode = true; - } else { - roiData = myRoi->getValuePointerForColumn(0); } } bool haveOp = false; @@ -288,29 +287,37 @@ void OperationMetricWeightedStats::useParameters(OperationParameters* myParams, } if (!haveOp) throw OperationException("you must specify an operation"); bool showMapName = myParams->getOptionalParameter(10)->m_present; + int32_t columnStart, columnEnd; if (column == -1) { - for (int i = 0; i < numCols; ++i) - {//store result before printing anything, in case it throws while computing - if (matchColumnMode) - { - roiData = myRoi->getValuePointerForColumn(i); - } + columnStart = 0; + columnEnd = numCols; + } else { + columnStart = column; + columnEnd = column + 1; + } + for (int i = columnStart; i < columnEnd; ++i) + { + if (showMapName) cout << AString::number(i + 1) << ":\t" << input->getMapName(i) << ":\t"; + if (matchColumnMode) + {//trick: matchColumn is only true when we have an roi + const float* roiData = myRoi->getValuePointerForColumn(i); const float result = doOperation(input->getValuePointerForColumn(i), useWeights, numNodes, myop, roiData, argument); - if (showMapName) cout << AString::number(i + 1) << ": " << input->getMapName(i) << ": "; stringstream resultsstr; resultsstr << setprecision(7) << result; - cout << resultsstr.str() << endl; - } - } else { - if (matchColumnMode) - { - roiData = myRoi->getValuePointerForColumn(column); + cout << resultsstr.str(); + } else { + for (int j = 0; j < numRoiCols; ++j) + { + const float* roiData = NULL; + if (myRoi != NULL) roiData = myRoi->getValuePointerForColumn(j); + const float result = doOperation(input->getValuePointerForColumn(i), useWeights, numNodes, myop, roiData, argument); + stringstream resultsstr; + resultsstr << setprecision(7) << result; + if (j != 0) cout << "\t"; + cout << resultsstr.str(); + } } - const float result = doOperation(input->getValuePointerForColumn(column), useWeights, numNodes, myop, roiData, argument); - if (showMapName) cout << AString::number(column + 1) << ": " << input->getMapName(column) << ": "; - stringstream resultsstr; - resultsstr << setprecision(7) << result; - cout << resultsstr.str() << endl; + cout << endl; } } diff --git a/src/Operations/OperationNiftiInformation.cxx b/src/Operations/OperationNiftiInformation.cxx index ab2ed9fabb3ec839c5cd7fc4fe0c65bdaeef62ed..770fe8aa134d915f53c85db2b1d1a169e4a05257 100644 --- a/src/Operations/OperationNiftiInformation.cxx +++ b/src/Operations/OperationNiftiInformation.cxx @@ -21,6 +21,7 @@ #include "OperationNiftiInformation.h" #include "OperationException.h" +#include "CaretBinaryFile.h" #include "CiftiFile.h" #include "NiftiIO.h" #include "CiftiXML.h" @@ -46,7 +47,8 @@ OperationParameters* OperationNiftiInformation::getParameters() OperationParameters* ret = new OperationParameters(); ret->addStringParameter(1, "nifti-file", "the nifti/cifti file to examine"); - ret->createOptionalParameter(2, "-print-header", "display the header contents"); + OptionalParameter* headerOpt = ret->createOptionalParameter(2, "-print-header", "display the header contents"); + headerOpt->createOptionalParameter(1, "-allow-truncated", "print the header even if the data is truncated"); ret->createOptionalParameter(3, "-print-matrix", "output the values in the matrix (cifti only)"); @@ -64,7 +66,8 @@ void OperationNiftiInformation::useParameters(OperationParameters* myParams, Pro { LevelProgress myProgress(myProgObj); const AString fileName = myParams->getString(1); - bool printHeader = myParams->getOptionalParameter(2)->m_present; + OptionalParameter* headerOpt = myParams->getOptionalParameter(2); + bool printHeader = headerOpt->m_present; bool printMatrix = myParams->getOptionalParameter(3)->m_present; OptionalParameter* printXmlOpt = myParams->getOptionalParameter(4); bool printXml = printXmlOpt->m_present; @@ -73,10 +76,19 @@ void OperationNiftiInformation::useParameters(OperationParameters* myParams, Pro if(printHeader) { - NiftiIO myIO; - myIO.openRead(fileName); - cout << myIO.getHeader().toString() << endl; - cout << getMemorySizeAsString(myIO.getDimensions()) << endl; + if (!headerOpt->getOptionalParameter(1)->m_present) + {//NiftiIO contains the file length check + NiftiIO myIO; + myIO.openRead(fileName); + cout << myIO.getHeader().toString() << endl; + cout << getMemorySizeAsString(myIO.getDimensions()) << endl; + } else {//NiftiHeader doesn't + CaretBinaryFile myFile(fileName); + NiftiHeader myHeader; + myHeader.read(myFile); + cout << myHeader.toString() << endl; + cout << getMemorySizeAsString(myHeader.getDimensions()) << endl; + } } if(printXml) { diff --git a/src/Operations/OperationProbtrackXDotConvert.cxx b/src/Operations/OperationProbtrackXDotConvert.cxx index e8121695b9770a02c036ec35239086da83fcc94c..0be7d85340b98d32922b9a33c7db6d67beb31a3c 100644 --- a/src/Operations/OperationProbtrackXDotConvert.cxx +++ b/src/Operations/OperationProbtrackXDotConvert.cxx @@ -181,7 +181,7 @@ void OperationProbtrackXDotConvert::useParameters(OperationParameters* myParams, } myXML.copyMapping(CiftiXMLOld::ALONG_COLUMN, colCiftiOpt->getCifti(1)->getCiftiXMLOld(), myDir); } - fstream dotFile(dotFileName.toAscii().constData(), fstream::in); + fstream dotFile(dotFileName.toLatin1().constData(), fstream::in); if (!dotFile.good()) { throw OperationException("error opening text file '" + dotFileName + "'"); @@ -410,7 +410,7 @@ void OperationProbtrackXDotConvert::addVoxelMapping(const VolumeFile* myLabelVol voxelLists.resize(count); inputIndices.resize(count); voxelIndexType vi, vj, vk; - fstream myTextFile(textFileName.toAscii().constData(), fstream::in); + fstream myTextFile(textFileName.toLatin1().constData(), fstream::in); if (!myTextFile.good()) { throw OperationException("error opening text file '" + textFileName + "'"); diff --git a/src/Operations/OperationShowScene.cxx b/src/Operations/OperationShowScene.cxx index 59df02ca82c2bcdced17069966acd7524d60ebd0..ccdf98f3de1cd4387bb189f29603d74787d86dff 100644 --- a/src/Operations/OperationShowScene.cxx +++ b/src/Operations/OperationShowScene.cxx @@ -22,6 +22,10 @@ #include #include +#ifdef HAVE_GLEW +#include +#endif + #ifdef HAVE_OSMESA #include #endif // HAVE_OSMESA @@ -33,15 +37,21 @@ #include "Brain.h" #include "BrainOpenGLFixedPipeline.h" #include "BrainOpenGLViewportContent.h" +#include "BrainOpenGLWindowContent.h" +#include "BrowserWindowContent.h" #include "CaretAssert.h" +#include "CaretPreferences.h" #include "CaretLogger.h" #include "DataFileException.h" #include "EventBrowserTabGet.h" +#include "EventBrowserWindowContent.h" +#include "EventMapYokingSelectMap.h" #include "EventManager.h" #include "FileInformation.h" #include "DummyFontTextRenderer.h" #include "FtglFontTextRenderer.h" #include "ImageFile.h" +#include "MapYokingGroupEnum.h" #include "OperationShowScene.h" #include "OperationException.h" #include "Scene.h" @@ -59,7 +69,7 @@ using namespace caret; /** - * \class caret::OperationShowScene + * \class caret::OperationShowScene * \brief Offscreen rendering of scene to an image file * * Render a scene into an image file using the Offscreen Mesa Library @@ -98,7 +108,7 @@ OperationShowScene::getParameters() ret->addStringParameter(3, "image-file-name", "output image file name"); ret->addIntegerParameter(4, "image-width", "width of output image(s)"); - + ret->addIntegerParameter(5, "image-height", "height of output image(s)"); const QString windowSizeSwitch("-use-window-size"); @@ -106,6 +116,14 @@ OperationShowScene::getParameters() ret->createOptionalParameter(7, "-no-scene-colors", "Do not use background and foreground colors in scene"); + OptionalParameter* mapYokeOpt = ret->createOptionalParameter(8, "-set-map-yoke", "Override selected map index for a map yoking group."); + mapYokeOpt->addStringParameter(1, "Map Yoking Roman Numeral", "Roman numeral identifying the map yoking group (I, II, III, IV, V, VI, VII, VIII, IX, X)"); + mapYokeOpt->addIntegerParameter(2, "Map Index", "Map index for yoking group. Indices start at 1 (one)"); + + OptionalParameter* connDbOpt = ret->createOptionalParameter(9, "-conn-db-login", "Login for scenes with files in Connectome Database"); + connDbOpt->addStringParameter(1, "Username", "Connectome DB Username"); + connDbOpt->addStringParameter(2, "Password", "Connectome DB Password"); + AString helpText("Render content of browser windows displayed in a scene " "into image file(s). The image file name should be " "similar to \"capture.png\". If there is only one image " @@ -114,9 +132,16 @@ OperationShowScene::getParameters() "into the image name: \"capture_01.png\", \"capture_02.png\" " "etc.\n" "\n" + "If the scene references files in the Connectome Database,\n" + "the \"-conn-db-login\" option is available for providing the \n" + "username and password. If this options is not specified, \n" + "the username and password stored in the user's preferences\n" + "is used.\n" + "\n" "The image format is determined by the image file extension.\n" "The available image formats may vary by operating system.\n" - "Image formats available on this system are:\n"); + "Image formats available on this system are:\n" + ); std::vector imageFileExtensions; AString defaultExtension; ImageFile::getImageFileExtensions(imageFileExtensions, @@ -157,7 +182,7 @@ OperationShowScene::getParameters() #ifndef HAVE_OSMESA void OperationShowScene::useParameters(OperationParameters* /*myParams*/, - ProgressObject* /*myProgObj*/) + ProgressObject* /*myProgObj*/) { throw OperationException("Show scene command not available due to this software version " "not being built with the Mesa OffScreen Library"); @@ -179,6 +204,28 @@ OperationShowScene::useParameters(OperationParameters* myParams, const bool doNotUseSceneColorsFlag = myParams->getOptionalParameter(7)->m_present; + MapYokingGroupEnum::Enum mapYokingGroup = MapYokingGroupEnum::MAP_YOKING_GROUP_OFF; + int32_t mapYokingMapIndex = -1; + OptionalParameter* mapYokeOpt = myParams->getOptionalParameter(8); + if (mapYokeOpt->m_present) { + const AString romanNumeral = mapYokeOpt->getString(1); + bool validFlag = false; + mapYokingGroup = MapYokingGroupEnum::fromGuiName(romanNumeral, &validFlag); + if ( ! validFlag) { + throw OperationException(romanNumeral + + " does not identify a valid Map Yoking Group. "); + } + mapYokingMapIndex = mapYokeOpt->getInteger(2); + if (mapYokingMapIndex < 1) { + throw OperationException("Map yoking map index must be one or greater."); + } + + /* + * Map indice in code start at zero + */ + mapYokingMapIndex--; + } + if ( ! useWindowSizeForImageSizeFlag) { if ((userImageWidth <= 0) || (userImageHeight <= 0)) { @@ -188,7 +235,25 @@ OperationShowScene::useParameters(OperationParameters* myParams, + QString::number(userImageHeight)); } } - + + /* + * Need to set username/password for files in ConnectomeDB + */ + AString username; + AString password; + OptionalParameter* connDbOpt = myParams->getOptionalParameter(9); + if (connDbOpt->m_present) { + username = connDbOpt->getString(1); + password = connDbOpt->getString(2); + } + else { + CaretPreferences* prefs = SessionManager::get()->getCaretPreferences(); + prefs->getRemoteFileUserNameAndPassword(username, + password); + } + CaretDataFile::setFileReadingUsernameAndPassword(username, + password); + /* * Read the scene file and load the scene */ @@ -212,13 +277,14 @@ OperationShowScene::useParameters(OperationParameters* myParams, throw OperationException("Scene name is invalid"); } } - + /* * Enable voxel coloring since it is defaulted off for commands */ VolumeFile::setVoxelColoringEnabled(true); - SceneAttributes sceneAttributes(SceneTypeEnum::SCENE_TYPE_FULL); + SceneAttributes sceneAttributes(SceneTypeEnum::SCENE_TYPE_FULL, + scene); if (doNotUseSceneColorsFlag) { sceneAttributes.setUseSceneForegroundAndBackgroundColors(false); @@ -237,6 +303,11 @@ OperationShowScene::useParameters(OperationParameters* myParams, sessionManager->restoreFromScene(&sceneAttributes, guiManagerClass->getClass("m_sessionManager")); + /* + * Get the error message but continue processing since the error + * may not affect the scene. Print error message later. + */ + const AString sceneErrorMessage = sceneAttributes.getErrorMessage(); if (sessionManager->getNumberOfBrains() <= 0) { throw OperationException("Scene loading failure, SessionManager contains no Brains"); @@ -247,252 +318,206 @@ OperationShowScene::useParameters(OperationParameters* myParams, bool missingWindowMessageHasBeenDisplayed = false; + /* + * Apply map yoking + */ + if (mapYokingGroup != MapYokingGroupEnum::MAP_YOKING_GROUP_OFF) { + MapYokingGroupEnum::setSelectedMapIndex(mapYokingGroup, mapYokingMapIndex); + + EventMapYokingSelectMap yokeEvent(mapYokingGroup, + NULL, + mapYokingMapIndex, + true); + EventManager::get()->sendEvent(yokeEvent.getPointer()); + } + + std::vector allBrowserWindowContent; + for (int32_t i = 0; i < BrainConstants::MAXIMUM_NUMBER_OF_BROWSER_WINDOWS; i++) { + std::unique_ptr browserContentEvent = EventBrowserWindowContent::getWindowContent(i); + EventManager::get()->sendEvent(browserContentEvent->getPointer()); + const BrowserWindowContent* bwc = browserContentEvent->getBrowserWindowContent(); + CaretAssert(bwc); + if (bwc->isValid()) { + allBrowserWindowContent.push_back(bwc); + } + } + const int32_t numberOfWindows = static_cast(allBrowserWindowContent.size()); + if (numberOfWindows <= 0) { + throw OperationException("No BrowserWindowContent was found for showing as scene"); + } + /* * Restore windows */ - const SceneClassArray* browserWindowArray = guiManagerClass->getClassArray("m_brainBrowserWindows"); - if (browserWindowArray != NULL) { - const int32_t numBrowserClasses = browserWindowArray->getNumberOfArrayElements(); - for (int32_t i = 0; i < numBrowserClasses; i++) { - const SceneClass* browserClass = browserWindowArray->getClassAtIndex(i); - - const bool restoreToTabTiles = browserClass->getBooleanValue("m_viewTileTabsAction", - false); - const int32_t windowIndex = browserClass->getIntegerValue("m_browserWindowIndex", 0); - - int32_t imageWidth = userImageWidth; - int32_t imageHeight = userImageHeight; - - if (useWindowSizeForImageSizeFlag) { - /* - * Requires version AFTER 1.2.0-pre1 - */ - const SceneClass* graphicsGeometry = browserClass->getClass("openGLWidgetGeometry"); - if (graphicsGeometry != NULL) { - const int32_t windowGeometryWidth = graphicsGeometry->getIntegerValue("geometryWidth", -1); - const int32_t windowGeometryHeight = graphicsGeometry->getIntegerValue("geometryHeight", -1); - - if ((windowGeometryWidth > 0) - && (windowGeometryHeight > 0)) { - imageWidth = windowGeometryWidth; - imageHeight = windowGeometryHeight; - } - } - else { - if ((imageWidth <= 0) - || (imageHeight <= 0)) { - const QString msg("Option " - + useWindowSizeParam->m_optionSwitch - + " is used but window size not found in scene and width=" - + QString::number(imageWidth) - + " height=" - + QString::number(imageWidth) - + " on command line is invalid."); - - throw OperationException(msg); - } - - if ( ! missingWindowMessageHasBeenDisplayed) { - const QString msg("Option \"" - + useWindowSizeParam->m_optionSwitch - + "\" is used but window size not found in scene.\n" - " Scene was created prior to implementation of this option.\n" - " Image size will be width=" - + QString::number(imageWidth) - + " and height=" - + QString::number(imageHeight) - + " as specified on command line.\n" - " Recreating the scene will allow use of the option.\n"); - CaretLogWarning(msg); - - /* - * Avoid message being displayed more than once when - * there are more than one windows. - */ - missingWindowMessageHasBeenDisplayed = true; - } - } - } - - if ((imageWidth <= 0) - || (imageHeight <= 0)) { - throw OperationException("Invalid image size width=" - + QString::number(imageWidth) - + " height=" - + QString::number(imageHeight)); - } - - int windowViewport[4] = { 0.0, 0.0, imageWidth, imageHeight }; - - float aspectRatio = -1.0; - const bool windowAspectRatioLocked = browserClass->getBooleanValue("m_aspectRatioLockedStatus"); - if (windowAspectRatioLocked) { - aspectRatio = browserClass->getFloatValue("m_aspectRatio", -1.0); - } - - const int windowWidth = windowViewport[2]; - const int windowHeight = windowViewport[3]; - - // - // Create the Mesa Context - // - const int depthBits = 16; - const int stencilBits = 0; - const int accumBits = 0; - OSMesaContext mesaContext = OSMesaCreateContextExt(OSMESA_RGBA, - depthBits, - stencilBits, - accumBits, - NULL); - if (mesaContext == 0) { - throw ("Creating Mesa Context failed."); - } - - // - // Allocate image buffer - // - const int32_t imageBufferSize =imageWidth * imageHeight * 4 * sizeof(unsigned char); - unsigned char* imageBuffer = new unsigned char[imageBufferSize]; - if (imageBuffer == 0) { - throw OperationException("Allocating image buffer size=" - + QString::number(imageBufferSize) - + " failed."); - } - - // - // Assign buffer to Mesa Context and make current - // - if (OSMesaMakeCurrent(mesaContext, - imageBuffer, - GL_UNSIGNED_BYTE, - imageWidth, - imageHeight) == 0) { - throw OperationException("Assigning buffer to context and make current failed."); - } - + for (int32_t iWindow = 0; iWindow < numberOfWindows; iWindow++) { + CaretAssertVectorIndex(allBrowserWindowContent, iWindow); + const auto bwc = allBrowserWindowContent[iWindow]; + + const bool restoreToTabTiles = bwc->isTileTabsEnabled(); + const int32_t windowIndex = bwc->getWindowIndex(); + + int32_t imageWidth = userImageWidth; + int32_t imageHeight = userImageHeight; + + if (useWindowSizeForImageSizeFlag) { /* - * If tile tabs was saved to the scene, restore it as the scenes tile tabs configuration + * Requires version AFTER 1.2.0-pre1 */ - if (restoreToTabTiles) { - CaretPointer brainOpenGL(createBrainOpenGL(windowIndex)); + const float geomWidth = bwc->getSceneGraphicsWidth(); + const float geomHeight = bwc->getSceneGraphicsHeight(); + if ((geomWidth > 0) + && (geomHeight > 0)) { + imageWidth = geomWidth; + imageHeight = geomHeight; + } + else { + if ((imageWidth <= 0) + || (imageHeight <= 0)) { + const QString msg("Option " + + useWindowSizeParam->m_optionSwitch + + " is used but window size not found in scene and width=" + + QString::number(imageWidth) + + " height=" + + QString::number(imageWidth) + + " on command line is invalid."); + + throw OperationException(msg); + } - const AString tileTabsConfigString = browserClass->getStringValue("m_sceneTileTabsConfiguration"); - if ( ! tileTabsConfigString.isEmpty()) { - TileTabsConfiguration tileTabsConfiguration; - tileTabsConfiguration.decodeFromXML(tileTabsConfigString); + if ( ! missingWindowMessageHasBeenDisplayed) { + const QString msg("Option \"" + + useWindowSizeParam->m_optionSwitch + + "\" is used but window size not found in scene.\n" + " Scene was created prior to implementation of this option.\n" + " Image size will be width=" + + QString::number(imageWidth) + + " and height=" + + QString::number(imageHeight) + + " as specified on command line.\n" + " Recreating the scene will allow use of the option.\n"); + CaretLogWarning(msg); /* - * Restore toolbar + * Avoid message being displayed more than once when + * there are more than one windows. */ - const SceneClass* toolbarClass = browserClass->getClass("m_toolbar"); - if (toolbarClass != NULL) { - /* - * Index of selected browser tab (NOT the tabBar) - */ - std::vector allTabContent; - const ScenePrimitiveArray* tabIndexArray = toolbarClass->getPrimitiveArray("tabIndices"); - if (tabIndexArray != NULL) { - const int32_t numTabs = tabIndexArray->getNumberOfArrayElements(); - for (int32_t iTab = 0; iTab < numTabs; iTab++) { - const int32_t tabIndex = tabIndexArray->integerValue(iTab); - - EventBrowserTabGet getTabContent(tabIndex); - EventManager::get()->sendEvent(getTabContent.getPointer()); - BrowserTabContent* tabContent = getTabContent.getBrowserTab(); - if (tabContent == NULL) { - throw OperationException("Failed to obtain tab number " - + AString::number(tabIndex + 1) - + " for window " - + AString::number(windowIndex + 1)); - } - allTabContent.push_back(tabContent); - } - } - - const int32_t numTabContent = static_cast(allTabContent.size()); - if (numTabContent <= 0) { - throw OperationException("Failed to find any tab content"); - } - std::vector rowHeights; - std::vector columnWidths; - if ( ! tileTabsConfiguration.getRowHeightsAndColumnWidthsForWindowSize(windowWidth, - windowHeight, - numTabContent, - rowHeights, - columnWidths)) { - throw OperationException("Tile Tabs Row/Column sizing failed !!!"); - } - - const int32_t tabIndexToHighlight = -1; - std::vector viewports = - BrainOpenGLViewportContent::createViewportContentForTileTabs(allTabContent, - &tileTabsConfiguration, - gapsAndMargins, - windowIndex, - windowViewport, - tabIndexToHighlight); - - brainOpenGL->drawModels(brain, - viewports); - - const int32_t outputImageIndex = ((numBrowserClasses > 1) - ? i - : -1); - - writeImage(imageFileName, - outputImageIndex, - imageBuffer, - imageWidth, - imageHeight); - - for (std::vector::iterator vpIter = viewports.begin(); - vpIter != viewports.end(); - vpIter++) { - delete *vpIter; - } - viewports.clear(); - } - } - else { - throw OperationException("Tile tabs configuration is corrupted."); + missingWindowMessageHasBeenDisplayed = true; } } - else { - CaretPointer brainOpenGL(createBrainOpenGL(windowIndex)); + } + + if ((imageWidth <= 0) + || (imageHeight <= 0)) { + throw OperationException("Invalid image size width=" + + QString::number(imageWidth) + + " height=" + + QString::number(imageHeight)); + } + + int windowViewport[4] = { 0, 0, imageWidth, imageHeight }; + + const int windowWidth = windowViewport[2]; + const int windowHeight = windowViewport[3]; + + // + // Create the Mesa Context + // + const int depthBits = 16; + const int stencilBits = 0; + const int accumBits = 0; + OSMesaContext mesaContext = OSMesaCreateContextExt(OSMESA_RGBA, + depthBits, + stencilBits, + accumBits, + NULL); + if (mesaContext == 0) { + throw ("Creating Mesa Context failed."); + } + + // + // Allocate image buffer + // + const int32_t imageBufferSize =imageWidth * imageHeight * 4 * sizeof(unsigned char); + unsigned char* imageBuffer = new unsigned char[imageBufferSize]; + if (imageBuffer == 0) { + throw OperationException("Allocating image buffer size=" + + QString::number(imageBufferSize) + + " failed."); + } + + // + // Assign buffer to Mesa Context and make current + // + if (OSMesaMakeCurrent(mesaContext, + imageBuffer, + GL_UNSIGNED_BYTE, + imageWidth, + imageHeight) == 0) { + throw OperationException("Assigning buffer to context and make current failed."); + } + + /* + * If tile tabs was saved to the scene, restore it as the scenes tile tabs configuration + */ + if (restoreToTabTiles) { + CaretPointer brainOpenGL(createBrainOpenGL()); + + TileTabsConfiguration* tileTabsConfiguration = bwc->getSceneTileTabsConfiguration(); + CaretAssert(tileTabsConfiguration); + if ((tileTabsConfiguration->getMaximumNumberOfRows() > 0) + && (tileTabsConfiguration->getMaximumNumberOfColumns() > 0)) { - /* - * Restore toolbar - */ - const SceneClass* toolbarClass = browserClass->getClass("m_toolbar"); - if (toolbarClass != NULL) { - /* - * Index of selected browser tab (NOT the tabBar) - */ - const int32_t selectedTabIndex = toolbarClass->getIntegerValue("selectedTabIndex", -1); + const std::vector tabIndices = bwc->getSceneTabIndices(); + if ( ! tabIndices.empty()) { + std::vector allTabContent; + const int32_t numTabs = static_cast(tabIndices.size()); + for (int32_t iTab = 0; iTab < numTabs; iTab++) { + CaretAssertVectorIndex(tabIndices, iTab); + const int32_t tabIndex = tabIndices[iTab]; + EventBrowserTabGet getTabContent(tabIndex); + EventManager::get()->sendEvent(getTabContent.getPointer()); + BrowserTabContent* tabContent = getTabContent.getBrowserTab(); + if (tabContent == NULL) { + throw OperationException("Failed to obtain tab number " + + AString::number(tabIndex + 1) + + " for window " + + AString::number(windowIndex + 1)); + } + allTabContent.push_back(tabContent); + } - EventBrowserTabGet getTabContent(selectedTabIndex); - EventManager::get()->sendEvent(getTabContent.getPointer()); - BrowserTabContent* tabContent = getTabContent.getBrowserTab(); - if (tabContent == NULL) { - throw OperationException("Failed to obtain tab number " - + AString::number(selectedTabIndex + 1) - + " for window " - + AString::number(i + 1)); + const int32_t numTabContent = static_cast(allTabContent.size()); + if (numTabContent <= 0) { + throw OperationException("Failed to find any tab content"); + } + std::vector rowHeights; + std::vector columnWidths; + if ( ! tileTabsConfiguration->getRowHeightsAndColumnWidthsForWindowSize(windowWidth, + windowHeight, + numTabContent, + rowHeights, + columnWidths)) { + throw OperationException("Tile Tabs Row/Column sizing failed !!!"); } - CaretPointer content(NULL); - content.grabNew(BrainOpenGLViewportContent::createViewportForSingleTab(tabContent, - gapsAndMargins, - windowIndex, - windowViewport)); - std::vector viewportContents; - viewportContents.push_back(content); + const int32_t tabIndexToHighlight = -1; + std::vector viewports = + BrainOpenGLViewportContent::createViewportContentForTileTabs(allTabContent, + tileTabsConfiguration, + gapsAndMargins, + windowIndex, + windowViewport, + tabIndexToHighlight); - brainOpenGL->drawModels(brain, - viewportContents); + std::vector constViewports(viewports.begin(), + viewports.end()); + brainOpenGL->drawModels(windowIndex, + brain, + mesaContext, + constViewports); - const int32_t outputImageIndex = ((numBrowserClasses > 1) - ? i + const int32_t outputImageIndex = ((numberOfWindows > 1) + ? iWindow : -1); writeImage(imageFileName, @@ -501,15 +526,73 @@ OperationShowScene::useParameters(OperationParameters* myParams, imageWidth, imageHeight); + for (std::vector::iterator vpIter = viewports.begin(); + vpIter != viewports.end(); + vpIter++) { + delete *vpIter; + } + viewports.clear(); } } + else { + throw OperationException("Tile tabs configuration is corrupted."); + } + } + else { + CaretPointer brainOpenGL(createBrainOpenGL()); + + const int32_t selectedTabIndex = bwc->getSceneSelectedTabIndex(); + + EventBrowserTabGet getTabContent(selectedTabIndex); + EventManager::get()->sendEvent(getTabContent.getPointer()); + BrowserTabContent* tabContent = getTabContent.getBrowserTab(); + if (tabContent == NULL) { + throw OperationException("Failed to obtain tab number " + + AString::number(selectedTabIndex + 1) + + " for window " + + AString::number(iWindow + 1)); + } - /* - * Free image memory and Mesa context - */ - delete[] imageBuffer; - OSMesaDestroyContext(mesaContext); + CaretPointer content(NULL); + std::vector allTabs; + allTabs.push_back(tabContent); + content.grabNew(BrainOpenGLViewportContent::createViewportForSingleTab(allTabs, + tabContent, + gapsAndMargins, + windowIndex, + windowViewport)); + std::vector viewportContents; + viewportContents.push_back(content); + + brainOpenGL->drawModels(windowIndex, + brain, + mesaContext, + viewportContents); + + const int32_t outputImageIndex = ((numberOfWindows > 1) + ? iWindow + : -1); + + writeImage(imageFileName, + outputImageIndex, + imageBuffer, + imageWidth, + imageHeight); } + + /* + * Free image memory and Mesa context + */ + delete[] imageBuffer; + OSMesaDestroyContext(mesaContext); + } + + /* + * Print error messages + */ + if ( ! sceneErrorMessage.isEmpty()) { + std::cerr << "ERRORS loading scene, output image may be incorrect." << std::endl; + std::cerr << sceneErrorMessage << std::endl; } } @@ -586,7 +669,7 @@ OperationShowScene::estimateGraphicsSize(const SceneClass* windowSceneClass, /** * Get the size of a toolbox. - * + * * @param toolBoxClass * The toolbox scene class. * @param overlayToolBoxWidthOut @@ -601,9 +684,9 @@ OperationShowScene::estimateGraphicsSize(const SceneClass* windowSceneClass, bool OperationShowScene::getToolBoxSize(const SceneClass* toolBoxClass, const SceneClass* activeToolBoxClass, - float& overlayToolBoxWidthOut, - float& overlayToolBoxHeightOut, - QString& overlayToolBoxOrientationOut) + float& overlayToolBoxWidthOut, + float& overlayToolBoxHeightOut, + QString& overlayToolBoxOrientationOut) { if (toolBoxClass == NULL) { return false; @@ -652,13 +735,11 @@ OperationShowScene::getToolBoxSize(const SceneClass* toolBoxClass, /** * Create OpenGL Rendering. * - * @param windowIndex - * Index of window. * @return * BrainOpenGL. */ BrainOpenGLFixedPipeline* -OperationShowScene::createBrainOpenGL(const int32_t windowIndex) +OperationShowScene::createBrainOpenGL() { /* * The OpenGL rendering takes ownership of the text renderer @@ -686,8 +767,7 @@ OperationShowScene::createBrainOpenGL(const int32_t windowIndex) * will occur as the OpenGL context is invalid when things such as * display lists or buffers are deleted. */ - BrainOpenGLFixedPipeline* brainOpenGL = new BrainOpenGLFixedPipeline(windowIndex, - textRenderer); + BrainOpenGLFixedPipeline* brainOpenGL = new BrainOpenGLFixedPipeline(textRenderer); brainOpenGL->initializeOpenGL(); return brainOpenGL; @@ -737,7 +817,6 @@ OperationShowScene::writeImage(const AString& imageFileName, } try { - //ImageFile imageFile(image); ImageFile imageFile(imageContent, imageWidth, imageHeight, diff --git a/src/Operations/OperationShowScene.h b/src/Operations/OperationShowScene.h index 0c09eacb6877023af73fa6989318e50ce27e0b6a..30e177cc6afef104832433571d07ee3c0289ba0d 100644 --- a/src/Operations/OperationShowScene.h +++ b/src/Operations/OperationShowScene.h @@ -43,7 +43,7 @@ namespace caret { static bool isShowSceneCommandAvailable(); private: - static BrainOpenGLFixedPipeline* createBrainOpenGL(const int32_t windowIndex); + static BrainOpenGLFixedPipeline* createBrainOpenGL(); static void writeImage(const AString& imageFileName, const int32_t imageIndex, diff --git a/src/Operations/OperationSurfaceGeodesicDistance.cxx b/src/Operations/OperationSurfaceGeodesicDistance.cxx index 01793bd14d5b4fadc22afcc40a3c6dc758ac5a40..6efff3642ae5261ee201fd1902cfd5be9f9cff98 100644 --- a/src/Operations/OperationSurfaceGeodesicDistance.cxx +++ b/src/Operations/OperationSurfaceGeodesicDistance.cxx @@ -53,6 +53,9 @@ OperationParameters* OperationSurfaceGeodesicDistance::getParameters() OptionalParameter* limitOpt = ret->createOptionalParameter(5, "-limit", "stop at a certain distance"); limitOpt->addDoubleParameter(1, "limit-mm", "distance in mm to stop at"); + OptionalParameter* corrAreaOpt = ret->createOptionalParameter(6, "-corrected-areas", "vertex areas to use instead of computing them from the surface"); + corrAreaOpt->addMetricParameter(1, "area-metric", "the corrected vertex areas, as a metric"); + ret->setHelpText( AString("Unless -limit is specified, computes the geodesic distance from the specified vertex to all others. ") + "The result is output as a single column metric file, with a value of -1 for vertices that the distance was not computed for. " + @@ -68,9 +71,20 @@ void OperationSurfaceGeodesicDistance::useParameters(OperationParameters* myPara int myVertex = (int)myParams->getInteger(2); MetricFile* myMetricOut = myParams->getOutputMetric(3); bool smooth = !(myParams->getOptionalParameter(4)->m_present); - CaretPointer myHelp = mySurf->getGeodesicHelper(); - vector scratch(mySurf->getNumberOfNodes(), -1.0f);//use -1 to specify invalid OptionalParameter* limitOpt = myParams->getOptionalParameter(5); + CaretPointer myHelp; + CaretPointer myBase; + OptionalParameter* corrAreaOpt = myParams->getOptionalParameter(6); + if (corrAreaOpt->m_present) + { + MetricFile* corrAreas = corrAreaOpt->getMetric(1); + if (corrAreas->getNumberOfNodes() != mySurf->getNumberOfNodes()) throw OperationException("corrected vertex areas metric does not match surface number of vertices"); + myBase.grabNew(new GeodesicHelperBase(mySurf, corrAreas->getValuePointerForColumn(0))); + myHelp.grabNew(new GeodesicHelper(myBase)); + } else { + myHelp = mySurf->getGeodesicHelper(); + } + vector scratch(mySurf->getNumberOfNodes(), -1.0f);//use -1 to specify invalid if (limitOpt->m_present) { vector nodes; diff --git a/src/Operations/OperationSurfaceGeodesicROIs.cxx b/src/Operations/OperationSurfaceGeodesicROIs.cxx index ac74a5ca49cd206ac9c26c2375a7ee44231efcbd..ad5d301698127c4a3d2dd0896019c674f91960c8 100644 --- a/src/Operations/OperationSurfaceGeodesicROIs.cxx +++ b/src/Operations/OperationSurfaceGeodesicROIs.cxx @@ -62,6 +62,9 @@ OperationParameters* OperationSurfaceGeodesicROIs::getParameters() OptionalParameter* namesOpt = ret->createOptionalParameter(7, "-names", "name the columns from text file"); namesOpt->addStringParameter(1, "name-list-file", "a text file containing column names, one per line"); + OptionalParameter* corrAreaOpt = ret->createOptionalParameter(8, "-corrected-areas", "vertex areas to use instead of computing them from the surface"); + corrAreaOpt->addMetricParameter(1, "area-metric", "the corrected vertex areas, as a metric"); + ret->setHelpText( AString("For each vertex in the list file, a column in the output metric is created, and an ROI around that vertex is drawn in that column. ") + "Each metric column will have zeros outside the geodesic distance spacified by , and by default will have a value of 1.0 inside it. " + @@ -144,6 +147,16 @@ void OperationSurfaceGeodesicROIs::useParameters(OperationParameters* myParams, ++i; } } + MetricFile* corrAreas = NULL; + OptionalParameter* corrAreaOpt = myParams->getOptionalParameter(8); + if (corrAreaOpt->m_present) + { + corrAreas = corrAreaOpt->getMetric(1); + if (corrAreas->getNumberOfNodes() != numNodes) + { + throw OperationException("corrected areas metric does not match surface in number of vertices"); + } + } myMetricOut->setNumberOfNodesAndColumns(numNodes, (int)nodelist.size()); myMetricOut->setStructure(mySurf->getStructure()); float invneg2sigmasqr = -0.5f / (sigma * sigma); @@ -161,12 +174,20 @@ void OperationSurfaceGeodesicROIs::useParameters(OperationParameters* myParams, ", " + AString::number(myCoord[2], 'f', 1) + ")"); } } + CaretPointer myhelp; + CaretPointer mygeobase; + if (corrAreas == NULL) + { + myhelp = mySurf->getGeodesicHelper(); + } else { + mygeobase.grabNew(new GeodesicHelperBase(mySurf, corrAreas->getValuePointerForColumn(0))); + myhelp.grabNew(new GeodesicHelper(mygeobase)); + } switch (overlapType) { case 1://ALLOW for (int i = 0; i < (int)nodelist.size(); ++i) { - CaretPointer myhelp = mySurf->getGeodesicHelper(); vector roinodes; vector dists; myhelp->getNodesToGeoDist(nodelist[i], limit, roinodes, dists); @@ -199,7 +220,6 @@ void OperationSurfaceGeodesicROIs::useParameters(OperationParameters* myParams, vector bestDists(numNodes, -1.0f); for (int i = 0; i < (int)nodelist.size(); ++i) { - CaretPointer myhelp = mySurf->getGeodesicHelper(); vector roinodes; vector dists; myhelp->getNodesToGeoDist(nodelist[i], limit, roinodes, dists); diff --git a/src/Operations/OperationSurfaceSetCoordinates.cxx b/src/Operations/OperationSurfaceSetCoordinates.cxx new file mode 100644 index 0000000000000000000000000000000000000000..dd4fb0e63e13f54ee1a34824af02ace8cb835fd3 --- /dev/null +++ b/src/Operations/OperationSurfaceSetCoordinates.cxx @@ -0,0 +1,83 @@ +/*LICENSE_START*/ +/* + * Copyright (C) 2016 Washington University School of Medicine + * + * 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 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ +/*LICENSE_END*/ + +#include "OperationSurfaceSetCoordinates.h" +#include "OperationException.h" + +#include "MetricFile.h" +#include "SurfaceFile.h" + +using namespace caret; +using namespace std; + +AString OperationSurfaceSetCoordinates::getCommandSwitch() +{ + return "-surface-set-coordinates"; +} + +AString OperationSurfaceSetCoordinates::getShortDescription() +{ + return "MODIFY COORDINATES OF A SURFACE"; +} + +OperationParameters* OperationSurfaceSetCoordinates::getParameters() +{ + OperationParameters* ret = new OperationParameters(); + + ret->addSurfaceParameter(1, "surface-in", "the surface to use for the topology"); + + ret->addMetricParameter(2, "coord-metric", "the new coordinates, as a 3-column metric file"); + + ret->addSurfaceOutputParameter(3, "surface-out", "the new surface"); + + ret->setHelpText( + AString("Takes the topology from an existing surface file, and uses values from a metric file as coordinates to construct a new surface file.\n\n") + + "See -surface-coordinates-to-metric for how to get surface coordinates as a metric file, such that you can then modify them via metric commands, etc." + ); + return ret; +} + +void OperationSurfaceSetCoordinates::useParameters(OperationParameters* myParams, ProgressObject* myProgObj) +{ + LevelProgress myProgress(myProgObj); + SurfaceFile* inSurf = myParams->getSurface(1); + MetricFile* coordsIn = myParams->getMetric(2); + SurfaceFile* outSurf = myParams->getOutputSurface(3); + int numNodes = inSurf->getNumberOfNodes(); + if (coordsIn->getNumberOfNodes() != numNodes) + { + throw OperationException("coordinate metric file must have the same number of vertices as the input surface"); + } + if (coordsIn->getNumberOfColumns() != 3) + { + throw OperationException("coordinate metric file must have exactly 3 columns: x, y, and z"); + } + *outSurf = *inSurf; + vector combinedCoords(numNodes * 3); + for (int i = 0; i < 3; ++i) + { + const float* colData = coordsIn->getValuePointerForColumn(i); + for (int j = 0; j < numNodes; ++j) + { + combinedCoords[j * 3 + i] = colData[j]; + } + } + outSurf->setCoordinates(combinedCoords.data()); +} diff --git a/src/Operations/OperationSurfaceSetCoordinates.h b/src/Operations/OperationSurfaceSetCoordinates.h new file mode 100644 index 0000000000000000000000000000000000000000..88e51b813e82394a5ee575002a61c6220d914cbf --- /dev/null +++ b/src/Operations/OperationSurfaceSetCoordinates.h @@ -0,0 +1,41 @@ +#ifndef __OPERATION_SURFACE_SET_COORDINATES_H__ +#define __OPERATION_SURFACE_SET_COORDINATES_H__ + +/*LICENSE_START*/ +/* + * Copyright (C) 2016 Washington University School of Medicine + * + * 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 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ +/*LICENSE_END*/ + +#include "AbstractOperation.h" + +namespace caret { + + class OperationSurfaceSetCoordinates : public AbstractOperation + { + public: + static OperationParameters* getParameters(); + static void useParameters(OperationParameters* myParams, ProgressObject* myProgObj); + static AString getCommandSwitch(); + static AString getShortDescription(); + }; + + typedef TemplateAutoOperation AutoOperationSurfaceSetCoordinates; + +} + +#endif //__OPERATION_SURFACE_SET_COORDINATES_H__ diff --git a/src/Operations/OperationVolumeCapturePlane.cxx b/src/Operations/OperationVolumeCapturePlane.cxx index f7749cecaa2d5903650603a120d6539d02c09845..40ef9766bb98f0c65404ef34d78404ac8614078d 100644 --- a/src/Operations/OperationVolumeCapturePlane.cxx +++ b/src/Operations/OperationVolumeCapturePlane.cxx @@ -141,6 +141,11 @@ void OperationVolumeCapturePlane::useParameters(OperationParameters* myParams, P { CaretLogWarning("corner points describe non-orthogonal directions, image will be skewed"); } + float aspectRatio = (rightTraverse.length() / width) / (upTraverse.length() / height); + if (aspectRatio > 1.001f || aspectRatio < 0.999f) + { + CaretLogWarning("corner points and image dimensions are different aspect ratios, image will be stretched"); + } vector imageData(width * height * 4); for (int h = 0; h < height; ++h) { diff --git a/src/Operations/OperationVolumeCreate.cxx b/src/Operations/OperationVolumeCreate.cxx index 735371f3184e73c43e6c83bfcd7892a58cadbbe7..9e70399502ca80b830ac0b01bb7594fdb65867ba 100644 --- a/src/Operations/OperationVolumeCreate.cxx +++ b/src/Operations/OperationVolumeCreate.cxx @@ -98,7 +98,7 @@ void OperationVolumeCreate::useParameters(OperationParameters* myParams, Progres for (int i = 0; i < 3; ++i) { int dir = -1; - switch (orient[i].toAscii()) + switch (orient[i].toLatin1()) { case 'X': case 'x': diff --git a/src/Operations/OperationVolumeMath.cxx b/src/Operations/OperationVolumeMath.cxx index 21daf49eff6c04c6a747806989212ead45ee65af..5533a47690139ab0835bddfac836cc5ec53aa1eb 100644 --- a/src/Operations/OperationVolumeMath.cxx +++ b/src/Operations/OperationVolumeMath.cxx @@ -91,10 +91,10 @@ void OperationVolumeMath::useParameters(OperationParameters* myParams, ProgressO } int numInputs = myVarOpts.size(); int numVars = myVarNames.size(); - vector varVolumes(numVars, (VolumeFile*)NULL); + vector varVolumes(numVars, NULL); vector varSubvolumes(numVars, -1); if (numInputs == 0 && numVars == 0) throw OperationException("you must specify at least one input volume (-var), even if the expression doesn't use a variable"); - VolumeFile* first; + VolumeFile* first = NULL, *namefile = NULL; VolumeSpace mySpace; vector outDims; int numSubvols = -1; @@ -143,8 +143,10 @@ void OperationVolumeMath::useParameters(OperationParameters* myParams, ProgressO { numSubvols = thisSubvols; outDims = thisVolume->getOriginalDimensions(); - if (useSubvolume != -1) + if (useSubvolume == -1) { + namefile = thisVolume;//if it also doesn't use -subvolume, take map names from it + } else { outDims.resize(3);//change to output only one subvolume in the simplest way } } else { @@ -192,6 +194,7 @@ void OperationVolumeMath::useParameters(OperationParameters* myParams, ProgressO myVolOut->reinitialize(outDims, first->getSform());//DO NOT take volume type from first volume, because we don't check for or copy label tables, nor do we want to for (int s = 0; s < numSubvols; ++s) { + if (namefile != NULL) myVolOut->setMapName(s, namefile->getMapName(s)); for (int v = 0; v < numVars; ++v) { if (varSubvolumes[v] == -1) diff --git a/src/Operations/OperationVolumePalette.cxx b/src/Operations/OperationVolumePalette.cxx index 22b1c9ecf372a30ca621b2adc25f66844537210d..a74cc5ee276df0151fff90b62eb919827d47e166 100644 --- a/src/Operations/OperationVolumePalette.cxx +++ b/src/Operations/OperationVolumePalette.cxx @@ -83,6 +83,9 @@ OperationParameters* OperationVolumePalette::getParameters() thresholdOpt->addDoubleParameter(3, "min", "lower threshold"); thresholdOpt->addDoubleParameter(4, "max", "upper threshold"); + OptionalParameter* inversionOpt = ret->createOptionalParameter(15, "-inversion", "specify palette inversion"); + inversionOpt->addStringParameter(1, "type", "the type of inversion"); + AString myText = AString("The original volume file is overwritten with the modified version. By default, all columns of the volume file are adjusted ") + "to the new settings, use the -subvolume option to change only one subvolume. Mapping settings not specified in options will be taken from the first subvolume. " + "The argument must be one of the following:\n\n"; @@ -113,6 +116,13 @@ OperationParameters* OperationVolumePalette::getParameters() { myText += PaletteThresholdTestEnum::toName(myEnums3[i]) + "\n"; } + myText += "\nThe argument to -inversion must be one of the following:\n\n"; + vector myEnums4; + PaletteInvertModeEnum::getAllEnums(myEnums4); + for (int i = 0; i < (int)myEnums4.size(); ++i) + { + myText += PaletteInvertModeEnum::toName(myEnums4[i]) + "\n"; + } ret->setHelpText(myText); return ret; } @@ -204,6 +214,14 @@ void OperationVolumePalette::useParameters(OperationParameters* myParams, Progre myMapping.setThresholdMinimum(mytype, thresholdOpt->getDouble(3)); myMapping.setThresholdMaximum(mytype, thresholdOpt->getDouble(4)); } + OptionalParameter* inversionOpt = myParams->getOptionalParameter(15); + if (inversionOpt->m_present) + { + bool ok = false; + PaletteInvertModeEnum::Enum inversionType = PaletteInvertModeEnum::fromName(inversionOpt->getString(1), &ok); + if (!ok) throw OperationException("unrecognized palette inversion type: " + inversionOpt->getString(1)); + myMapping.setInvertedMode(inversionType); + } if (mySubvolume == -1) { for (int i = 0; i < myVolume.getNumberOfMaps(); ++i) diff --git a/src/Operations/OperationVolumeReorient.cxx b/src/Operations/OperationVolumeReorient.cxx index e3bfcbbee43bc2f514ecd1423094b36f2804d4bf..4c55ea99927c78fb6c84f6612c89ae381472b547 100644 --- a/src/Operations/OperationVolumeReorient.cxx +++ b/src/Operations/OperationVolumeReorient.cxx @@ -63,7 +63,7 @@ void OperationVolumeReorient::useParameters(OperationParameters* myParams, Progr VolumeSpace::OrientTypes orient[3]; for (int i = 0; i < 3; ++i) { - char id = orientString[i].toAscii(); + char id = orientString[i].toLatin1(); switch (id) { case 'L': diff --git a/src/Operations/OperationVolumeSetSpace.cxx b/src/Operations/OperationVolumeSetSpace.cxx index 3fe300d9cbe99b03a4b7d979f56d2d5aaee657e1..1edf3ed115d90170423777b6fa1445b0d1706d96 100644 --- a/src/Operations/OperationVolumeSetSpace.cxx +++ b/src/Operations/OperationVolumeSetSpace.cxx @@ -91,7 +91,7 @@ void OperationVolumeSetSpace::useParameters(OperationParameters* myParams, Progr for (int i = 0; i < 3; ++i) { int dir = -1; - switch (orient[i].toAscii()) + switch (orient[i].toLatin1()) { case 'X': case 'x': diff --git a/src/Operations/OperationVolumeStats.cxx b/src/Operations/OperationVolumeStats.cxx index 31e44d58d6b441f4d277c0bcba3fda927aeb4bab..f26ee2028a16e9edf1cd43cd160ee0b4a420a4e2 100644 --- a/src/Operations/OperationVolumeStats.cxx +++ b/src/Operations/OperationVolumeStats.cxx @@ -160,11 +160,12 @@ void OperationVolumeStats::useParameters(OperationParameters* myParams, Progress } bool matchSubvolMode = false; VolumeFile* myRoi = NULL; - const float* roiData = NULL; + int numRoiMaps = 1;//trick: pretend there is one ROI map with no ROI file OptionalParameter* roiOpt = myParams->getOptionalParameter(5); if (roiOpt->m_present) { myRoi = roiOpt->getVolume(1); + numRoiMaps = myRoi->getNumberOfMaps(); if (!input->matchesVolumeSpace(myRoi)) throw OperationException("roi doesn't match volume space of input"); if (roiOpt->getOptionalParameter(2)->m_present) { @@ -173,63 +174,55 @@ void OperationVolumeStats::useParameters(OperationParameters* myParams, Progress throw OperationException("-match-maps specified, but roi has different number of subvolumes than input"); } matchSubvolMode = true; - } else { - roiData = myRoi->getFrame(); } } bool showMapName = myParams->getOptionalParameter(6)->m_present; int numMaps = input->getNumberOfMaps(); + int startSubvol, endSubvol; if (subvol == -1) { - if (reduceOpt->m_present) - { - for (int i = 0; i < numMaps; ++i) - {//store result before printing anything, in case it throws while computing - if (matchSubvolMode) - { - roiData = myRoi->getFrame(i); - } - const float result = reduce(input->getFrame(i), frameSize, myop, roiData); - if (showMapName) cout << AString::number(i + 1) << ": " << input->getMapName(i) << ": "; - stringstream resultsstr; - resultsstr << setprecision(7) << result; - cout << resultsstr.str() << endl; + startSubvol = 0; + endSubvol = numMaps; + } else { + startSubvol = subvol; + endSubvol = subvol + 1; + } + for (int i = startSubvol; i < endSubvol; ++i) + { + if (showMapName) cout << AString::number(i + 1) << ":\t" << input->getMapName(i) << ":\t"; + if (matchSubvolMode) + {//trick: matchSubvolMode is only true when we have an roi + const float* roiData = myRoi->getFrame(i); + float result; + if (reduceOpt->m_present) + { + result = reduce(input->getFrame(i), frameSize, myop, roiData); + } else { + CaretAssert(percentileOpt->m_present); + result = percentile(input->getFrame(i), frameSize, percent, roiData); } + stringstream resultsstr; + resultsstr << setprecision(7) << result; + cout << resultsstr.str(); } else { - CaretAssert(percentileOpt->m_present); - for (int i = 0; i < numMaps; ++i) - {//store result before printing anything, in case it throws while computing - if (matchSubvolMode) + const float* roiData = NULL; + for (int j = 0; j < numRoiMaps; ++j) + { + if (myRoi != NULL) roiData = myRoi->getFrame(j); + float result; + if (reduceOpt->m_present) { - roiData = myRoi->getFrame(i); + result = reduce(input->getFrame(i), frameSize, myop, roiData); + } else { + CaretAssert(percentileOpt->m_present); + result = percentile(input->getFrame(i), frameSize, percent, roiData); } - const float result = percentile(input->getFrame(i), frameSize, percent, roiData); - if (showMapName) cout << AString::number(i + 1) << ": " << input->getMapName(i) << ": "; stringstream resultsstr; resultsstr << setprecision(7) << result; - cout << resultsstr.str() << endl; + if (j != 0) cout << "\t"; + cout << resultsstr.str(); } } - } else { - CaretAssert(subvol >= 0 && subvol < numMaps); - if (matchSubvolMode) - { - roiData = myRoi->getFrame(subvol); - } - if (reduceOpt->m_present) - { - const float result = reduce(input->getFrame(subvol), frameSize, myop, roiData); - if (showMapName) cout << AString::number(subvol + 1) << ": " << input->getMapName(subvol) << ": "; - stringstream resultsstr; - resultsstr << setprecision(7) << result; - cout << resultsstr.str() << endl; - } else { - CaretAssert(percentileOpt->m_present); - const float result = percentile(input->getFrame(subvol), frameSize, percent, roiData); - if (showMapName) cout << AString::number(subvol + 1) << ": " << input->getMapName(subvol) << ": "; - stringstream resultsstr; - resultsstr << setprecision(7) << result; - cout << resultsstr.str() << endl; - } + cout << endl; } } diff --git a/src/Operations/OperationVolumeWeightedStats.cxx b/src/Operations/OperationVolumeWeightedStats.cxx index 6e08029932b398eb583efab7b95f8a7f5bce8cd8..85e61368d3f5db3379c44f7acc10f4b5eb62d6a3 100644 --- a/src/Operations/OperationVolumeWeightedStats.cxx +++ b/src/Operations/OperationVolumeWeightedStats.cxx @@ -51,6 +51,7 @@ OperationParameters* OperationVolumeWeightedStats::getParameters() OptionalParameter* weightVolumeOpt = ret->createOptionalParameter(2, "-weight-volume", "use weights from a volume file"); weightVolumeOpt->addVolumeParameter(1, "weight-volume", "volume file containing the weights"); + weightVolumeOpt->createOptionalParameter(2, "-match-maps", "each subvolume of input uses the corresponding subvolume from the weights file"); OptionalParameter* subvolOpt = ret->createOptionalParameter(3, "-subvolume", "only display output for one subvolume"); subvolOpt->addStringParameter(1, "subvolume", "the subvolume number or name"); @@ -275,11 +276,21 @@ void OperationVolumeWeightedStats::useParameters(OperationParameters* myParams, OptionalParameter* weightVolumeOpt = myParams->getOptionalParameter(2); VolumeFile* myWeights = NULL; const float* weightData = NULL; + bool matchSubvolWeights = false; if (weightVolumeOpt->m_present) { myWeights = weightVolumeOpt->getVolume(1); if (!myWeights->matchesVolumeSpace(input)) throw OperationException("weight volume doesn't match volume space of input"); - weightData = myWeights->getFrame(); + if (weightVolumeOpt->getOptionalParameter(2)->m_present) + { + if (myWeights->getDimensions()[3] != dims[3]) + { + throw OperationException("-match-maps specified, but weights file has different number of subvolumes than input"); + } + matchSubvolWeights = true; + } else { + weightData = myWeights->getFrame(); + } } int subvol = -1; OptionalParameter* subvolOpt = myParams->getOptionalParameter(3); @@ -290,21 +301,20 @@ void OperationVolumeWeightedStats::useParameters(OperationParameters* myParams, } bool matchSubvolMode = false; VolumeFile* myRoi = NULL; - const float* roiData = NULL; + int numRoiMaps = 1;//trick: pretend there is one ROI map with no ROI file OptionalParameter* roiOpt = myParams->getOptionalParameter(4); if (roiOpt->m_present) { myRoi = roiOpt->getVolume(1); + numRoiMaps = myRoi->getNumberOfMaps(); if (!input->matchesVolumeSpace(myRoi)) throw OperationException("roi doesn't match volume space of input"); if (roiOpt->getOptionalParameter(2)->m_present) { if (myRoi->getDimensions()[3] != dims[3]) { - throw OperationException("-match-maps specified, but roi has different number of subvolumes than input"); + throw OperationException("-match-maps specified, but roi file has different number of subvolumes than input"); } matchSubvolMode = true; - } else { - roiData = myRoi->getFrame(); } } bool haveOp = false; @@ -345,13 +355,25 @@ void OperationVolumeWeightedStats::useParameters(OperationParameters* myParams, if (!haveOp) throw OperationException("you must specify an operation"); bool showMapName = myParams->getOptionalParameter(9)->m_present; int numMaps = input->getNumberOfMaps(); + int startSubvol, endSubvol; if (subvol == -1) { - for (int i = 0; i < numMaps; ++i) - {//store result before printing anything, in case it throws while computing - if (matchSubvolMode) + startSubvol = 0; + endSubvol = numMaps; + } else { + startSubvol = subvol; + endSubvol = subvol + 1; + } + const float* roiData = NULL; + for (int i = startSubvol; i < endSubvol; ++i) + { + if (showMapName) cout << AString::number(i + 1) << ":\t" << input->getMapName(i) << ":\t"; + if (matchSubvolMode) + {//trick: matchSubvolMode is only true when we have an roi + roiData = myRoi->getFrame(i); + if (matchSubvolWeights) { - roiData = myRoi->getFrame(i); + weightData = myWeights->getFrame(i); } float result; if (weightData != NULL) @@ -360,26 +382,30 @@ void OperationVolumeWeightedStats::useParameters(OperationParameters* myParams, } else { result = doOperationSingleWeight(input->getFrame(i), constWeight, frameSize, myop, roiData, argument); } - if (showMapName) cout << AString::number(i + 1) << ": " << input->getMapName(i) << ": "; stringstream resultsstr; resultsstr << setprecision(7) << result; - cout << resultsstr.str() << endl; - } - } else { - if (matchSubvolMode) - { - roiData = myRoi->getFrame(subvol); - } - float result; - if (weightData != NULL) - { - result = doOperation(input->getFrame(subvol), weightData, frameSize, myop, roiData, argument); + cout << resultsstr.str(); } else { - result = doOperationSingleWeight(input->getFrame(subvol), constWeight, frameSize, myop, roiData, argument); + if (matchSubvolWeights) + { + weightData = myWeights->getFrame(i); + } + for (int j = 0; j < numRoiMaps; ++j) + { + if (myRoi != NULL) roiData = myRoi->getFrame(j); + float result; + if (weightData != NULL) + { + result = doOperation(input->getFrame(i), weightData, frameSize, myop, roiData, argument); + } else { + result = doOperationSingleWeight(input->getFrame(i), constWeight, frameSize, myop, roiData, argument); + } + stringstream resultsstr; + resultsstr << setprecision(7) << result; + if (j != 0) cout << "\t"; + cout << resultsstr.str(); + } } - if (showMapName) cout << AString::number(subvol + 1) << ": " << input->getMapName(subvol) << ": "; - stringstream resultsstr; - resultsstr << setprecision(7) << result; - cout << resultsstr.str() << endl; + cout << endl; } } diff --git a/src/Operations/OperationZipSceneFile.cxx b/src/Operations/OperationZipSceneFile.cxx index 209acbe57d98b1a4b301cdf4106803d9cb10ddb2..f0d1891d743c878df179899eed19809f37817de0 100644 --- a/src/Operations/OperationZipSceneFile.cxx +++ b/src/Operations/OperationZipSceneFile.cxx @@ -66,7 +66,7 @@ OperationParameters* OperationZipSceneFile::getParameters() baseOpt->addStringParameter(1, "directory", "the directory"); ret->setHelpText("If zip-file already exists, it will be overwritten. " - "If -base-dir is not specified, the directory containing the scene file is used for the base directory. " + "If -base-dir is not specified, the base directory will be automatically set to the lowest level directory containing all files. " "The scene file must contain only relative paths, and no data files may be outside the base directory."); return ret; } @@ -103,39 +103,53 @@ void OperationZipSceneFile::createZipFile(const AString& sceneFileName, if (outputSubDirectory.isEmpty()) { throw OperationException("extract-dir must contain characters"); } + if (FileInformation(outputSubDirectory).isAbsolute()) + { + CaretLogWarning("You have specified that the zip file should extract to an absolute path, this is generally frowned on. " + "The parameter should generally be a string without '/' or '\\' in it."); + } else { + if (outputSubDirectory.indexOfAnyChar("/\\") != -1)//assume backslashes work too + { + CaretLogWarning("You have specified that the zipfile should create multiple levels of otherwise empty directories " + "before the file paths starting from the base directory, this is probably going to be inconvenient. " + "The parameter should generally be a string without '/' or '\\' in it."); + } + } + + SceneFile sceneFile; + sceneFile.readFile(sceneFileName); AString myBaseDir; if ( ! baseDirectory.isEmpty()) { myBaseDir = QDir::cleanPath(QDir(baseDirectory).absolutePath()); } else { - myBaseDir = QDir::cleanPath(sceneFileInfo.getAbsolutePath()); + AString baseDirectoryName; + std::vector missingFileNames; + AString errorMessage; + const bool validBasePathFlag = sceneFile.findBaseDirectoryForDataFiles(baseDirectoryName, + missingFileNames, + errorMessage); + if ( ! validBasePathFlag) { + throw OperationException("Automatic Base Directory Failed: " + + errorMessage); + } + + myBaseDir = QDir::cleanPath(baseDirectoryName); } if (!myBaseDir.endsWith('/'))//root is a special case, if we didn't handle it differently it would end up looking for "//somefile" {//this is actually because the path function strips the final "/" from the path, but not when it is just "/" myBaseDir += "/";//so, add the trailing slash to the path } + AString sceneFilePath = QDir::cleanPath(sceneFileInfo.getAbsoluteFilePath());//resolve filenames to open from the spec file's location, NOT from current directory if (!sceneFilePath.startsWith(myBaseDir)) { throw OperationException("scene file lies outside the base directory"); } - if (FileInformation(outputSubDirectory).isAbsolute()) - { - CaretLogWarning("You have specified that the zip file should extract to an absolute path, this is generally frowned on. " - "The parameter should generally be a string without '/' or '\\' in it."); - } else { - if (outputSubDirectory.indexOfAnyChar("/\\") != -1)//assume backslashes work too - { - CaretLogWarning("You have specified that the zipfile should create multiple levels of otherwise empty directories " - "before the file paths starting from the base directory, this is probably going to be inconvenient. " - "The parameter should generally be a string without '/' or '\\' in it."); - } - } + set allFiles; allFiles.insert(sceneFilePath); - SceneFile sceneFile; - sceneFile.readFile(sceneFileName); const int numScenes = sceneFile.getNumberOfScenes(); for (int i = 0; i < numScenes; ++i) { @@ -291,162 +305,3 @@ void OperationZipSceneFile::createZipFile(const AString& sceneFileName, break; } } - -//void OperationZipSceneFile::useParameters(OperationParameters* myParams, ProgressObject* myProgObj) -//{ -// LevelProgress myProgress(myProgObj); -// AString sceneFileName = myParams->getString(1); -// AString outputSubDirectory = myParams->getString(2); -// AString zipFileName = myParams->getString(3); -// FileInformation sceneFileInfo(sceneFileName); -// if (outputSubDirectory.isEmpty()) { -// throw OperationException("extract-dir must contain characters"); -// } -// OptionalParameter* baseOpt = myParams->getOptionalParameter(4); -// AString myBaseDir; -// if (baseOpt->m_present) -// { -// myBaseDir = QDir::cleanPath(QDir(baseOpt->getString(1)).absolutePath()); -// } else { -// myBaseDir = QDir::cleanPath(sceneFileInfo.getAbsolutePath()); -// } -// if (!myBaseDir.endsWith('/'))//root is a special case, if we didn't handle it differently it would end up looking for "//somefile" -// {//this is actually because the path function strips the final "/" from the path, but not when it is just "/" -// myBaseDir += "/";//so, add the trailing slash to the path -// } -// AString sceneFilePath = QDir::cleanPath(sceneFileInfo.getAbsoluteFilePath());//resolve filenames to open from the spec file's location, NOT from current directory -// if (!sceneFilePath.startsWith(myBaseDir)) -// { -// throw OperationException("scene file lies outside the base directory"); -// } -// if (FileInformation(outputSubDirectory).isAbsolute()) -// { -// CaretLogWarning("You have specified that the zip file should extract to an absolute path, this is generally frowned on. " -// "The parameter should generally be a string without '/' or '\\' in it."); -// } else { -// if (outputSubDirectory.indexOfAnyChar("/\\") != -1)//assume backslashes work too -// { -// CaretLogWarning("You have specified that the zipfile should create multiple levels of otherwise empty directories " -// "before the file paths starting from the base directory, this is probably going to be inconvenient. " -// "The parameter should generally be a string without '/' or '\\' in it."); -// } -// } -// set allFiles; -// allFiles.insert(sceneFilePath); -// SceneFile sceneFile; -// sceneFile.readFile(sceneFileName); -// const int numScenes = sceneFile.getNumberOfScenes(); -// for (int i = 0; i < numScenes; ++i) -// { -// Scene* thisScene = sceneFile.getSceneAtIndex(i); -// SceneAttributes* myAttrs = thisScene->getAttributes(); -// const SceneClass* guiMgrClass = thisScene->getClassWithName("guiManager"); -// if (guiMgrClass == NULL) -// { -// throw OperationException("scene '" + thisScene->getName() + "' is missing guiManager class"); -// } -// const SceneClass* sessMgrClass = guiMgrClass->getClass("m_sessionManager"); -// if (sessMgrClass == NULL) -// { -// throw OperationException("scene '" + thisScene->getName() + "' is missing m_sessionManager class"); -// } -// const SceneClassArray* brainArray = sessMgrClass->getClassArray("m_brains"); -// if (brainArray == NULL) -// { -// throw OperationException("scene '" + thisScene->getName() + "' is missing m_brains class array"); -// } -// const int numBrainClasses = brainArray->getNumberOfArrayElements(); -// for (int j = 0; j < numBrainClasses; ++j) -// { -// const SceneClass* brainClass = brainArray->getClassAtIndex(j); -// const SceneClass* specClass = brainClass->getClass("specFile"); -// if (specClass == NULL) -// { -// throw OperationException("scene '" + thisScene->getName() + "' is missing specFile class in m_brains element " + AString::number(j)); -// } -// SpecFile tempSpec; -// tempSpec.restoreFromScene(myAttrs, specClass); -// vector tempNames = tempSpec.getAllDataFileNamesSelectedForLoading(); -// int numNames = (int)tempNames.size(); -// for (int k = 0; k < numNames; ++k) -// { -// if (DataFile::isFileOnNetwork(tempNames[k])) -// { -// cout << "skipping network file '" << tempNames[k] << "'" << endl; -// continue; -// } -// AString thisName = QDir::cleanPath(tempNames[k]); -// if (allFiles.insert(thisName).second) -// { -// if (FileInformation(thisName).isRelative()) -// { -// throw OperationException("scene '" + thisScene->getName() + "' contains an unresolved relative path: '" + tempNames[k] + "'"); -// } -// if (!thisName.startsWith(myBaseDir)) -// { -// throw OperationException("scene '" + thisScene->getName() + "' contains a file outside the base directory: '" + thisName + "', try using -base-dir"); -// } -// } -// } -// } -// } -// QFile zipFileObject(zipFileName); -// QuaZip zipFile(&zipFileObject); -// if (!zipFile.open(QuaZip::mdCreate)) -// { -// throw OperationException("Unable to open ZIP File \"" -// + zipFileName -// + "\" for writing."); -// } -// static const char *myUnits[9] = {" B ", " KB", " MB", " GB", " TB", " PB", " EB", " ZB", " YB"}; -// for (set::iterator iter = allFiles.begin(); iter != allFiles.end(); ++iter) -// { -// AString dataFileName = *iter; -// AString unzippedDataFileName = outputSubDirectory + "/" + dataFileName.mid(myBaseDir.size());//we know the string matches to the length of myBaseDir, and is cleaned, so we can just chop the right number of characters off -// QFile dataFileIn(dataFileName); -// if (!dataFileIn.open(QFile::ReadOnly)) { -// throw OperationException("Unable to open \"" + dataFileName + "\" for reading: " + dataFileIn.errorString()); -// } -// float fileSize = (float)dataFileIn.size(); -// int unit = 0; -// while (unit < 8 && fileSize >= 1000.0f)//don't let there be 4 digits to the left of decimal point -// { -// ++unit; -// fileSize /= 1000.0f;//use GB and friends, not GiB -// } -// if (unit > 0) -// { -// cout << AString::number(fileSize, 'f', 2); -// } else { -// cout << AString::number(fileSize); -// } -// cout << myUnits[unit] << " \t" << unzippedDataFileName; -// cout.flush();//don't endl until it finishes -// -// QuaZipNewInfo zipNewInfo(unzippedDataFileName, -// dataFileName); -// zipNewInfo.externalAttr |= (6 << 22L) | (6 << 19L) | (4 << 16L);//make permissions 664 -// -// QuaZipFile dataFileOut(&zipFile); -// if (!dataFileOut.open(QIODevice::WriteOnly, zipNewInfo)) { -// throw OperationException("Unable to open zip output for \"" + dataFileName + "\""); -// } -// -// const qint64 BUFFER_SIZE = 1024 * 1024; -// vector buffer(BUFFER_SIZE); -// -// while (dataFileIn.atEnd() == false) { -// const qint64 numRead = dataFileIn.read(buffer.data(), BUFFER_SIZE); -// if (numRead < 0) throw OperationException("Error reading from data file"); -// if (numRead > 0) { -// qint64 result = dataFileOut.write(buffer.data(), numRead); -// if (result != numRead) throw OperationException("Error writing to zip file"); -// } -// } -// -// dataFileIn.close(); -// dataFileOut.close(); -// cout << endl; -// } -// zipFile.close(); -//} diff --git a/src/OperationsBase/CMakeLists.txt b/src/OperationsBase/CMakeLists.txt index 4b8325228c72666c2736e318ca985b200b22e086..98b57cd042c188db88d4613b66aa4533dfe4d49e 100644 --- a/src/OperationsBase/CMakeLists.txt +++ b/src/OperationsBase/CMakeLists.txt @@ -3,15 +3,17 @@ # PROJECT (OperationsBase) -# -# Need XML from Qt -# -SET(QT_DONT_USE_QTGUI) - # # Add QT for includes # -INCLUDE (${QT_USE_FILE}) +if(Qt5_FOUND) + include_directories(${Qt5Core_INCLUDE_DIRS}) + #include_directories(${Qt5Network_INCLUDE_DIRS}) +endif() +IF (QT4_FOUND) + SET(QT_DONT_USE_QTGUI) + INCLUDE(${QT_USE_FILE}) +ENDIF () # # Create the helper library @@ -26,6 +28,8 @@ OperationParameters.cxx OperationParametersEnum.cxx ) +TARGET_LINK_LIBRARIES(OperationsBase ${CARET_QT5_LINK}) + # # Find Headers # diff --git a/src/Palette/CMakeLists.txt b/src/Palette/CMakeLists.txt index c36de81622f27fd71f32f4570457104db55eb487..adf533d11084033df6f28fd76c7f124ce6361bb3 100644 --- a/src/Palette/CMakeLists.txt +++ b/src/Palette/CMakeLists.txt @@ -3,15 +3,16 @@ # project (Palette) -# -# Need XML from Qt -# -SET(QT_DONT_USE_QTGUI) - # # Add QT for includes # -INCLUDE (${QT_USE_FILE}) +if(Qt5_FOUND) + SET(QT_DONT_USE_QTGUI) + include_directories(${Qt5Core_INCLUDE_DIRS}) +endif() +IF (QT4_FOUND) + INCLUDE(${QT_USE_FILE}) +ENDIF () # @@ -24,6 +25,9 @@ PaletteColorMapping.h PaletteColorMappingSaxReader.h PaletteColorMappingXmlElements.h PaletteEnums.h +PaletteHistogramRangeModeEnum.h +PaletteInvertModeEnum.h +PaletteModifiedStatusEnum.h PaletteNormalizationModeEnum.h PaletteScalarAndColor.h PaletteThresholdRangeModeEnum.h @@ -33,11 +37,16 @@ PaletteColorBarValuesModeEnum.cxx PaletteColorMapping.cxx PaletteColorMappingSaxReader.cxx PaletteEnums.cxx +PaletteHistogramRangeModeEnum.cxx +PaletteInvertModeEnum.cxx +PaletteModifiedStatusEnum.cxx PaletteNormalizationModeEnum.cxx PaletteScalarAndColor.cxx PaletteThresholdRangeModeEnum.cxx ) +TARGET_LINK_LIBRARIES(Palette ${CARET_QT5_LINK}) + # # Find Headers # diff --git a/src/Palette/Palette.cxx b/src/Palette/Palette.cxx index 37fa2b95188104b610352e10473f4371a6957dea..015da54750c2af64fc0ad5a6d9a69d57fb58df76 100644 --- a/src/Palette/Palette.cxx +++ b/src/Palette/Palette.cxx @@ -18,6 +18,7 @@ */ /*LICENSE_END*/ +#include #include #include "CaretAssert.h" @@ -107,13 +108,14 @@ Palette::toString() const { AString s; - s += "[name=" + this->name + ", "; + s += "name=" + this->name + "\n"; uint64_t num = this->paletteScalars.size(); for (uint64_t i = 0; i < num; i++) { - if (i > 0) s += ","; - s += this->paletteScalars[i]->toString(); + s += (" " + + this->paletteScalars[i]->toString() + + "\n"); } - s += "]"; + s += "\n"; return s; } @@ -446,47 +448,37 @@ Palette::getPaletteColor( } } } - - // if (paletteIndex != binaryPaletteIndex) { - // std::cout << "FAILED palette indices correct=" - // << paletteIndex << " binary-index=" << binaryPaletteIndex << std::endl; - // } - -// /* -// * Always interpolate if there are only two colors -// */ -// if (numScalarColors == 2) { -// interpolateColorFlag = true; -// } } } if (paletteIndex >= 0) { const PaletteScalarAndColor* psac = this->getScalarAndColor(paletteIndex); - psac->getColor(rgbaOut); - if (interpolateColorFlag && - (paletteIndex < (numScalarColors - 1))) { - const PaletteScalarAndColor* psacBelow = this->getScalarAndColor(paletteIndex + 1); - float totalDiff = psac->getScalar() - psacBelow->getScalar(); - if (totalDiff != 0.0) { - float offset = scalar - psacBelow->getScalar(); - float percentAbove = offset / totalDiff; - float percentBelow = 1.0f - percentAbove; - if ( ! psacBelow->isNoneColor()) { - const float* rgbaAbove = psac->getColor(); - const float* rgbaBelow = psacBelow->getColor(); - - rgbaOut[0] = (percentAbove * rgbaAbove[0] - + percentBelow * rgbaBelow[0]); - rgbaOut[1] = (percentAbove * rgbaAbove[1] - + percentBelow * rgbaBelow[1]); - rgbaOut[2] = (percentAbove * rgbaAbove[2] - + percentBelow * rgbaBelow[2]); + if (psac->isNoneColor()) { + rgbaOut[3] = 0.0; + } + else { + psac->getColor(rgbaOut); // color assigned here + if (interpolateColorFlag && + (paletteIndex < (numScalarColors - 1))) { + const PaletteScalarAndColor* psacBelow = this->getScalarAndColor(paletteIndex + 1); + float totalDiff = psac->getScalar() - psacBelow->getScalar(); + if (totalDiff != 0.0) { + float offset = scalar - psacBelow->getScalar(); + float percentAbove = offset / totalDiff; + float percentBelow = 1.0f - percentAbove; + if ( ! psacBelow->isNoneColor()) { + const float* rgbaAbove = psac->getColor(); + const float* rgbaBelow = psacBelow->getColor(); + + rgbaOut[0] = (percentAbove * rgbaAbove[0] + + percentBelow * rgbaBelow[0]); + rgbaOut[1] = (percentAbove * rgbaAbove[1] + + percentBelow * rgbaBelow[1]); + rgbaOut[2] = (percentAbove * rgbaAbove[2] + + percentBelow * rgbaBelow[2]); + } } } } - else if (psac->isNoneColor()) { - rgbaOut[3] = 0.0f; - } } } } @@ -524,3 +516,293 @@ Palette::isModified() const return this->modifiedFlag; } +/** + * @return An sign separate inverted version of this palette. + * The positive and negative sections are separately inverted. + * + * For the POSITIVE region, the conversion formula is: + * IS = -S + (Min + Max) + * Where: + * IS => Inverted Scalar + * S => Scalar + * Min => Minimum positive value + * Max => Maximum positive value + * + * The formula was derived by charting: + * Plot (x=Min, y=Max) and (x=Max, y=Min) + * The slope is negative one [dx = (max - min); dy = (min-max)] + * So Y = -(dy/dx) * X + C + * Y = -X + C + * C = X + Y + * C = (Min + Max) + * And finally: + * Y = -X + (Min + Max) + * + * Example: (1.0, Red), (0.4, Yellow), (0, Black), (-0.3, Green), (-1.0, Blue) + * becomes (1.0, Black), (0.6, Yellow), (1.0, Red), (0, Blue), (-0.7, Green), (-1.0, Black) + */ +const Palette* +Palette::getSignSeparateInvertedPalette() const +{ + if ( ! m_signSeparateInvertedPalette) { + + m_signSeparateInvertedPalette.reset(createSignSeparateInvertedPalette()); + } + + return m_signSeparateInvertedPalette.get(); +} + +/** + * @return New instance of sign separate palette + */ +Palette* +Palette::createSignSeparateInvertedPalette() const +{ + float leastPositiveScalar = 1000.0f; + float mostPositiveScalar = -1000.0f; + float leastNegativeScalar = -1000.0f; + float mostNegativeScalar = 1000.0f; + bool havePositivesFlag = false; + bool haveNegativesFlag = false; + for (const auto ps : paletteScalars) { + const float scalar = ps->getScalar(); + + if (scalar >= 0.0) { + if (scalar > mostPositiveScalar) mostPositiveScalar = scalar; + if (scalar < leastPositiveScalar) leastPositiveScalar = scalar; + if (scalar > 0.0) { + havePositivesFlag = true; + } + } + + if (scalar <= 0.0) { + if (scalar > leastNegativeScalar) leastNegativeScalar = scalar; + if (scalar < mostNegativeScalar) mostNegativeScalar = scalar; + if (scalar < 0.0) { + haveNegativesFlag = true; + } + } + } + + std::deque positives; + std::deque negatives; + + for (const auto ps : paletteScalars) { + const float scalar = ps->getScalar(); + if (havePositivesFlag) { + if (scalar >= 0.0) { + PaletteScalarAndColor* psc = new PaletteScalarAndColor(*ps); + psc->setScalar((leastPositiveScalar + mostPositiveScalar) - scalar); + positives.push_front(psc); + } + } + if (haveNegativesFlag) { + if (scalar <= 0.0) { + PaletteScalarAndColor* psc = new PaletteScalarAndColor(*ps); + psc->setScalar((leastNegativeScalar + mostNegativeScalar) - scalar); + negatives.push_front(psc); + } + } + } + + Palette* palette = new Palette(); + palette->setName(getName()); + palette->paletteScalars.insert(palette->paletteScalars.end(), + positives.begin(), positives.end()); + + palette->paletteScalars.insert(palette->paletteScalars.end(), + negatives.begin(), negatives.end()); + + palette->clearModified(); + + return palette; +} + + + + +/** + * @return An inverted version of this palette. An inverted + * palette may be useful when data is all negative and the + * palette is for positive data. + * + * Example: (1.0, Red), (0.4, Yellow), (-0.3, Green), (-1.0, Blue) + * becomes (1.0, Blue), (0.3, Green), (-0.4, Yellow), (-1.0, Red) + */ +const Palette* +Palette::getInvertedPalette() const +{ + if ( ! m_invertedPalette) { + Palette* palette = new Palette(*this); + std::reverse(palette->paletteScalars.begin(), palette->paletteScalars.end()); + + /* + * Reverse colors and then flip sign of scalars so + * that it is still negative to positive + */ + for (auto scalar : palette->paletteScalars) { + scalar->setScalar( - scalar->getScalar()); + } + + palette->clearModified(); + + m_invertedPalette.reset(palette); + } + + return m_invertedPalette.get(); +} + +/** + * @return Name of the default palette. + */ +AString +Palette::getDefaultPaletteName() +{ + return ROY_BIG_BL_PALETTE_NAME; +} + + +/** + * @return An NONE separate inverted version of this palette. + * The positive and negative sections are separately inverted + * around the NONE color. If the palette contains zero it MUST + * be the NONE color. + * + * If the palette DOES NOT have NONE or if it has both + * NONE and zero, the sign separate palette is created and returned. + * + * For the POSITIVE region, the conversion formula is: + * IS = -S + (Min + Max) + * Where: + * IS => Inverted Scalar + * S => Scalar + * Min => Minimum positive value + * Max => Maximum positive value + * + * The formula was derived by charting: + * Plot (x=Min, y=Max) and (x=Max, y=Min) + * The slope is negative one [dx = (max - min); dy = (min-max)] + * So Y = -(dy/dx) * X + C + * Y = -X + C + * C = X + Y + * C = (Min + Max) + * And finally: + * Y = -X + (Min + Max) + * + * Example: (1.0, Red), (0.4, Yellow), (0, Black), (-0.3, Green), (-1.0, Blue) + * becomes (1.0, Black), (0.6, Yellow), (1.0, Red), (0, Blue), (-0.7, Green), (-1.0, Black) + */ +const Palette* +Palette::getNoneSeparateInvertedPalette() const +{ + if ( ! m_noneSeparateInvertedPalette) { + float leastPositiveScalar = 1000.0f; + float mostPositiveScalar = -1000.0f; + float leastNegativeScalar = -1000.0f; + float mostNegativeScalar = 1000.0f; + int32_t noneColorIndex = -1; + int32_t zeroIndex = -1; + + const int32_t numScalars = static_cast(this->paletteScalars.size()); + for (int32_t i = 0; i < numScalars; i++) { + CaretAssertVectorIndex(this->paletteScalars, i); + const float scalar = this->paletteScalars[i]->getScalar(); + + if (this->paletteScalars[i]->isNoneColor()) { + noneColorIndex = i; + } + else { + if (scalar == 0.0) { + zeroIndex = i; + } + if (scalar >= 0.0) { + if (scalar > mostPositiveScalar) mostPositiveScalar = scalar; + if (scalar < leastPositiveScalar) leastPositiveScalar = scalar; + } + if (scalar <= 0.0) { + if (scalar > leastNegativeScalar) leastNegativeScalar = scalar; + if (scalar < mostNegativeScalar) mostNegativeScalar = scalar; + } + + } + } + + bool badNoneLocationFlag = false; + if (noneColorIndex >= 0) { + CaretAssertVectorIndex(this->paletteScalars, noneColorIndex); + const float noneValue = this->paletteScalars[noneColorIndex]->getScalar(); + if ((noneValue >= leastPositiveScalar) + && (noneValue <= mostPositiveScalar)) { + badNoneLocationFlag = true; + } + else if ((noneValue >= mostNegativeScalar) + && (noneValue <= leastNegativeScalar)) { + badNoneLocationFlag = true; + } + } + + bool validNoneInvertFlag = true; + if (badNoneLocationFlag) { + const AString msg("Palette named " + getName() + + " has NONE within the positive or negative range."); + CaretAssertMessage(0, msg); + validNoneInvertFlag = false; + } + else if (noneColorIndex < 0) { + /* no NONE so same as sign separate palette */ + validNoneInvertFlag = false; + } + else if (zeroIndex >= 0) { + /* both none and zero not allowed */ + const AString msg("Palette named " + getName() + + " has both NONE and zero so NONE separated palette cannot be created."); + CaretAssertMessage(0, msg); + validNoneInvertFlag = false; + } + if ( ! validNoneInvertFlag) { + m_noneSeparateInvertedPalette.reset(createSignSeparateInvertedPalette()); + return m_noneSeparateInvertedPalette.get(); + } + + std::deque positives; + std::deque negatives; + + for (int32_t i = 0; i < numScalars; i++) { + CaretAssertVectorIndex(this->paletteScalars, i); + const PaletteScalarAndColor* ps = this->paletteScalars[i]; + const float scalar = ps->getScalar(); + + if (i < noneColorIndex) { + PaletteScalarAndColor* psc = new PaletteScalarAndColor(*ps); + psc->setScalar((leastPositiveScalar + mostPositiveScalar) - scalar); + positives.push_front(psc); + } + else if (i > noneColorIndex) { + PaletteScalarAndColor* psc = new PaletteScalarAndColor(*ps); + psc->setScalar((leastNegativeScalar + mostNegativeScalar) - scalar); + negatives.push_front(psc); + } + } + + + Palette* palette = new Palette(); + palette->setName(getName()); + palette->paletteScalars.insert(palette->paletteScalars.end(), + positives.begin(), positives.end()); + + CaretAssertVectorIndex(this->paletteScalars, noneColorIndex); + palette->paletteScalars.push_back(new PaletteScalarAndColor(*this->paletteScalars[noneColorIndex])); + + palette->paletteScalars.insert(palette->paletteScalars.end(), + negatives.begin(), negatives.end()); + + palette->clearModified(); + + m_noneSeparateInvertedPalette.reset(palette); + } + + return m_noneSeparateInvertedPalette.get(); +} + + + diff --git a/src/Palette/Palette.h b/src/Palette/Palette.h index 7420269b7d0f7499bcc045ed1a3186b803567ff6..f87cb599964b857a7bdb36752d4ddbd7e596b576 100644 --- a/src/Palette/Palette.h +++ b/src/Palette/Palette.h @@ -21,6 +21,7 @@ */ /*LICENSE_END*/ +#include #include #include @@ -102,6 +103,14 @@ namespace caret { bool isModified() const; + const Palette* getInvertedPalette() const; + + const Palette* getSignSeparateInvertedPalette() const; + + const Palette* getNoneSeparateInvertedPalette() const; + + static AString getDefaultPaletteName(); + public: /**Name of gray interpolate palette */ static const AString GRAY_INTERP_PALETTE_NAME; @@ -116,6 +125,8 @@ namespace caret { static const AString ROY_BIG_BL_PALETTE_NAME; private: + Palette* createSignSeparateInvertedPalette() const; + /**has this object been modified. (DO NOT CLONE) */ bool modifiedFlag; @@ -125,6 +136,14 @@ namespace caret { /**The scalars in the palette. */ std::vector paletteScalars; + /** The inverted palette is lazily initialized */ + mutable std::unique_ptr m_invertedPalette; + + /** The inverted palette with negative inverted separate from positive */ + mutable std::unique_ptr m_signSeparateInvertedPalette; + + /** The inverted palette with negative inverted separate from positive */ + mutable std::unique_ptr m_noneSeparateInvertedPalette; }; diff --git a/src/Palette/PaletteColorMapping.cxx b/src/Palette/PaletteColorMapping.cxx index c96ce04cc6940193d86a780539520df8ddfa98ad..6646db08f64bdcdd3ce92732b5494405b94ed20b 100644 --- a/src/Palette/PaletteColorMapping.cxx +++ b/src/Palette/PaletteColorMapping.cxx @@ -25,6 +25,7 @@ #include "AnnotationColorBarNumericText.h" #include "CaretLogger.h" #include "CaretOMP.h" +#include "DeveloperFlagsEnum.h" #include "EventManager.h" #include "EventPaletteGetByName.h" #include "FastStatistics.h" @@ -71,7 +72,8 @@ PaletteColorMapping::PaletteColorMapping(const PaletteColorMapping& o) : CaretObject(o) { this->initializeMembersPaletteColorMapping(); - this->copyHelper(o); + this->copyHelper(o, + true); } /** @@ -82,7 +84,8 @@ PaletteColorMapping::operator=(const PaletteColorMapping& o) { if (this != &o) { CaretObject::operator=(o); - this->copyHelper(o); + this->copyHelper(o, + true); }; return *this; } @@ -92,19 +95,29 @@ PaletteColorMapping::operator=(const PaletteColorMapping& o) * color mapping. * @param pcm * Color mapping that is copied to this. + * @param copyHistogramAttributesFlag + * If true copy histogram attributes. */ void -PaletteColorMapping::copy(const PaletteColorMapping& pcm) +PaletteColorMapping::copy(const PaletteColorMapping& pcm, + const bool copyHistogramAttributesFlag) { - this->copyHelper(pcm); + this->copyHelper(pcm, + copyHistogramAttributesFlag); setModified(); } /** * Helps with copy constructor and assignment operator. + * + * @param pcm + * Color mapping that is copied to this. + * @param copyHistogramAttributesFlag + * If true copy histogram attributes. */ void -PaletteColorMapping::copyHelper(const PaletteColorMapping& pcm) +PaletteColorMapping::copyHelper(const PaletteColorMapping& pcm, + const bool copyHistogramAttributesFlag) { this->autoScalePercentageNegativeMaximum = pcm.autoScalePercentageNegativeMaximum; this->autoScalePercentageNegativeMinimum = pcm.autoScalePercentageNegativeMinimum; @@ -116,6 +129,7 @@ PaletteColorMapping::copyHelper(const PaletteColorMapping& pcm) this->displayPositiveDataFlag = pcm.displayPositiveDataFlag; this->displayZeroDataFlag = pcm.displayZeroDataFlag; this->interpolatePaletteFlag = pcm.interpolatePaletteFlag; + this->invertedMode = pcm.invertedMode; this->scaleMode = pcm.scaleMode; this->selectedPaletteName = pcm.selectedPaletteName; this->userScaleNegativeMaximum = pcm.userScaleNegativeMaximum; @@ -134,11 +148,20 @@ PaletteColorMapping::copyHelper(const PaletteColorMapping& pcm) this->thresholdShowFailureInGreen = pcm.thresholdShowFailureInGreen; this->thresholdRangeMode = pcm.thresholdRangeMode; this->thresholdNegMinPosMaxLinked = pcm.thresholdNegMinPosMaxLinked; - this->numericFormatMode = pcm.numericFormatMode; - this->precisionDigits = pcm.precisionDigits; - this->numericSubdivisionCount = pcm.numericSubdivisionCount; + if (copyHistogramAttributesFlag) { + this->histogramRangeMode = pcm.histogramRangeMode; + this->histogramBarsVisible = pcm.histogramBarsVisible; + this->histogramEnvelopeVisible = pcm.histogramEnvelopeVisible; + this->histogramBarsColor = pcm.histogramBarsColor; + this->histogramEnvelopeColor = pcm.histogramEnvelopeColor; + this->histogramEnvelopeLineWidthPercentage = pcm.histogramEnvelopeLineWidthPercentage; + this->histogramNumberOfBuckets = pcm.histogramNumberOfBuckets; + } + this->colorBarNumericFormatMode = pcm.colorBarNumericFormatMode; + this->colorBarPrecisionDigits = pcm.colorBarPrecisionDigits; + this->colorBarNumericSubdivisionCount = pcm.colorBarNumericSubdivisionCount; this->colorBarValuesMode = pcm.colorBarValuesMode; - this->showTickMarksSelected = pcm.showTickMarksSelected; + this->colorBarShowTickMarksSelected = pcm.colorBarShowTickMarksSelected; this->clearModified(); } @@ -153,6 +176,8 @@ PaletteColorMapping::copyHelper(const PaletteColorMapping& pcm) bool PaletteColorMapping::operator==(const PaletteColorMapping& pcm) const { + bool allMatchFlag = false; + if ((this->autoScalePercentageNegativeMaximum == pcm.autoScalePercentageNegativeMaximum) && (this->autoScalePercentageNegativeMinimum == pcm.autoScalePercentageNegativeMinimum) && (this->autoScalePercentagePositiveMaximum == pcm.autoScalePercentagePositiveMaximum) @@ -163,6 +188,7 @@ PaletteColorMapping::operator==(const PaletteColorMapping& pcm) const && (this->displayPositiveDataFlag == pcm.displayPositiveDataFlag) && (this->displayZeroDataFlag == pcm.displayZeroDataFlag) && (this->interpolatePaletteFlag == pcm.interpolatePaletteFlag) + && (this->invertedMode == pcm.invertedMode) && (this->scaleMode == pcm.scaleMode) && (this->selectedPaletteName == pcm.selectedPaletteName) && (this->userScaleNegativeMaximum == pcm.userScaleNegativeMaximum) @@ -181,15 +207,32 @@ PaletteColorMapping::operator==(const PaletteColorMapping& pcm) const && (this->thresholdShowFailureInGreen == pcm.thresholdShowFailureInGreen) && (this->thresholdRangeMode == pcm.thresholdRangeMode) && (this->thresholdNegMinPosMaxLinked == pcm.thresholdNegMinPosMaxLinked) - && (this->numericFormatMode == pcm.numericFormatMode) - && (this->precisionDigits == pcm.precisionDigits) - && (this->numericSubdivisionCount == pcm.numericSubdivisionCount) + && (this->colorBarNumericFormatMode == pcm.colorBarNumericFormatMode) + && (this->colorBarPrecisionDigits == pcm.colorBarPrecisionDigits) + && (this->colorBarNumericSubdivisionCount == pcm.colorBarNumericSubdivisionCount) && (this->colorBarValuesMode == pcm.colorBarValuesMode) - && (this->showTickMarksSelected == pcm.showTickMarksSelected)) { - return true; + && (this->colorBarShowTickMarksSelected == pcm.colorBarShowTickMarksSelected)) { + + allMatchFlag = true; + + const bool includeHistogramAttributesFlag = false; + if (includeHistogramAttributesFlag) { + if ((this->histogramRangeMode == pcm.histogramRangeMode) + && (this->histogramBarsVisible == pcm.histogramBarsVisible) + && (this->histogramEnvelopeVisible == pcm.histogramEnvelopeVisible) + && (this->histogramBarsColor == pcm.histogramBarsColor) + && (this->histogramEnvelopeColor == pcm.histogramEnvelopeColor) + && (this->histogramEnvelopeLineWidthPercentage == pcm.histogramEnvelopeLineWidthPercentage) + && (this->histogramNumberOfBuckets == pcm.histogramNumberOfBuckets)) { + allMatchFlag = true; + } + else { + allMatchFlag = false; + } + } } - return false; + return allMatchFlag; } @@ -209,6 +252,7 @@ PaletteColorMapping::initializeMembersPaletteColorMapping() this->userScalePositiveMaximum = 100.0f; this->selectedPaletteName = Palette::ROY_BIG_BL_PALETTE_NAME; this->interpolatePaletteFlag = true; + this->invertedMode = PaletteInvertModeEnum::OFF; this->displayPositiveDataFlag = true; this->displayZeroDataFlag = false; this->displayNegativeDataFlag = true; @@ -222,14 +266,21 @@ PaletteColorMapping::initializeMembersPaletteColorMapping() this->thresholdMappedAverageAreaMaximum = 1.0f; this->thresholdDataName = ""; this->thresholdShowFailureInGreen = false; - this->thresholdRangeMode = PaletteThresholdRangeModeEnum::PALETTE_THRESHOLD_RANGE_MODE_MAP; + this->thresholdRangeMode = PaletteThresholdRangeModeEnum::PALETTE_THRESHOLD_RANGE_MODE_FILE; this->thresholdNegMinPosMaxLinked = false; - this->numericFormatMode = NumericFormatModeEnum::AUTO; - this->precisionDigits = 2; - this->numericSubdivisionCount = 0; - this->modifiedFlag = false; + this->histogramRangeMode = PaletteHistogramRangeModeEnum::PALETTE_HISTOGRAM_RANGE_ALL; + this->histogramBarsVisible = true; + this->histogramEnvelopeVisible = false; + this->histogramBarsColor = CaretColorEnum::CUSTOM; // CUSTOM is color with palette + this->histogramEnvelopeColor = CaretColorEnum::RED; + this->histogramEnvelopeLineWidthPercentage = 0.5f; + this->histogramNumberOfBuckets = 100; + this->colorBarNumericFormatMode = NumericFormatModeEnum::AUTO; + this->colorBarPrecisionDigits = 2; + this->colorBarNumericSubdivisionCount = 0; this->colorBarValuesMode = PaletteColorBarValuesModeEnum::DATA; - this->showTickMarksSelected = false; + this->colorBarShowTickMarksSelected = false; + this->modifiedStatus = PaletteModifiedStatusEnum::UNMODIFIED; } /** @@ -285,6 +336,8 @@ PaletteColorMapping::writeAsXML(XmlWriter& xmlWriter) xmlWriter.writeElementCharacters( PaletteColorMappingXmlElements::XML_TAG_INTERPOLATE, this->interpolatePaletteFlag); + xmlWriter.writeElementCharacters(PaletteColorMappingXmlElements::XML_TAG_INVERT, + PaletteInvertModeEnum::toName(this->invertedMode)); xmlWriter.writeElementCharacters( PaletteColorMappingXmlElements::XML_TAG_DISPLAY_POSITIVE, this->displayPositiveDataFlag); @@ -340,19 +393,32 @@ PaletteColorMapping::writeAsXML(XmlWriter& xmlWriter) xmlWriter.writeElementCharacters(PaletteColorMappingXmlElements::XML_TAG_THRESHOLD_NEG_MIN_POS_MAX_LINKED, this->thresholdNegMinPosMaxLinked); - + xmlWriter.writeElementCharacters(PaletteColorMappingXmlElements::XML_TAG_HISTOGRAM_RANGE_MODE, + PaletteHistogramRangeModeEnum::toName(this->histogramRangeMode)); + xmlWriter.writeElementCharacters(PaletteColorMappingXmlElements::XML_TAG_HISTOGRAM_BARS_VISIBLE, + this->histogramBarsVisible); + xmlWriter.writeElementCharacters(PaletteColorMappingXmlElements::XML_TAG_HISTOGRAM_ENVELOPE_VISIBLE, + this->histogramEnvelopeVisible); + xmlWriter.writeElementCharacters(PaletteColorMappingXmlElements::XML_TAG_HISTOGRAM_BARS_COLOR, + CaretColorEnum::toName(this->histogramBarsColor)); + xmlWriter.writeElementCharacters(PaletteColorMappingXmlElements::XML_TAG_HISTOGRAM_ENVELOPE_COLOR, + CaretColorEnum::toName(this->histogramEnvelopeColor)); + xmlWriter.writeElementCharacters(PaletteColorMappingXmlElements::XML_TAG_HISTOGRAM_ENVELOPE_LINE_WIDTH_PERCENTAGE, + this->histogramEnvelopeLineWidthPercentage); + xmlWriter.writeElementCharacters(PaletteColorMappingXmlElements::XML_TAG_HISTOGRAM_NUMBER_OF_BUCKETS, + this->histogramNumberOfBuckets); xmlWriter.writeElementCharacters(PaletteColorMappingXmlElements::XML_TAG_NUMERIC_FORMAT_MODE, - NumericFormatModeEnum::toName(this->numericFormatMode)); + NumericFormatModeEnum::toName(this->colorBarNumericFormatMode)); xmlWriter.writeElementCharacters(PaletteColorMappingXmlElements::XML_TAG_PRECISION_DIGITS, - this->precisionDigits); + this->colorBarPrecisionDigits); xmlWriter.writeElementCharacters(PaletteColorMappingXmlElements::XML_TAG_NUMERIC_SUBDIVISIONS, - this->numericSubdivisionCount); + this->colorBarNumericSubdivisionCount); xmlWriter.writeElementCharacters(PaletteColorMappingXmlElements::XML_TAG_COLOR_BAR_VALUES_MODE, PaletteColorBarValuesModeEnum::toName(this->colorBarValuesMode)); xmlWriter.writeElementCharacters(PaletteColorMappingXmlElements::XML_TAG_SHOW_TICK_MARKS, - this->showTickMarksSelected); + this->colorBarShowTickMarksSelected); xmlWriter.writeEndElement(); } @@ -386,7 +452,7 @@ PaletteColorMapping::decodeFromStringXML(const AString& xml) { PaletteColorMappingSaxReader saxReader(this); - XmlSaxParser* parser = XmlSaxParser::createXmlParser(); + CaretPointer parser(XmlSaxParser::createXmlParser()); try { parser->parseString(xml, &saxReader); } @@ -407,7 +473,6 @@ PaletteColorMapping::decodeFromStringXML(const AString& xml) << e.whatString().toStdString(); throw XmlException(AString::fromStdString(str.str())); } - delete parser; } /** @@ -427,31 +492,19 @@ PaletteColorMapping::setupAnnotationColorBar(const FastStatistics* statistics, colorBar->clearSections(); - const AString paletteName = getSelectedPaletteName(); - EventPaletteGetByName paletteEvent(paletteName); - EventManager::get()->sendEvent(paletteEvent.getPointer()); - const Palette* palette = paletteEvent.getPalette(); - if (palette == NULL) { - CaretLogSevere("Unable to find palette named \"" - + paletteName - + "\""); - return; - } - + const Palette* palette = getPalette(); + CaretAssert(palette); /* * Types of values for display */ - const bool isPositiveOnly = (this->displayPositiveDataFlag && ( ! this->displayNegativeDataFlag)); - const bool isNegativeOnly = (( ! this->displayPositiveDataFlag) && this->displayNegativeDataFlag); - - float xMinimum = -1.0; - float xMaximum = 1.0; - if (isPositiveOnly) { - xMinimum = 0.0; + float xMinimum = 0.0; + float xMaximum = 0.0; + if (this->displayPositiveDataFlag) { + xMaximum = 1.0; } - else if (isNegativeOnly) { - xMaximum = 0.0; + if (this->displayNegativeDataFlag) { + xMinimum = -1.0; } /* @@ -462,119 +515,228 @@ PaletteColorMapping::setupAnnotationColorBar(const FastStatistics* statistics, interpolateColor = true; } - /* - * Draw the colorbar starting with the color assigned - * to the negative end of the palette. - * Colorbar scalars range from -1 to 1. - */ - const int iStart = palette->getNumberOfScalarsAndColors() - 1; - const int iEnd = 1; - const int iStep = -1; - for (int i = iStart; i >= iEnd; i += iStep) { - /* - * palette data for 'left' side of a color in the palette. - */ - const PaletteScalarAndColor* sc = palette->getScalarAndColor(i); - float scalar = sc->getScalar(); - float rgba[4]; - sc->getColor(rgba); - - /* - * palette data for 'right' side of a color in the palette. - */ - const PaletteScalarAndColor* nextSC = palette->getScalarAndColor(i - 1); - float nextScalar = nextSC->getScalar(); - float nextRGBA[4]; - nextSC->getColor(nextRGBA); - const bool isNoneColorFlag = nextSC->isNoneColor(); - - /* - * Exclude negative regions if not displayed. - */ - if ( ! this->displayNegativeDataFlag) { - if (nextScalar < 0.0) { - continue; - } - else if (scalar < 0.0) { - scalar = 0.0; - } - } - + float zeroRGBA[4] = { 0.0, 0.0, 0.0, 0.0 }; + + if (DeveloperFlagsEnum::isFlag(DeveloperFlagsEnum::DEVELOPER_FLAG_FLIP_PALETTE_NOT_DATA)) + { /* - * Exclude positive regions if not displayed. - */ - if ( ! this->displayPositiveDataFlag) { - if (scalar > 0.0) { + * Palettes start with most positive scalar value that in on right side of palette + */ + const int32_t numberOfColors = palette->getNumberOfScalarsAndColors(); + const bool interpolationOnFlag = interpolateColor; + const int32_t lastColorIndex = numberOfColors - 1; + for (int32_t index = 0; index < numberOfColors; index++) { + const PaletteScalarAndColor* rightSideScalarColor = palette->getScalarAndColor(index); + if (rightSideScalarColor->isNoneColor()) { + /* + * No drawing for "none" color; skip to next color + */ continue; } - else if (nextScalar > 0.0) { - nextScalar = 0.0; - } - } - - /* - * Normally, the first entry has a scalar value of -1. - * If it does not, use the first color draw from - * -1 to the first scalar value. - */ - if (i == iStart) { - if ( ! sc->isNoneColor()) { - if (scalar > -1.0) { - const float xStart = -1.0; - const float xEnd = scalar; - - colorBar->addSection(xStart, - xEnd, - rgba, - rgba); - } + + /* + * Add vertices for right side of quad + */ + float rightSideScalar = rightSideScalarColor->getScalar(); + if (index == 0) { + rightSideScalar = xMaximum; // First color extends to right side } - } - - /* - * If the 'next' color is none, drawing - * is skipped to let the background show - * throw the 'none' region of the palette. - */ - if ( ! isNoneColorFlag) { + float rightSideRGBA[4]; + rightSideScalarColor->getColor(rightSideRGBA); + /* - * left and right region of an entry in the palette - */ - const float xStart = scalar; - const float xEnd = nextScalar; + * Default left side of palette subsection to far left side + * and same color as right side of palette subsection + */ + float leftSideScalar = xMinimum; + float leftSideRGBA[4] = { rightSideRGBA[0], rightSideRGBA[1], rightSideRGBA[2], rightSideRGBA[3] }; /* - * Unless interpolating, use the 'next' color. - */ - float* startRGBA = nextRGBA; - float* endRGBA = nextRGBA; - if (interpolateColor) { - startRGBA = rgba; + * Determine the next scalar and color index for left side of quad + * Left side color is same color as right if: + * # At last color in palette + * # Next color is "none" + * # Interpolation is OFF + */ + int32_t nextIndex = index + 1; + if (nextIndex <= lastColorIndex) { + /* + * Set scalar value for left side of subsection of palette + */ + const PaletteScalarAndColor* leftSideScalarColor = palette->getScalarAndColor(nextIndex); + leftSideScalar = leftSideScalarColor->getScalar(); + + if (palette->getScalarAndColor(nextIndex)->isNoneColor()) { + /* + * No interpolation if left side of subsection is "none" color + */ + } + else if (interpolationOnFlag) { + /* + * Set color for left side of subsection of palette + */ + leftSideScalarColor->getColor(leftSideRGBA); + } } - /* - * Draw the region in the palette. - */ - colorBar->addSection(xStart, - xEnd, - startRGBA, - endRGBA); + if ((rightSideScalar >= 0.0) + && (leftSideScalar <= 0.0)) { + /* + * Used for special case when zero is one but both + * negative and postive are off + */ + const float diff = rightSideScalar - leftSideScalar; + if (diff > 0.0) { + const float rightWeight = rightSideScalar; + const float leftWeight = -leftSideScalar; + for (int32_t m = 0; m < 4; m++) { + zeroRGBA[m] = ((leftSideRGBA[m] * leftWeight) + + (rightSideRGBA[m] * rightWeight)); + } + } + else { + for (int32_t m = 0; m < 4; m++) { + zeroRGBA[m] = rightSideRGBA[m]; + } + } + } /* - * The last scalar value is normally 1.0. If the last - * scalar is less than 1.0, then fill in the rest of - * the palette from the last scalar to 1.0. - */ - if (i == iEnd) { - if (nextScalar < 1.0) { - const float xStart = nextScalar; - const float xEnd = 1.0; - colorBar->addSection(xStart, - xEnd, - nextRGBA, - nextRGBA); + * Add a section to the color bar + */ + leftSideScalar = MathFunctions::limitRange(leftSideScalar, xMinimum, xMaximum); + rightSideScalar = MathFunctions::limitRange(rightSideScalar, xMinimum, xMaximum); + if (rightSideScalar > leftSideScalar) { + colorBar->addSection(leftSideScalar, rightSideScalar, leftSideRGBA, rightSideRGBA); + } + } + } else {//flip the control point values, like the palette inversion code - would be a lot simpler to just draw a lot of boxes and approximate it, but now that this is working... + bool invert_pos_neg = false;//draw the sections from the palette control points, invert their positions as needed + bool invert_min_max = false;//use same size of zero range for display as is specified for the zero range hack in the palette definitions + int inv_min_max_zero_maps_to = 0; + bool displayPosColors = this->displayPositiveDataFlag, displayNegColors = this->displayNegativeDataFlag; + //convert the inversion settings into the orthogonal inversions that could be applied simultaneously, if desired + switch (this->invertedMode) + { + case PaletteInvertModeEnum::OFF: + break; + case PaletteInvertModeEnum::POSITIVE_NEGATIVE_SEPARATE: + invert_min_max = true; + break; + case PaletteInvertModeEnum::POSITIVE_WITH_NEGATIVE: + invert_pos_neg = true; + displayPosColors = this->displayNegativeDataFlag; + displayNegColors = this->displayPositiveDataFlag; + break; + } + if (invert_min_max && (inv_min_max_zero_maps_to > 1 || inv_min_max_zero_maps_to < -1)) + { + CaretLogWarning("invalid 'inv_min_max_zero_maps_to' setting '" + AString::number(inv_min_max_zero_maps_to) + "' for min/max palette inversion, resetting to 0"); + inv_min_max_zero_maps_to = 0; + } + if (invert_min_max) + { + if (invert_pos_neg)//when both apply, the positive data range uses negative colors, so it should map to the most negative color instead + { + palette->getPaletteColor(-inv_min_max_zero_maps_to, interpolateColor, zeroRGBA); + } else { + palette->getPaletteColor(inv_min_max_zero_maps_to, interpolateColor, zeroRGBA); + } + } else { + palette->getPaletteColor(0.0f, interpolateColor, zeroRGBA);//palette should give the right answer for 0 color + } + const int32_t numberOfColors = palette->getNumberOfScalarsAndColors(); + //take two, now that I know where the skeletons are: single loop, always insert virtual zero point, because it is also needed for turning off positive or negative range cleanly + for (int i = -1; i < numberOfColors; ++i)//also insert virtual points at 1 and -1 if they don't exist + { + PaletteScalarAndColor* highSideScalarColor = NULL, *lowSideScalarColor = NULL; + if (i > 0) highSideScalarColor = palette->getScalarAndColor(i); + if (i < numberOfColors - 1) lowSideScalarColor = palette->getScalarAndColor(i + 1); + float highScalar = 1.0f, lowScalar = -1.0f; + if (highSideScalarColor != NULL) highScalar = highSideScalarColor->getScalar(); + if (lowSideScalarColor != NULL) lowScalar = lowSideScalarColor->getScalar(); + if (lowScalar == highScalar) continue;//all special cases of extra virtual pieces of the noninverted palette are handled at this point, we just need to color them, split at zero, and reposition for inversion + bool colorIsNone = (highSideScalarColor != NULL && highSideScalarColor->isNoneColor()); + bool interpolateSection = (interpolateColor && (lowSideScalarColor == NULL || !lowSideScalarColor->isNoneColor())); + if (displayPosColors && highScalar > PALETTE_ZERO_COLOR_ZONE) + { + float highPosition = highScalar, lowPosition = lowScalar; + if (lowPosition < -PALETTE_ZERO_COLOR_ZONE) lowPosition = 0.0f;//don't draw across zero + float highRGBA[4], lowRGBA[4]; + if (colorIsNone) + { + colorBar->getBackgroundColorRGBA(highRGBA);//drawing a section in background color makes sure it doesn't get shifted around + colorBar->getBackgroundColorRGBA(lowRGBA); + } else { + palette->getPaletteColor(highScalar, interpolateColor, highRGBA);//palette should always give the right answer as long as point isn't none, allows easier treatment of splitting for 0 and added end bar + if (interpolateSection)//when exactly on the control point, it should consider it in the range between that and the lower point, and give the equivalent of the PSAC + { + palette->getPaletteColor(lowPosition, interpolateColor, lowRGBA);//NOTE: this uses Position because it could be a virtual inserted zero point + } else { + palette->getPaletteColor(highScalar, interpolateColor, lowRGBA); + } + } + if (invert_min_max)//doing this manually here means that the normalization code can't introduce a discontinuity + { + highPosition = 1.0f - highPosition + PALETTE_ZERO_COLOR_ZONE;//for consistency, keep them in the same range + lowPosition = 1.0f - lowPosition + PALETTE_ZERO_COLOR_ZONE;//could instead stretch them to 0 to 1 when converting from scalar to position + if (lowPosition > 1.0f) lowPosition = 1.0f; + } + if (invert_pos_neg) + { + highPosition = -highPosition; + lowPosition = -lowPosition; + } + if (highPosition < lowPosition) + {//color bar section drawing has normals test enabled, for whatever reason, so make sure these are the right way round + colorBar->addSection(highPosition, lowPosition, highRGBA, lowRGBA);//trust that palettes are sanity checked to not have control points outside [-1, 1] + } else { + colorBar->addSection(lowPosition, highPosition, lowRGBA, highRGBA);//trust that palettes are sanity checked to not have control points outside [-1, 1] + } + } + if (displayNegColors && lowScalar < -PALETTE_ZERO_COLOR_ZONE) + { + float highPosition = highScalar, lowPosition = lowScalar; + if (highPosition > PALETTE_ZERO_COLOR_ZONE) highPosition = 0.0f;//don't draw across zero + float highRGBA[4], lowRGBA[4]; + if (colorIsNone) + { + colorBar->getBackgroundColorRGBA(highRGBA);//drawing a section in background color makes sure it doesn't get shifted around + colorBar->getBackgroundColorRGBA(lowRGBA); + } else { + if (interpolateSection) + { + palette->getPaletteColor(highPosition, interpolateColor, highRGBA);//NOTE: this uses Position because it could be a virtual inserted zero point + palette->getPaletteColor(lowScalar, interpolateColor, lowRGBA); + } else { + palette->getPaletteColor(highScalar, interpolateColor, highRGBA); + palette->getPaletteColor(highScalar, interpolateColor, lowRGBA); + } + } + if (invert_min_max)//doing this manually here means that the normalization code can't introduce a discontinuity + { + highPosition = -1.0f - highPosition - PALETTE_ZERO_COLOR_ZONE; + lowPosition = -1.0f - lowPosition - PALETTE_ZERO_COLOR_ZONE; + if (highPosition < -1.0f) highPosition = -1.0f; + } + if (invert_pos_neg) + { + highPosition = -highPosition; + lowPosition = -lowPosition; + } + if (highPosition < lowPosition) + {//color bar section drawing has normals test enabled, for whatever reason, so make sure these are the right way round + colorBar->addSection(highPosition, lowPosition, highRGBA, lowRGBA);//trust that palettes are sanity checked to not have control points outside [-1, 1] + } else { + colorBar->addSection(lowPosition, highPosition, lowRGBA, highRGBA);//trust that palettes are sanity checked to not have control points outside [-1, 1] } } + }//so much cleaner, and handles edge cases the other didn't + //zero: use the color that exact zeros should get colored as by asking the palette, after applying zero mapping setting + //draw it last so any discontinuity is visible + if (this->displayZeroDataFlag) + { + colorBar->addSection(0.0f, 0.0f, zeroRGBA, zeroRGBA);//NOTE: zero-width ranges are treated specially, drawn as lines, which will be thicker than the palette special zero range } } @@ -584,17 +746,42 @@ PaletteColorMapping::setupAnnotationColorBar(const FastStatistics* statistics, /* * Draw over thresholded regions with background color */ - if (this->thresholdType != PaletteThresholdTypeEnum::THRESHOLD_TYPE_OFF) { + bool thresholdSelfFlag = false; + switch (thresholdType) { + case PaletteThresholdTypeEnum::THRESHOLD_TYPE_OFF: + break; + case PaletteThresholdTypeEnum::THRESHOLD_TYPE_NORMAL: + thresholdSelfFlag = true; + break; + case PaletteThresholdTypeEnum::THRESHOLD_TYPE_FILE: + break; + case PaletteThresholdTypeEnum::THRESHOLD_TYPE_MAPPED: + break; + case PaletteThresholdTypeEnum::THRESHOLD_TYPE_MAPPED_AVERAGE_AREA: + break; + } + + if (thresholdSelfFlag) { const float minMaxThresholds[2] = { getThresholdMinimum(thresholdType), getThresholdMaximum(thresholdType) }; float normalizedThresholds[2]; - mapDataToPaletteNormalizedValues(statistics, - minMaxThresholds, - normalizedThresholds, - 2); + if (DeveloperFlagsEnum::isFlag(DeveloperFlagsEnum::DEVELOPER_FLAG_FLIP_PALETTE_NOT_DATA)) + { + mapDataToPaletteNormalizedValues(statistics, + minMaxThresholds, + normalizedThresholds, + 2); + } else {//this assumes the same zero size in palette display as is reserved in the palette definitions + PaletteColorMapping normalize_noinv = *this; + normalize_noinv.setInvertedMode(PaletteInvertModeEnum::OFF); + normalize_noinv.mapDataToPaletteNormalizedValues(statistics, + minMaxThresholds, + normalizedThresholds, + 2); + } switch (this->thresholdTest) { case PaletteThresholdTestEnum::THRESHOLD_TEST_SHOW_INSIDE: @@ -620,8 +807,8 @@ PaletteColorMapping::setupAnnotationColorBar(const FastStatistics* statistics, xMinimum, xMaximum); if (xMin < xMax) { - colorBar->addSection(normalizedThresholds[0], - normalizedThresholds[1], + colorBar->addSection(xMin, + xMax, backgroundRGBA, backgroundRGBA); } @@ -635,9 +822,22 @@ PaletteColorMapping::setupAnnotationColorBar(const FastStatistics* statistics, * background color at zero in the palette. */ if ( ! this->displayZeroDataFlag) { - colorBar->addSection(0.0, 0.0, backgroundRGBA, backgroundRGBA); + colorBar->addSection(0.0, 0.0, backgroundRGBA, backgroundRGBA);//NOTE: zero-width ranges are treated specially, drawn as lines } - + + /* + * If only zero is displayed + */ + if (this->displayZeroDataFlag + && ( ! this->displayPositiveDataFlag) + && ( ! this->displayNegativeDataFlag)) { + if (zeroRGBA[3] > 0.0) { + colorBar->addSection(-1.0, 0.0, backgroundRGBA, backgroundRGBA); + colorBar->addSection( 0.0, 1.0, backgroundRGBA, backgroundRGBA);//TSC: fix the reversed drawing of the positive section, and place zero last so that it shows + colorBar->addSection( 0.0, 0.0, zeroRGBA, zeroRGBA); + } + } + /** * Add numeric text to the color bar. */ @@ -894,6 +1094,30 @@ PaletteColorMapping::setInterpolatePaletteFlag(const bool interpolatePaletteFlag } } +/** + * @return The inverted mode. + */ +PaletteInvertModeEnum::Enum +PaletteColorMapping::getInvertedMode() const +{ + return this->invertedMode; +} + +/** + * Set the inverted mode + * + * @param invertedMode + * New inverted mode. + */ +void +PaletteColorMapping::setInvertedMode(const PaletteInvertModeEnum::Enum invertedMode) +{ + if (this->invertedMode != invertedMode) { + this->invertedMode = invertedMode; + setModified(); + } +} + /** * Get how the data is scaled to the palette. * @return Enumerated type indicating how data is scaled to the palette. @@ -920,6 +1144,60 @@ PaletteColorMapping::setScaleMode(const PaletteScaleModeEnum::Enum scaleMode) } } +/** + * @return The palette used by this palette color mapping. + * This method should always return a valid palette. + * + * If the inverted palette flag is set, this will return + * the inverted version of the palette. + * + * The palette is set by calling setSelectedPaletteName(). + * + * If the palette name does not match a valid palette, + * the default palette (ROY-BIG-BL) is returned. + */ +const Palette* +PaletteColorMapping::getPalette() const +{ + const AString paletteName = getSelectedPaletteName(); + EventPaletteGetByName paletteEvent(paletteName); + EventManager::get()->sendEvent(paletteEvent.getPointer()); + Palette* palette = paletteEvent.getPalette(); + + if (palette == NULL) { + if (s_missingPaletteNames.find(paletteName) == s_missingPaletteNames.end()) { + s_missingPaletteNames.insert(paletteName); + CaretLogSevere("Palette named " + + paletteName + + " is not a valid palette name."); + } + + /* + * Use the "default" palette + */ + EventPaletteGetByName paletteEvent(Palette::getDefaultPaletteName()); + EventManager::get()->sendEvent(paletteEvent.getPointer()); + palette = paletteEvent.getPalette(); + CaretAssert(palette); + } + + if (DeveloperFlagsEnum::isFlag(DeveloperFlagsEnum::DEVELOPER_FLAG_FLIP_PALETTE_NOT_DATA)) { + switch (this->invertedMode) { + case PaletteInvertModeEnum::OFF: + break; + case PaletteInvertModeEnum::POSITIVE_WITH_NEGATIVE: + return palette->getInvertedPalette(); + break; + case PaletteInvertModeEnum::POSITIVE_NEGATIVE_SEPARATE: + return palette->getNoneSeparateInvertedPalette(); + //return palette->getSignSeparateInvertedPalette(); + break; + } + } + + return palette; +} + /** * Get the name of the selected palette. * @return Name of the selected palette. @@ -1104,6 +1382,9 @@ PaletteColorMapping::getThresholdMinimum(const PaletteThresholdTypeEnum::Enum th case PaletteThresholdTypeEnum::THRESHOLD_TYPE_NORMAL: value = this->thresholdNormalMinimum; break; + case PaletteThresholdTypeEnum::THRESHOLD_TYPE_FILE: + value = this->thresholdNormalMinimum; + break; case PaletteThresholdTypeEnum::THRESHOLD_TYPE_MAPPED: value = this->thresholdMappedMinimum; break; @@ -1134,6 +1415,9 @@ PaletteColorMapping::getThresholdMaximum(const PaletteThresholdTypeEnum::Enum th case PaletteThresholdTypeEnum::THRESHOLD_TYPE_NORMAL: value = this->thresholdNormalMaximum; break; + case PaletteThresholdTypeEnum::THRESHOLD_TYPE_FILE: + value = this->thresholdNormalMaximum; + break; case PaletteThresholdTypeEnum::THRESHOLD_TYPE_MAPPED: value = this->thresholdMappedMaximum; break; @@ -1164,6 +1448,9 @@ PaletteColorMapping::setThresholdMinimum(const PaletteThresholdTypeEnum::Enum th case PaletteThresholdTypeEnum::THRESHOLD_TYPE_NORMAL: setThresholdNormalMinimum(thresholdMinimum); break; + case PaletteThresholdTypeEnum::THRESHOLD_TYPE_FILE: + setThresholdNormalMinimum(thresholdMinimum); + break; case PaletteThresholdTypeEnum::THRESHOLD_TYPE_MAPPED: setThresholdMappedMinimum(thresholdMinimum); break; @@ -1191,6 +1478,9 @@ PaletteColorMapping::setThresholdMaximum(const PaletteThresholdTypeEnum::Enum th case PaletteThresholdTypeEnum::THRESHOLD_TYPE_NORMAL: setThresholdNormalMaximum(thresholdMaximum); break; + case PaletteThresholdTypeEnum::THRESHOLD_TYPE_FILE: + setThresholdNormalMaximum(thresholdMaximum); + break; case PaletteThresholdTypeEnum::THRESHOLD_TYPE_MAPPED: setThresholdMappedMaximum(thresholdMaximum); break; @@ -1495,7 +1785,7 @@ PaletteColorMapping::setShowThresholdFailureInGreen(const bool showInGreenFlag) void PaletteColorMapping::setModified() { - this->modifiedFlag = true; + this->modifiedStatus = PaletteModifiedStatusEnum::MODIFIED; } /** @@ -1506,7 +1796,7 @@ PaletteColorMapping::setModified() void PaletteColorMapping::clearModified() { - this->modifiedFlag = false; + this->modifiedStatus = PaletteModifiedStatusEnum::UNMODIFIED; } /** @@ -1518,11 +1808,31 @@ PaletteColorMapping::clearModified() bool PaletteColorMapping::isModified() const { - return this->modifiedFlag; + return (this->modifiedStatus == PaletteModifiedStatusEnum::MODIFIED); +} + +/** + * @return Modified status enumerated type that indicates specific type of modification + */ +PaletteModifiedStatusEnum::Enum +PaletteColorMapping::getModifiedStatus() const +{ + return this->modifiedStatus; +} + +/** + * When restored from a scene, need to set the + * 'scene modified' status to both avoid 'file modified' warnings + * but need to save palette to any new scenes + */ +void +PaletteColorMapping::setSceneModified() +{ + this->modifiedStatus = PaletteModifiedStatusEnum::MODIFIED_BY_SHOW_SCENE; } /** - * Map data values to palette normalized values using the + * Map data values to palette normalized values using the * settings in this palette color mapping. * * @param statistics @@ -1542,9 +1852,33 @@ PaletteColorMapping::mapDataToPaletteNormalizedValues(const FastStatistics* stat float* normalizedValuesOut, const int64_t numberOfData) const { + bool enable_normalization_flipping = ( ! DeveloperFlagsEnum::isFlag(DeveloperFlagsEnum::DEVELOPER_FLAG_FLIP_PALETTE_NOT_DATA)); //temporary debug/developer variable: use this to trigger the flipping code in this function if (numberOfData <= 0) { return; } + bool invert_pos_neg = false; + bool invert_min_max = false; + int inv_min_max_zero_maps_to = 0; + if (enable_normalization_flipping) + { + //convert the inversion settings into the orthogonal inversions that could be applied simultaneously, if desired + switch (this->invertedMode) + { + case PaletteInvertModeEnum::OFF: + break; + case PaletteInvertModeEnum::POSITIVE_NEGATIVE_SEPARATE: + invert_min_max = true; + break; + case PaletteInvertModeEnum::POSITIVE_WITH_NEGATIVE: + invert_pos_neg = true; + break; + } + if (invert_min_max && (inv_min_max_zero_maps_to > 1 || inv_min_max_zero_maps_to < -1)) + { + CaretLogWarning("invalid 'inv_min_max_zero_maps_to' setting '" + AString::number(inv_min_max_zero_maps_to) + "' for min/max palette inversion, resetting to 0"); + inv_min_max_zero_maps_to = 0; + } + } /* * Minimum and maximum values used when mapping scalar into color palette. @@ -1586,16 +1920,24 @@ PaletteColorMapping::mapDataToPaletteNormalizedValues(const FastStatistics* stat mappingMostPositive = this->getUserScalePositiveMaximum(); break; } - //TSC: the excluded zone of normalization is a SEPARATE issue to zero detection in the data - //specifically, it is a HACK, in order for palettes to be able to specify a special color for data that is 0, which is not involved in color interpolation - const float PALETTE_ZERO_COLOR_ZONE = 0.00001f; + //TSC: hack to do the min/max inversion without extra conditionals: swap most and least + if (enable_normalization_flipping && invert_min_max) + { + float temp = mappingMostPositive; + mappingMostPositive = mappingLeastPositive; + mappingLeastPositive = temp; + + temp = mappingMostNegative; + mappingMostNegative = mappingLeastNegative; + mappingLeastNegative = temp; + } bool settingsValidPos = true, settingsValidNeg = true; float mappingPositiveDenominator = (mappingMostPositive - mappingLeastPositive) / (1.0f - PALETTE_ZERO_COLOR_ZONE);//this correction prevents normalization from assigning most positive a normalized value greater than 1 - if (mappingPositiveDenominator == 0.0) {//if we don't want backwards pos/neg settings to invert bright/dark, then change both these tests to be >= 0.0f + if (mappingPositiveDenominator == 0.0f) {//needs to be "== 0.0f", so that we can swap most/least to do min/max inversion settingsValidPos = false; } float mappingNegativeDenominator = (mappingMostNegative - mappingLeastNegative) / (-1.0f + PALETTE_ZERO_COLOR_ZONE);//ditto, but most negative maps to -1 - if (mappingNegativeDenominator == 0.0) { + if (mappingNegativeDenominator == 0.0f) { settingsValidNeg = false; } @@ -1606,35 +1948,47 @@ PaletteColorMapping::mapDataToPaletteNormalizedValues(const FastStatistics* stat * Color scalar using palette */ float normalized = 0.0f; - if (scalar > 0.0) { + if (scalar > 0.0f) { if (settingsValidPos) { normalized = (scalar - mappingLeastPositive) / mappingPositiveDenominator + PALETTE_ZERO_COLOR_ZONE; - if (normalized > 1.0f) { + } else { + if (scalar > mappingLeastPositive) + { normalized = 1.0f; } - else if (normalized < PALETTE_ZERO_COLOR_ZONE) { - normalized = PALETTE_ZERO_COLOR_ZONE; - } - } else { + } + if (normalized > 1.0f)//don't allow zero from nonzero, even when coloring range is 0 + { normalized = 1.0f; + } else if (normalized < PALETTE_ZERO_COLOR_ZONE) { + normalized = PALETTE_ZERO_COLOR_ZONE; } - } - else if (scalar < 0.0) { + } else if (scalar < 0.0f) { if (settingsValidNeg) { normalized = (scalar - mappingLeastNegative) / mappingNegativeDenominator - PALETTE_ZERO_COLOR_ZONE; - if (normalized < -1.0f) { + } else { + if (scalar < mappingLeastNegative) + { normalized = -1.0f; } - else if (normalized > -PALETTE_ZERO_COLOR_ZONE) { - normalized = -PALETTE_ZERO_COLOR_ZONE; - } - } else { + } + if (normalized < -1.0f) + { normalized = -1.0f; + } else if (normalized > -PALETTE_ZERO_COLOR_ZONE) { + normalized = -PALETTE_ZERO_COLOR_ZONE; } + } else if (enable_normalization_flipping && scalar == 0.0f && invert_min_max) {//don't apply the replacement to NaN, only to 0 + normalized = inv_min_max_zero_maps_to; + } + if (enable_normalization_flipping && invert_pos_neg) + { + normalizedValuesOut[i] = -normalized; + } else { + normalizedValuesOut[i] = normalized; } - normalizedValuesOut[i] = normalized; } } @@ -1656,9 +2010,6 @@ PaletteColorMapping::setupAnnotationColorBarNumericText(const FastStatistics* st getPaletteColorBarScaleText(statistics, colorBarNumericText); -// std::vector > normalizedPositionAndText; -// getPaletteColorBarScaleText(statistics, -// normalizedPositionAndText); for (std::vector::iterator iter = colorBarNumericText.begin(); iter != colorBarNumericText.end(); @@ -1671,7 +2022,7 @@ PaletteColorMapping::setupAnnotationColorBarNumericText(const FastStatistics* st delete nt; } - colorBar->setShowTickMarksSelected(this->showTickMarksSelected); + colorBar->setShowTickMarksSelected(this->colorBarShowTickMarksSelected); } /** @@ -1701,9 +2052,9 @@ PaletteColorMapping::getPaletteColorBarScaleText(const FastStatistics* statistic { const int emDash = 0x2014; - const AString positive("(+)"); //"POS"); + const AString positive("(+)"); const AString zero("0"); - const AString negative("(" + QString(QChar(emDash)) + ")"); //"NEG"); + const AString negative("(" + QString(QChar(emDash)) + ")"); if (isDisplayPositiveDataFlag() && isDisplayNegativeDataFlag()) { @@ -1719,9 +2070,6 @@ PaletteColorMapping::getPaletteColorBarScaleText(const FastStatistics* statistic positive, AnnotationTextAlignHorizontalEnum::RIGHT, true)); -// normalizedPositionAndTextOut.push_back(std::make_pair(0.0, negative)); -// normalizedPositionAndTextOut.push_back(std::make_pair(0.5, zero)); -// normalizedPositionAndTextOut.push_back(std::make_pair(1.0, positive)); } else if (isDisplayPositiveDataFlag()) { colorBarNumericTextOut.push_back(new AnnotationColorBarNumericText(0.0, @@ -1732,8 +2080,6 @@ PaletteColorMapping::getPaletteColorBarScaleText(const FastStatistics* statistic positive, AnnotationTextAlignHorizontalEnum::RIGHT, true)); -// normalizedPositionAndTextOut.push_back(std::make_pair(0.0, zero)); -// normalizedPositionAndTextOut.push_back(std::make_pair(1.0, positive)); } else if (isDisplayNegativeDataFlag()) { colorBarNumericTextOut.push_back(new AnnotationColorBarNumericText(0.0, @@ -1744,15 +2090,12 @@ PaletteColorMapping::getPaletteColorBarScaleText(const FastStatistics* statistic zero, AnnotationTextAlignHorizontalEnum::CENTER, true)); -// normalizedPositionAndTextOut.push_back(std::make_pair(0.0, negative)); -// normalizedPositionAndTextOut.push_back(std::make_pair(1.0, zero)); } else if (isDisplayZeroDataFlag()) { colorBarNumericTextOut.push_back(new AnnotationColorBarNumericText(0.5, zero, AnnotationTextAlignHorizontalEnum::CENTER, true)); -// normalizedPositionAndTextOut.push_back(std::make_pair(0.5, zero)); } return; @@ -1804,41 +2147,51 @@ PaletteColorMapping::getPaletteColorBarScaleText(const FastStatistics* statistic break; } + /* + * Types of values for display + */ + const bool positiveDataDisplayedFlag = isDisplayPositiveDataFlag(); + const bool negativeDataDisplayedFlag = isDisplayNegativeDataFlag(); + /* * numeric values displayed for negative data */ std::vector negativeValues; - negativeValues.push_back(negMax); - if (this->numericSubdivisionCount > 0) { - const float range = negMin - negMax; - const float interval = range / (this->numericSubdivisionCount + 1); - for (int32_t i = 0; i < this->numericSubdivisionCount; i++) { - negativeValues.push_back(negMax - + (interval * (i + 1))); + if (negativeDataDisplayedFlag) { + negativeValues.push_back(negMax); + if (this->colorBarNumericSubdivisionCount > 0) { + const float range = negMin - negMax; + const float interval = range / (this->colorBarNumericSubdivisionCount + 1); + for (int32_t i = 0; i < this->colorBarNumericSubdivisionCount; i++) { + negativeValues.push_back(negMax + + (interval * (i + 1))); + } } + negativeValues.push_back(negMin); } - negativeValues.push_back(negMin); /* * numeric values displayed for positive data */ std::vector positiveValues; - positiveValues.push_back(posMin); - if (this->numericSubdivisionCount > 0) { - const float range = posMax - posMin; - const float interval = range / (this->numericSubdivisionCount + 1); - for (int32_t i = 0; i < this->numericSubdivisionCount; i++) { - positiveValues.push_back(posMin - + (interval * (i + 1))); + if (positiveDataDisplayedFlag) { + positiveValues.push_back(posMin); + if (this->colorBarNumericSubdivisionCount > 0) { + const float range = posMax - posMin; + const float interval = range / (this->colorBarNumericSubdivisionCount + 1); + for (int32_t i = 0; i < this->colorBarNumericSubdivisionCount; i++) { + positiveValues.push_back(posMin + + (interval * (i + 1))); + } } + positiveValues.push_back(posMax); } - positiveValues.push_back(posMax); /* * Will need to override these values when percentile */ - NumericFormatModeEnum::Enum numericFormatModeForTextFormatting = this->numericFormatMode; - int32_t precisionDigitsForTextFormatting = this->precisionDigits; + NumericFormatModeEnum::Enum numericFormatModeForTextFormatting = this->colorBarNumericFormatMode; + int32_t precisionDigitsForTextFormatting = this->colorBarPrecisionDigits; /* * Processing for percentile mode @@ -1860,12 +2213,6 @@ PaletteColorMapping::getPaletteColorBarScaleText(const FastStatistics* statistic const float percentile = statistics->getNegativeValuePercentile(*iter); *iter = percentile; } - - /* - * Decimal mode integers for percentile - */ - numericFormatModeForTextFormatting = NumericFormatModeEnum::DECIMAL; - //precisionDigitsForTextFormatting = 0; break; case PaletteColorBarValuesModeEnum::SIGN_ONLY: CaretAssertMessage(0, "Should never get here. Sign only handled above"); @@ -1877,32 +2224,25 @@ PaletteColorMapping::getPaletteColorBarScaleText(const FastStatistics* statistic */ const int32_t numberOfNegValues = static_cast(negativeValues.size()); std::vector negativeValuesText(numberOfNegValues); - NumericTextFormatting::formatValueRange(numericFormatModeForTextFormatting, - precisionDigitsForTextFormatting, - &negativeValues[0], - &negativeValuesText[0], - numberOfNegValues); /* * Create text representations for positive values */ const int32_t numberOfPosValues = static_cast(positiveValues.size()); std::vector positiveValuesText(numberOfPosValues); - NumericTextFormatting::formatValueRange(numericFormatModeForTextFormatting, - precisionDigitsForTextFormatting, - &positiveValues[0], - &positiveValuesText[0], - numberOfPosValues); + + NumericTextFormatting::formatValueRangeNegPos(numericFormatModeForTextFormatting, + precisionDigitsForTextFormatting, + &negativeValues[0], + &negativeValuesText[0], + numberOfNegValues, + &positiveValues[0], + &positiveValuesText[0], + numberOfPosValues); CaretAssert(negativeValues.size() == negativeValuesText.size()); CaretAssert(positiveValues.size() == positiveValuesText.size()); - /* - * Types of values for display - */ - const bool positiveDataDisplayedFlag = isDisplayPositiveDataFlag(); - const bool negativeDataDisplayedFlag = isDisplayNegativeDataFlag(); - /* * Are both negative and positive values displayed? */ @@ -1910,7 +2250,7 @@ PaletteColorMapping::getPaletteColorBarScaleText(const FastStatistics* statistic if (negativeDataDisplayedFlag && positiveDataDisplayedFlag) { CaretAssert(negativeValuesText.size() > 0); - const AString negMinText = negativeValuesText.back(); + AString negMinText = negativeValuesText.back(); CaretAssert(positiveValuesText.size() > 0); const AString posMinText = positiveValuesText.front(); @@ -1920,6 +2260,13 @@ PaletteColorMapping::getPaletteColorBarScaleText(const FastStatistics* statistic * same, there is no need to display both of them. */ zeroValueText = negMinText; + if (isZeroNumericText(zeroValueText)) { + zeroValueText = "0"; + } + } + else if (isZeroNumericText(negMinText) + && (isZeroNumericText(posMinText))) { + zeroValueText = "0"; } else { /* @@ -2001,8 +2348,6 @@ PaletteColorMapping::getPaletteColorBarScaleText(const FastStatistics* statistic negativeValuesText[i], alignment, true)); -// normalizedPositionAndTextOut.push_back(std::make_pair(value, -// negativeValuesText[i])); } } @@ -2015,8 +2360,27 @@ PaletteColorMapping::getPaletteColorBarScaleText(const FastStatistics* statistic zeroValueText, AnnotationTextAlignHorizontalEnum::CENTER, true)); -// normalizedPositionAndTextOut.push_back(std::make_pair(0.5, -// zeroValueText)); + } + + /* + * When zero only + */ + if (isDisplayZeroDataFlag()) { + if (( ! negativeDataDisplayedFlag) + && ( ! positiveDataDisplayedFlag)) { + colorBarNumericTextOut.push_back(new AnnotationColorBarNumericText(0.0, + "", + AnnotationTextAlignHorizontalEnum::CENTER, + true)); + colorBarNumericTextOut.push_back(new AnnotationColorBarNumericText(0.5, + "0.0", + AnnotationTextAlignHorizontalEnum::CENTER, + true)); + colorBarNumericTextOut.push_back(new AnnotationColorBarNumericText(1.0, + "", + AnnotationTextAlignHorizontalEnum::CENTER, + true)); + } } /* @@ -2042,8 +2406,6 @@ PaletteColorMapping::getPaletteColorBarScaleText(const FastStatistics* statistic positiveValuesText[i], alignment, true)); -// normalizedPositionAndTextOut.push_back(std::make_pair(value, -// positiveValuesText[i])); } } @@ -2060,11 +2422,6 @@ PaletteColorMapping::getPaletteColorBarScaleText(const FastStatistics* statistic AnnotationColorBarNumericText* nt = *iter; nt->setNumericText(nt->getNumericText() + "%"); } -// for (std::vector >::iterator iter = normalizedPositionAndTextOut.begin(); -// iter != normalizedPositionAndTextOut.end(); -// iter++) { -// iter->second.append("%"); -// } break; case PaletteColorBarValuesModeEnum::SIGN_ONLY: break; @@ -2091,345 +2448,38 @@ PaletteColorMapping::getPaletteColorBarScaleText(const FastStatistics* statistic } } - /** - * Get the text characters for drawing the scale above the palette - * color bar. + * Is the numeric value of the text zero? * - * @param statistics - * Statistics for the data. - * @param normalizedPositionAndTextOut, - * Horizontal position ranging 0.0 to 1.0 - * and text values. + * Examples: 0, -0.0, 0.00, etc + * + * @param numericText + * The text with a numeric value + * @return + * True if the text represents zero, else false */ -void -PaletteColorMapping::getPaletteColorBarScaleText(const FastStatistics* statistics, - std::vector >& normalizedPositionAndTextOut) const +bool +PaletteColorMapping::isZeroNumericText(const AString& numericText) const { - normalizedPositionAndTextOut.clear(); - /* - * Processing for Sign Only Mode + * While a regular expression could be used, it may + * be faster just to test for valid characters */ - switch (this->colorBarValuesMode) { - case PaletteColorBarValuesModeEnum::DATA: - break; - case PaletteColorBarValuesModeEnum::PERCENTILE: - break; - case PaletteColorBarValuesModeEnum::SIGN_ONLY: - { - const int emDash = 0x2014; - - const AString positive("(+)"); //"POS"); - const AString zero("0"); - const AString negative("(" + QString(QChar(emDash)) + ")"); //"NEG"); - - if (isDisplayPositiveDataFlag() - && isDisplayNegativeDataFlag()) { - normalizedPositionAndTextOut.push_back(std::make_pair(0.0, negative)); - normalizedPositionAndTextOut.push_back(std::make_pair(0.5, zero)); - normalizedPositionAndTextOut.push_back(std::make_pair(1.0, positive)); - } - else if (isDisplayPositiveDataFlag()) { - normalizedPositionAndTextOut.push_back(std::make_pair(0.0, zero)); - normalizedPositionAndTextOut.push_back(std::make_pair(1.0, positive)); - } - else if (isDisplayNegativeDataFlag()) { - normalizedPositionAndTextOut.push_back(std::make_pair(0.0, negative)); - normalizedPositionAndTextOut.push_back(std::make_pair(1.0, zero)); - } - else if (isDisplayZeroDataFlag()) { - normalizedPositionAndTextOut.push_back(std::make_pair(0.5, zero)); - } - - return; + const int32_t numChars = numericText.length(); + for (int32_t i = 0; i < numChars; i++) { + QChar c = numericText[i]; + if ((c == '+') + || (c == '-') + || (c == '0') + || (c == '.')) { + /* ok */ + } + else { + return false; } - break; } - - float negMax = -1.0; - float negMin = 0.0; - float posMin = 0.0; - float posMax = 1.0; - switch (getScaleMode()) { - case PaletteScaleModeEnum::MODE_AUTO_SCALE: - { - float dummy; - statistics->getNonzeroRanges(negMax, dummy, dummy, posMax); - } - break; - case PaletteScaleModeEnum::MODE_AUTO_SCALE_ABSOLUTE_PERCENTAGE: - { - const float maxPct = getAutoScaleAbsolutePercentageMaximum(); - const float minPct = getAutoScaleAbsolutePercentageMinimum(); - - negMax = -statistics->getApproxAbsolutePercentile(maxPct); - negMin = -statistics->getApproxAbsolutePercentile(minPct); - posMin = statistics->getApproxAbsolutePercentile(minPct); - posMax = statistics->getApproxAbsolutePercentile(maxPct); - } - break; - case PaletteScaleModeEnum::MODE_AUTO_SCALE_PERCENTAGE: - { - const float negMaxPct = getAutoScalePercentageNegativeMaximum(); - const float negMinPct = getAutoScalePercentageNegativeMinimum(); - const float posMinPct = getAutoScalePercentagePositiveMinimum(); - const float posMaxPct = getAutoScalePercentagePositiveMaximum(); - - negMax = statistics->getApproxNegativePercentile(negMaxPct); - negMin = statistics->getApproxNegativePercentile(negMinPct); - posMin = statistics->getApproxPositivePercentile(posMinPct); - posMax = statistics->getApproxPositivePercentile(posMaxPct); - } - break; - case PaletteScaleModeEnum::MODE_USER_SCALE: - negMax = getUserScaleNegativeMaximum(); - negMin = getUserScaleNegativeMinimum(); - posMin = getUserScalePositiveMinimum(); - posMax = getUserScalePositiveMaximum(); - break; - } - - /* - * numeric values displayed for negative data - */ - std::vector negativeValues; - negativeValues.push_back(negMax); - if (this->numericSubdivisionCount > 0) { - const float range = negMin - negMax; - const float interval = range / (this->numericSubdivisionCount + 1); - for (int32_t i = 0; i < this->numericSubdivisionCount; i++) { - negativeValues.push_back(negMax - + (interval * (i + 1))); - } - } - negativeValues.push_back(negMin); - - /* - * numeric values displayed for positive data - */ - std::vector positiveValues; - positiveValues.push_back(posMin); - if (this->numericSubdivisionCount > 0) { - const float range = posMax - posMin; - const float interval = range / (this->numericSubdivisionCount + 1); - for (int32_t i = 0; i < this->numericSubdivisionCount; i++) { - positiveValues.push_back(posMin - + (interval * (i + 1))); - } - } - positiveValues.push_back(posMax); - - /* - * Wil need to override these values when percentile - */ - NumericFormatModeEnum::Enum numericFormatModeForTextFormatting = this->numericFormatMode; - int32_t precisionDigitsForTextFormatting = this->precisionDigits; - - /* - * Processing for percentile mode - * Convert numeric values to percentiles - */ - switch (this->colorBarValuesMode) { - case PaletteColorBarValuesModeEnum::DATA: - break; - case PaletteColorBarValuesModeEnum::PERCENTILE: - for (std::vector::iterator iter = positiveValues.begin(); - iter != positiveValues.end(); - iter++) { - const float percentile = statistics->getPositiveValuePercentile(*iter); - *iter = percentile; - } - for (std::vector::iterator iter = negativeValues.begin(); - iter != negativeValues.end(); - iter++) { - const float percentile = statistics->getNegativeValuePercentile(*iter); - *iter = percentile; - } - - /* - * Decimal mode integers for percentile - */ - numericFormatModeForTextFormatting = NumericFormatModeEnum::DECIMAL; - precisionDigitsForTextFormatting = 0; - break; - case PaletteColorBarValuesModeEnum::SIGN_ONLY: - CaretAssertMessage(0, "Should never get here. Sign only handled above"); - break; - } - - /* - * Create text representations for negative values - */ - const int32_t numberOfNegValues = static_cast(negativeValues.size()); - std::vector negativeValuesText(numberOfNegValues); - NumericTextFormatting::formatValueRange(numericFormatModeForTextFormatting, - precisionDigitsForTextFormatting, - &negativeValues[0], - &negativeValuesText[0], - numberOfNegValues); - - /* - * Create text representations for positive values - */ - const int32_t numberOfPosValues = static_cast(positiveValues.size()); - std::vector positiveValuesText(numberOfPosValues); - NumericTextFormatting::formatValueRange(numericFormatModeForTextFormatting, - precisionDigitsForTextFormatting, - &positiveValues[0], - &positiveValuesText[0], - numberOfPosValues); - - CaretAssert(negativeValues.size() == negativeValuesText.size()); - CaretAssert(positiveValues.size() == positiveValuesText.size()); - - /* - * Types of values for display - */ - const bool positiveDataDisplayedFlag = isDisplayPositiveDataFlag(); - const bool negativeDataDisplayedFlag = isDisplayNegativeDataFlag(); - - /* - * Are both negative and positive values displayed? - */ - AString zeroValueText; - if (negativeDataDisplayedFlag - && positiveDataDisplayedFlag) { - CaretAssert(negativeValuesText.size() > 0); - const AString negMinText = negativeValuesText.back(); - CaretAssert(positiveValuesText.size() > 0); - const AString posMinText = positiveValuesText.front(); - - if (negMinText == posMinText) { - /* - * When the negative min and positive min values are the - * same, there is no need to display both of them. - */ - zeroValueText = negMinText; - } - else { - /* - * When the negative min and positive min values are the - * different, display both an separate with a slash - */ - zeroValueText = negMinText + "/" + posMinText; - } - - /* - * Since the negative min and positive min text values - * are dislayed together by above code, remove these - * values from their respective text values. - */ - negativeValuesText.resize(negativeValuesText.size() - 1); - negativeValues.resize(negativeValues.size() - 1); - CaretAssert(negativeValues.size() == negativeValuesText.size()); - positiveValuesText.erase(positiveValuesText.begin()); - positiveValues.erase(positiveValues.begin()); - CaretAssert(positiveValues.size() == positiveValuesText.size()); - } - - /* - * The positions of the text are normalized in the range zero - * to one where zero is at the left side of the color bar - * and one is at the right side of the color bar. - */ - float negPositionStart = 0.0; - float negPositionInterval = 0.0; - float posPositionStart = 0.0; - float posPositionInterval = 0.0; - if (negativeDataDisplayedFlag - && positiveDataDisplayedFlag) { - negPositionStart = 0.0; - CaretAssert(negativeValuesText.size() > 0); - negPositionInterval = (0.5 / negativeValuesText.size()); - - CaretAssert(positiveValuesText.size() > 0); - posPositionInterval = (0.5 / positiveValuesText.size()); - posPositionStart = 0.5 + posPositionInterval; - } - else if (negativeDataDisplayedFlag) { - negPositionStart = 0.0; - negPositionInterval = 1.0; - if (negativeValuesText.size() > 1) { - CaretAssert(negativeValuesText.size() > 0); - negPositionInterval = (1.0 / (negativeValuesText.size() - 1)); - } - } - else if (positiveDataDisplayedFlag) { - posPositionStart = 0.0; - posPositionInterval = 1.0; - if (positiveValuesText.size() > 1) { - CaretAssert(positiveValuesText.size() > 0); - posPositionInterval = (1.0 / (positiveValuesText.size() - 1)); - } - } - - /* - * Output the negative values text - */ - if (negativeDataDisplayedFlag) { - const int32_t numValues = static_cast(negativeValuesText.size()); - for (int32_t i = 0; i < numValues; i++) { - CaretAssertVectorIndex(negativeValuesText, i); - const float value = (negPositionStart - + (i * negPositionInterval)); - normalizedPositionAndTextOut.push_back(std::make_pair(value, - negativeValuesText[i])); - } - } - - /* - * Add the zero value text - */ - if (negativeDataDisplayedFlag - && positiveDataDisplayedFlag) { - normalizedPositionAndTextOut.push_back(std::make_pair(0.5, - zeroValueText)); - } - - /* - * Add the positive values text - */ - if (positiveDataDisplayedFlag) { - const int32_t numValues = static_cast(positiveValuesText.size()); - for (int32_t i = 0; i < numValues; i++) { - CaretAssertVectorIndex(positiveValuesText, i); - const float value = (posPositionStart - + (i * posPositionInterval)); - normalizedPositionAndTextOut.push_back(std::make_pair(value, - positiveValuesText[i])); - } - } - - /* - * Add percentage signs when percentile is selected - */ - switch (this->colorBarValuesMode) { - case PaletteColorBarValuesModeEnum::DATA: - break; - case PaletteColorBarValuesModeEnum::PERCENTILE: - for (std::vector >::iterator iter = normalizedPositionAndTextOut.begin(); - iter != normalizedPositionAndTextOut.end(); - iter++) { - iter->second.append("%"); - } - break; - case PaletteColorBarValuesModeEnum::SIGN_ONLY: - break; - } - - const bool debugFlag = false; - if (debugFlag) { - const int numItems = static_cast(normalizedPositionAndTextOut.size()); - std::cout << "Colorbar: " << std::endl; - for (int32_t i = 0; i < numItems; i++) { - std::cout << " " << qPrintable(QString::number(normalizedPositionAndTextOut[i].first) - + ": " - + normalizedPositionAndTextOut[i].second) << std::endl; - } - std::cout << std::endl; - } + return true; } /** @@ -2467,76 +2517,246 @@ PaletteColorMapping::setThresholdNegMinPosMaxLinked(const bool linked) } /** - * @return The numeric format mode. + * @return The histogram range mode. + */ +PaletteHistogramRangeModeEnum::Enum +PaletteColorMapping::getHistogramRangeMode() const +{ + return this->histogramRangeMode; +} + +/** + * Set the histogram range mode. + * + * @param histogramRangeMode + * New value for histogram range mode. + */ +void +PaletteColorMapping::setHistogramRangeMode(const PaletteHistogramRangeModeEnum::Enum histogramRangeMode) +{ + if (histogramRangeMode != this->histogramRangeMode) { + this->histogramRangeMode = histogramRangeMode; + setModified(); + } +} + +/** + * @return show histogram bars + */ +bool +PaletteColorMapping::isHistogramBarsVisible() const +{ + return this->histogramBarsVisible; +} + +/** + * Set show histogram bars + * + * @param histogramBarsVisible + * New value for show histogram bars + */ +void +PaletteColorMapping::setHistogramBarsVisible(const bool histogramBarsVisible) +{ + if (histogramBarsVisible != this->histogramBarsVisible) { + this->histogramBarsVisible = histogramBarsVisible; + setModified(); + } +} + +/** + * @return show histogram envelope + */ +bool +PaletteColorMapping::isHistogramEnvelopeVisible() const +{ + return this->histogramEnvelopeVisible; +} + +/** + * Set show histogram envelope + * + * @param histogramEnvelopeVisible + * New value for show histogram envelope + */ +void +PaletteColorMapping::setHistogramEnvelopeVisible(const bool histogramEnvelopeVisible) +{ + if (histogramEnvelopeVisible != this->histogramEnvelopeVisible) { + this->histogramEnvelopeVisible = histogramEnvelopeVisible; + setModified(); + } +} + +/** + * @return Color for drawing histogram bars + */ +CaretColorEnum::Enum +PaletteColorMapping::getHistogramBarsColor() const +{ + return histogramBarsColor; +} + +/** + * Set the color for drawing the histogram bars + * + * @param histogramBarsColor + * New color for the histogram bars + */ +void +PaletteColorMapping::setHistogramBarsColor(const CaretColorEnum::Enum histogramBarsColor) +{ + if (histogramBarsColor != this->histogramBarsColor) { + this->histogramBarsColor = histogramBarsColor; + setModified(); + } +} + +/** + * @return Color for drawing histogram envelope + */ +CaretColorEnum::Enum +PaletteColorMapping::getHistogramEnvelopeColor() const +{ + return histogramEnvelopeColor; +} + +/** + * Set the color for drawing the histogram envelope + * + * @param histogramEnvelopeColor + * New color for the histogram envelope. + */ +void +PaletteColorMapping::setHistogramEnvelopeColor(const CaretColorEnum::Enum histogramEnvelopeColor) +{ + if (histogramEnvelopeColor != this->histogramEnvelopeColor) { + this->histogramEnvelopeColor = histogramEnvelopeColor; + setModified(); + } +} + +/** + * @return The line width percentage for drawing histogram as envelope. + */ +float +PaletteColorMapping::getHistogramEnvelopeLineWidthPercentage() const +{ + return histogramEnvelopeLineWidthPercentage; +} + +/** + * Set line width percentage for drawing histogram as envelope. + * + * @param lineWidthPercentage + * New value for line width percentage. + */ +void +PaletteColorMapping::setHistogramEnvelopeLineWidthPercentage(const float lineWidthPercentage) +{ + if (lineWidthPercentage != this->histogramEnvelopeLineWidthPercentage) { + this->histogramEnvelopeLineWidthPercentage = lineWidthPercentage; + setModified(); + } +} + + +/** + * @return Histogram number of buckets. + */ +int32_t +PaletteColorMapping::getHistogramNumberOfBuckets() const +{ + return this->histogramNumberOfBuckets; +} + +/** + * Set the histogram number of buckets. + * + * @param histogramNumberOfBuckets + * Number of buckets for the histogram. + */ +void +PaletteColorMapping::setHistogramNumberOfBuckets(const int32_t histogramNumberOfBuckets) +{ + if (histogramNumberOfBuckets != this->histogramNumberOfBuckets) { + this->histogramNumberOfBuckets = histogramNumberOfBuckets; + setModified(); + } +} + +/** + * @return The color bar numeric format mode. */ -NumericFormatModeEnum::Enum PaletteColorMapping::getNumericFormatMode() const +NumericFormatModeEnum::Enum +PaletteColorMapping::getColorBarNumericFormatMode() const { - return this->numericFormatMode; + return this->colorBarNumericFormatMode; } /** - * Set the numeric format mode. + * Set the color bar numeric format mode. * - * @param numericFormatMode - * New value for precision mode. + * @param colorBarNumericFormatMode + * New value for color bar numberic format mode */ void -PaletteColorMapping::setNumericFormatMode(const NumericFormatModeEnum::Enum numericFormatMode) +PaletteColorMapping::setColorBarNumericFormatMode(const NumericFormatModeEnum::Enum colorBarNumericFormatMode) { - if (numericFormatMode != this->numericFormatMode) { - this->numericFormatMode = numericFormatMode; + if (colorBarNumericFormatMode != this->colorBarNumericFormatMode) { + this->colorBarNumericFormatMode = colorBarNumericFormatMode; setModified(); } } /** - * @return The precision digits (right of decimal). + * @return The color bar precision digits (right of decimal). */ int32_t -PaletteColorMapping::getPrecisionDigits() const +PaletteColorMapping::getColorBarPrecisionDigits() const { - return this->precisionDigits; + return this->colorBarPrecisionDigits; } /** - * Set the precision digits (right of decimal) + * Set the color bar precision digits (right of decimal) * - * @param precisionDigits + * @param colorBarPrecisionDigits * New value for number of digits right of decimal. */ void -PaletteColorMapping::setPrecisionDigits(const int32_t precisionDigits) +PaletteColorMapping::setColorBarPrecisionDigits(const int32_t colorBarPrecisionDigits) { - if (precisionDigits != this->precisionDigits) { - this->precisionDigits = precisionDigits; + if (colorBarPrecisionDigits != this->colorBarPrecisionDigits) { + this->colorBarPrecisionDigits = colorBarPrecisionDigits; setModified(); } } /** - * @return The numeric subdivision count which is the number of + * @return The color bar numeric subdivision count which is the number of * numeric values uniformly spaced between zero and the maximum * value. */ int32_t -PaletteColorMapping::getNumericSubdivisionCount() const +PaletteColorMapping::getColorBarNumericSubdivisionCount() const { - return this->numericSubdivisionCount; + return this->colorBarNumericSubdivisionCount; } /** - * Set the numeric subdivision count which is the number of + * Set the color bar numeric subdivision count which is the number of * numeric values uniformly spaced between zero and the maximum * value. * - * @param numericSubvisionCount + * @param colorBarNumericSubdivisionCount * New value for subdivision count. */ void -PaletteColorMapping::setNumericSubdivisionCount(const int32_t numericSubdivisionCount) +PaletteColorMapping::setColorBarNumericSubdivisionCount(const int32_t colorBarNumericSubdivisionCount) { - if (numericSubdivisionCount != this->numericSubdivisionCount) { - this->numericSubdivisionCount = numericSubdivisionCount; + if (colorBarNumericSubdivisionCount != this->colorBarNumericSubdivisionCount) { + this->colorBarNumericSubdivisionCount = colorBarNumericSubdivisionCount; setModified(); } } @@ -2567,9 +2787,9 @@ void PaletteColorMapping::setColorBarValuesMode(const PaletteColorBarValuesModeE * @param Is show tick marks selected? */ bool -PaletteColorMapping::isShowTickMarksSelected() const +PaletteColorMapping::isColorBarShowTickMarksSelected() const { - return this->showTickMarksSelected; + return this->colorBarShowTickMarksSelected; } /** @@ -2579,10 +2799,10 @@ PaletteColorMapping::isShowTickMarksSelected() const * New selection status. */ void -PaletteColorMapping::setShowTickMarksSelected(const bool selected) +PaletteColorMapping::setColorBarShowTickMarksSelected(const bool selected) { - if (selected != this->showTickMarksSelected) { - this->showTickMarksSelected = selected; + if (selected != this->colorBarShowTickMarksSelected) { + this->colorBarShowTickMarksSelected = selected; setModified(); } } diff --git a/src/Palette/PaletteColorMapping.h b/src/Palette/PaletteColorMapping.h index a0b57ca4e1d06cc606a59ad269f0df61d805531c..ec672e0843da02c10fe21479f8611327c72b7022 100644 --- a/src/Palette/PaletteColorMapping.h +++ b/src/Palette/PaletteColorMapping.h @@ -21,14 +21,20 @@ */ /*LICENSE_END*/ +#include + #include #include "CaretObject.h" #include "AnnotationColorBarNumericText.h" +#include "CaretColorEnum.h" #include "NumericFormatModeEnum.h" #include "PaletteColorBarValuesModeEnum.h" #include "PaletteEnums.h" +#include "PaletteHistogramRangeModeEnum.h" +#include "PaletteInvertModeEnum.h" +#include "PaletteModifiedStatusEnum.h" #include "PaletteThresholdRangeModeEnum.h" #include "XmlException.h" @@ -36,6 +42,7 @@ namespace caret { class AnnotationColorBar; class FastStatistics; + class Palette; class XmlWriter; /** @@ -43,7 +50,7 @@ namespace caret { */ class PaletteColorMapping : public CaretObject { - public: + public: PaletteColorMapping(); PaletteColorMapping(const PaletteColorMapping& o); @@ -56,10 +63,12 @@ namespace caret { virtual ~PaletteColorMapping(); - void copy(const PaletteColorMapping& pcm); + void copy(const PaletteColorMapping& pcm, + const bool copyHistogramAttributesFlag); private: - void copyHelper(const PaletteColorMapping& o); + void copyHelper(const PaletteColorMapping& o, + const bool copyHistogramAttributesFlag); void initializeMembersPaletteColorMapping(); @@ -116,6 +125,10 @@ namespace caret { void setInterpolatePaletteFlag(const bool interpolatePaletteFlag); + PaletteInvertModeEnum::Enum getInvertedMode() const; + + void setInvertedMode(const PaletteInvertModeEnum::Enum invertedMode); + PaletteScaleModeEnum::Enum getScaleMode() const; void setScaleMode(const PaletteScaleModeEnum::Enum scaleMode); @@ -124,6 +137,8 @@ namespace caret { void setSelectedPaletteName(const AString& selectedPaletteName); + const Palette* getPalette() const; + void setSelectedPaletteToPsych(); void setSelectedPaletteToPsychNoNone(); @@ -206,25 +221,53 @@ namespace caret { void setThresholdNegMinPosMaxLinked(const bool linked); - NumericFormatModeEnum::Enum getNumericFormatMode() const; + PaletteHistogramRangeModeEnum::Enum getHistogramRangeMode() const; + + void setHistogramRangeMode(const PaletteHistogramRangeModeEnum::Enum histogramRangeMode); + + bool isHistogramBarsVisible() const; + + void setHistogramBarsVisible(const bool histogramBarsVisible); + + bool isHistogramEnvelopeVisible() const; + + void setHistogramEnvelopeVisible(const bool histogramEnvelopeVisible); + + CaretColorEnum::Enum getHistogramBarsColor() const; + + void setHistogramBarsColor(const CaretColorEnum::Enum histogramBarsColor); + + CaretColorEnum::Enum getHistogramEnvelopeColor() const; + + void setHistogramEnvelopeColor(const CaretColorEnum::Enum histogramEnvelopeColor); + + float getHistogramEnvelopeLineWidthPercentage() const; + + void setHistogramEnvelopeLineWidthPercentage(const float lineWidthPercentage); - int32_t getPrecisionDigits() const; + int32_t getHistogramNumberOfBuckets() const; - int32_t getNumericSubdivisionCount() const; + void setHistogramNumberOfBuckets(const int32_t histogramNumberOfBuckets); + + NumericFormatModeEnum::Enum getColorBarNumericFormatMode() const; + + int32_t getColorBarPrecisionDigits() const; - void setNumericFormatMode(const NumericFormatModeEnum::Enum numericFormatMode); + int32_t getColorBarNumericSubdivisionCount() const; - void setPrecisionDigits(const int32_t precisionDigits); + void setColorBarNumericFormatMode(const NumericFormatModeEnum::Enum colorBarNumericFormatMode); - void setNumericSubdivisionCount(const int32_t numericSubdivisionCount); + void setColorBarPrecisionDigits(const int32_t colorBarPrecisionDigits); + + void setColorBarNumericSubdivisionCount(const int32_t colroBarNumericSubdivisionCount); PaletteColorBarValuesModeEnum::Enum getColorBarValuesMode() const; void setColorBarValuesMode(const PaletteColorBarValuesModeEnum::Enum colorBarValuesMode); - bool isShowTickMarksSelected() const; + bool isColorBarShowTickMarksSelected() const; - void setShowTickMarksSelected(const bool selected); + void setColorBarShowTickMarksSelected(const bool selected); void setModified(); @@ -232,14 +275,15 @@ namespace caret { bool isModified() const; + void setSceneModified(); + + PaletteModifiedStatusEnum::Enum getModifiedStatus() const; + void mapDataToPaletteNormalizedValues(const FastStatistics* statistics, const float* dataValues, float* normalizedValuesOut, const int64_t numberOfData) const; - void getPaletteColorBarScaleText(const FastStatistics* statistics, - std::vector >& normalizedPositionAndTextOut) const; - void getPaletteColorBarScaleText(const FastStatistics* statistics, std::vector& colorBarNumericTextOut) const; @@ -249,7 +293,12 @@ namespace caret { /** A negative value near zero - may be zero! */ static const float SMALL_NEGATIVE; + /** TSC: the excluded zone of normalization is a separate issue to zero detection in the data + * specifically, it is a HACK, in order for palettes to be able to specify a special color for data that is 0, which is not involved in color interpolation */ + static const float PALETTE_ZERO_COLOR_ZONE; private: + bool isZeroNumericText(const AString& numericText) const; + PaletteScaleModeEnum::Enum scaleMode; float autoScalePercentageNegativeMaximum; @@ -276,6 +325,8 @@ namespace caret { bool interpolatePaletteFlag; + PaletteInvertModeEnum::Enum invertedMode = PaletteInvertModeEnum::OFF; + bool displayPositiveDataFlag; bool displayZeroDataFlag; @@ -306,24 +357,42 @@ namespace caret { bool thresholdNegMinPosMaxLinked; - NumericFormatModeEnum::Enum numericFormatMode; + PaletteHistogramRangeModeEnum::Enum histogramRangeMode; + + bool histogramBarsVisible; + + bool histogramEnvelopeVisible; + + CaretColorEnum::Enum histogramBarsColor; + + CaretColorEnum::Enum histogramEnvelopeColor; + + float histogramEnvelopeLineWidthPercentage; + + int32_t histogramNumberOfBuckets; + + NumericFormatModeEnum::Enum colorBarNumericFormatMode; - int32_t precisionDigits; + int32_t colorBarPrecisionDigits; - int32_t numericSubdivisionCount; + int32_t colorBarNumericSubdivisionCount; PaletteColorBarValuesModeEnum::Enum colorBarValuesMode; - bool showTickMarksSelected; + bool colorBarShowTickMarksSelected; /**Tracks modification, DO NOT copy */ - bool modifiedFlag; + PaletteModifiedStatusEnum::Enum modifiedStatus; + /** keeps missing palettes from being logged more than once */ + static std::set s_missingPaletteNames; }; #ifdef __PALETTE_COLOR_MAPPING_DECLARE__ - const float PaletteColorMapping::SMALL_POSITIVE = 0.0; // JWH 24 April 2015 0.00001; - const float PaletteColorMapping::SMALL_NEGATIVE = 0.0; // JWH 24 April 2015 -0.00001; + const float PaletteColorMapping::SMALL_POSITIVE = 0.0; + const float PaletteColorMapping::SMALL_NEGATIVE = 0.0; + const float PaletteColorMapping::PALETTE_ZERO_COLOR_ZONE = 0.00001f; + std::set PaletteColorMapping::s_missingPaletteNames; #endif // __PALETTE_COLOR_MAPPING_DECLARE__ } // namespace diff --git a/src/Palette/PaletteColorMappingSaxReader.cxx b/src/Palette/PaletteColorMappingSaxReader.cxx index ded2e4b1b72bfe3e81e9101ff5eb44cded383d9a..1d1a611812c4aacd8e3d40fb1d1a7ad7fd4e5d22 100644 --- a/src/Palette/PaletteColorMappingSaxReader.cxx +++ b/src/Palette/PaletteColorMappingSaxReader.cxx @@ -40,6 +40,7 @@ PaletteColorMappingSaxReader::PaletteColorMappingSaxReader(PaletteColorMapping* this->stateStack.push(state); this->elementText = ""; this->paletteColorMapping = paletteColorMapping; + this->paletteColorMapping->setInvertedMode(PaletteInvertModeEnum::OFF); } /** @@ -183,6 +184,28 @@ PaletteColorMappingSaxReader::endElement(const AString& /* namspaceURI */, else if (qName == PaletteColorMappingXmlElements::XML_TAG_INTERPOLATE) { this->paletteColorMapping->setInterpolatePaletteFlag(toBool(this->elementText)); } + else if (qName == PaletteColorMappingXmlElements::XML_TAG_INVERT) { + bool isValid = false; + PaletteInvertModeEnum::Enum invertMode = PaletteInvertModeEnum::fromName(this->elementText, + &isValid); + if (isValid) { + this->paletteColorMapping->setInvertedMode(invertMode); + } + else { + if (this->elementText == "true") { + this->paletteColorMapping->setInvertedMode(PaletteInvertModeEnum::POSITIVE_WITH_NEGATIVE); + } + else if (this->elementText == "false") { + this->paletteColorMapping->setInvertedMode(PaletteInvertModeEnum::OFF); + } + else { + this->paletteColorMapping->setInvertedMode(PaletteInvertModeEnum::OFF); + CaretLogWarning("Invalid PaletteInvertModeEnum::Enum value \"" + + this->elementText + + "\""); + } + } + } else if (qName == PaletteColorMappingXmlElements::XML_TAG_PALETTE_NAME) { this->paletteColorMapping->setSelectedPaletteName(this->elementText); } @@ -289,25 +312,83 @@ PaletteColorMappingSaxReader::endElement(const AString& /* namspaceURI */, this->paletteColorMapping->setThresholdNegMinPosMaxLinked(toBool(this->elementText)); } else if (qName == PaletteColorMappingXmlElements::XML_TAG_PALETTE_COLOR_MAPPING) { - /* Top level tag, nothing to do */ + /* WB- + * We want to default the range mode to 'FILE'. While we do that in the constructor + * for PaletteColorMapping, most files contain PaletteColorMapping in the XML for + * each map and when the file is read, the default values are replaced. So, + * to avoid breaking older scenes, only change the range mode from 'MAP' to 'FILE' + * if thresholding is off when reading palette color mapping from XML. + */ + if (this->paletteColorMapping->getThresholdType() == PaletteThresholdTypeEnum::THRESHOLD_TYPE_OFF) { + this->paletteColorMapping->setThresholdRangeMode(PaletteThresholdRangeModeEnum::PALETTE_THRESHOLD_RANGE_MODE_FILE); + } + } + else if (qName == PaletteColorMappingXmlElements::XML_TAG_HISTOGRAM_RANGE_MODE) { + bool isValid = false; + PaletteHistogramRangeModeEnum::Enum histogramRangeMode = PaletteHistogramRangeModeEnum::fromName(this->elementText, + &isValid); + if (isValid) { + this->paletteColorMapping->setHistogramRangeMode(histogramRangeMode); + } + else { + throw XmlSaxParserException("Invalid PaletteHistogramRangeModeEnum::Enum: " + + this->elementText); + } + } + else if (qName == PaletteColorMappingXmlElements::XML_TAG_HISTOGRAM_BARS_VISIBLE) { + this->paletteColorMapping->setHistogramBarsVisible(toBool(this->elementText)); + } + else if (qName == PaletteColorMappingXmlElements::XML_TAG_HISTOGRAM_ENVELOPE_VISIBLE) { + this->paletteColorMapping->setHistogramEnvelopeVisible(toBool(this->elementText)); + } + else if ((qName == PaletteColorMappingXmlElements::XML_TAG_HISTOGRAM_BARS_COLOR) + || (qName == "HistogramColor")) { + bool isValid = false; + CaretColorEnum::Enum histogramBarsColor = CaretColorEnum::fromName(this->elementText, + &isValid); + if (isValid) { + this->paletteColorMapping->setHistogramBarsColor(histogramBarsColor); + } + else { + throw XmlSaxParserException("Invalid CaretColorEnum::Enum: " + + this->elementText); + } + } + else if (qName == PaletteColorMappingXmlElements::XML_TAG_HISTOGRAM_ENVELOPE_COLOR) { + bool isValid = false; + CaretColorEnum::Enum histogramEnvelopeColor = CaretColorEnum::fromName(this->elementText, + &isValid); + if (isValid) { + this->paletteColorMapping->setHistogramEnvelopeColor(histogramEnvelopeColor); + } + else { + throw XmlSaxParserException("Invalid CaretColorEnum::Enum: " + + this->elementText); + } + } + else if (qName == PaletteColorMappingXmlElements::XML_TAG_HISTOGRAM_ENVELOPE_LINE_WIDTH_PERCENTAGE) { + this->paletteColorMapping->setHistogramEnvelopeLineWidthPercentage(this->elementText.toFloat()); + } + else if (qName == PaletteColorMappingXmlElements::XML_TAG_HISTOGRAM_NUMBER_OF_BUCKETS) { + this->paletteColorMapping->setHistogramNumberOfBuckets(this->elementText.toInt()); } else if (qName == PaletteColorMappingXmlElements::XML_TAG_NUMERIC_FORMAT_MODE) { bool isValid = false; NumericFormatModeEnum::Enum numericFormatMode = NumericFormatModeEnum::fromName(this->elementText, &isValid); if (isValid) { - this->paletteColorMapping->setNumericFormatMode(numericFormatMode); + this->paletteColorMapping->setColorBarNumericFormatMode(numericFormatMode); } else { - throw XmlSaxParserException("Invalid PalettePrecisionModeEnum::Enum: " + throw XmlSaxParserException("Invalid NumericFormatModeEnum::Enum: " + this->elementText); } } else if (qName == PaletteColorMappingXmlElements::XML_TAG_PRECISION_DIGITS) { - this->paletteColorMapping->setPrecisionDigits(this->elementText.toInt()); + this->paletteColorMapping->setColorBarPrecisionDigits(this->elementText.toInt()); } else if (qName == PaletteColorMappingXmlElements::XML_TAG_NUMERIC_SUBDIVISIONS) { - this->paletteColorMapping->setNumericSubdivisionCount(this->elementText.toInt()); + this->paletteColorMapping->setColorBarNumericSubdivisionCount(this->elementText.toInt()); } else if (qName == PaletteColorMappingXmlElements::XML_TAG_COLOR_BAR_VALUES_MODE) { bool isValid = false; @@ -322,15 +403,16 @@ PaletteColorMappingSaxReader::endElement(const AString& /* namspaceURI */, } } else if (qName == PaletteColorMappingXmlElements::XML_TAG_SHOW_TICK_MARKS) { - this->paletteColorMapping->setShowTickMarksSelected(toBool(this->elementText)); + this->paletteColorMapping->setColorBarShowTickMarksSelected(toBool(this->elementText)); } else { std::ostringstream str; str - << "Unrecognized palette color mapping element \"" + << "Unrecognized (perhaps new) palette color mapping element ignored \"" << qName.toStdString() - << "\"."; - throw XmlSaxParserException(AString::fromStdString(str.str())); + << "\" with content: " + << this->elementText.toStdString(); + warning(XmlSaxParserException(AString::fromStdString(str.str()))); } break; } diff --git a/src/Palette/PaletteColorMappingXmlElements.h b/src/Palette/PaletteColorMappingXmlElements.h index 820a8f1007c68cfdd1b175a8fa52cbeca029858b..9769ee499ceb26e5a27ee0cef8b2cbeb15431ae0 100644 --- a/src/Palette/PaletteColorMappingXmlElements.h +++ b/src/Palette/PaletteColorMappingXmlElements.h @@ -33,6 +33,7 @@ namespace PaletteColorMappingXmlElements { static const AString XML_TAG_USER_SCALE_VALUES = "UserScaleValues"; static const AString XML_TAG_PALETTE_NAME = "PaletteName"; static const AString XML_TAG_INTERPOLATE = "InterpolatePalette"; + static const AString XML_TAG_INVERT = "InvertPalette"; static const AString XML_TAG_DISPLAY_POSITIVE = "DisplayPositiveData"; static const AString XML_TAG_DISPLAY_NEGATIVE = "DisplayNegativeData"; static const AString XML_TAG_DISPLAY_ZERO = "DisplayZeroData"; @@ -45,6 +46,13 @@ namespace PaletteColorMappingXmlElements { static const AString XML_TAG_THRESHOLD_FAILURE_IN_GREEN = "ThresholdFailureInGreen"; static const AString XML_TAG_THRESHOLD_RANGE_MODE = "ThresholdRangeMode"; static const AString XML_TAG_THRESHOLD_NEG_MIN_POS_MAX_LINKED = "ThresholdLowHighLinked"; + static const AString XML_TAG_HISTOGRAM_RANGE_MODE = "HistogramRangeMode"; + static const AString XML_TAG_HISTOGRAM_BARS_VISIBLE = "HistogramBarsVisible"; + static const AString XML_TAG_HISTOGRAM_ENVELOPE_VISIBLE = "HistogramEnvelopeVisible"; + static const AString XML_TAG_HISTOGRAM_BARS_COLOR = "HistogramBarsColor"; + static const AString XML_TAG_HISTOGRAM_ENVELOPE_COLOR = "HistogramEnvelopeColor"; + static const AString XML_TAG_HISTOGRAM_ENVELOPE_LINE_WIDTH_PERCENTAGE = "HistogramEnvelopeLineWidthPercentage"; + static const AString XML_TAG_HISTOGRAM_NUMBER_OF_BUCKETS = "HistogramNumberOfBuckets"; static const AString XML_TAG_NUMERIC_FORMAT_MODE = "NumericFormatMode"; static const AString XML_TAG_PRECISION_DIGITS = "PrecisionDigits"; static const AString XML_TAG_NUMERIC_SUBDIVISIONS = "NumericSubivisions"; diff --git a/src/Palette/PaletteEnums.cxx b/src/Palette/PaletteEnums.cxx index 1ba7279ac0a57dce721ab998f47932f8d9416aa4..979014019f4ac2196e2866d08541eb4e7f8089d2 100644 --- a/src/Palette/PaletteEnums.cxx +++ b/src/Palette/PaletteEnums.cxx @@ -537,7 +537,8 @@ PaletteThresholdTypeEnum::initialize() enumData.push_back(PaletteThresholdTypeEnum(THRESHOLD_TYPE_MAPPED_AVERAGE_AREA, 3, "THRESHOLD_TYPE_MAPPED_AVERAGE_AREA", "Mapped Average Area")); } else { - enumData.push_back(PaletteThresholdTypeEnum(THRESHOLD_TYPE_NORMAL, 1, "THRESHOLD_TYPE_NORMAL", "On")); + enumData.push_back(PaletteThresholdTypeEnum(THRESHOLD_TYPE_NORMAL, 1, "THRESHOLD_TYPE_NORMAL", "Self")); + enumData.push_back(PaletteThresholdTypeEnum(THRESHOLD_TYPE_FILE, 2, "THRESHOLD_TYPE_FILE", "File")); } } diff --git a/src/Palette/PaletteEnums.h b/src/Palette/PaletteEnums.h index 21e577af3455f64e3203565280f512125c71e3be..863b1bf97470b3efb12f777a5f2fc911d228b479 100644 --- a/src/Palette/PaletteEnums.h +++ b/src/Palette/PaletteEnums.h @@ -147,6 +147,8 @@ public: THRESHOLD_TYPE_OFF, /** normal thresholding */ THRESHOLD_TYPE_NORMAL, + /** threshold with another file */ + THRESHOLD_TYPE_FILE, /** threshold from mapping of volume */ THRESHOLD_TYPE_MAPPED, /** threshold from mapping to PALS average area */ diff --git a/src/Palette/PaletteHistogramRangeModeEnum.cxx b/src/Palette/PaletteHistogramRangeModeEnum.cxx new file mode 100644 index 0000000000000000000000000000000000000000..1cf0d5a3e7d2f3d9c5875d10879c1614eff78877 --- /dev/null +++ b/src/Palette/PaletteHistogramRangeModeEnum.cxx @@ -0,0 +1,373 @@ + +/*LICENSE_START*/ +/* + * Copyright (C) 2017 Washington University School of Medicine + * + * 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 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ +/*LICENSE_END*/ + +#include +#define __PALETTE_HISTOGRAM_RANGE_MODE_ENUM_DECLARE__ +#include "PaletteHistogramRangeModeEnum.h" +#undef __PALETTE_HISTOGRAM_RANGE_MODE_ENUM_DECLARE__ + +#include "CaretAssert.h" + +using namespace caret; + + +/** + * \class caret::PaletteHistogramRangeModeEnum + * \brief Controls whether histogram shows all data or data using map statistics + * + * Using this enumerated type in the GUI with an EnumComboBoxTemplate + * + * Header File (.h) + * Forward declare the data type: + * class EnumComboBoxTemplate; + * + * Declare the member: + * EnumComboBoxTemplate* m_paletteHistogramRangeModeEnumComboBox; + * + * Declare a slot that is called when user changes selection + * private slots: + * void paletteHistogramRangeModeEnumComboBoxItemActivated(); + * + * Implementation File (.cxx) + * Include the header files + * #include "EnumComboBoxTemplate.h" + * #include "PaletteHistogramRangeModeEnum.h" + * + * Instatiate: + * m_paletteHistogramRangeModeEnumComboBox = new EnumComboBoxTemplate(this); + * m_paletteHistogramRangeModeEnumComboBox->setup(); + * + * Get notified when the user changes the selection: + * QObject::connect(m_paletteHistogramRangeModeEnumComboBox, SIGNAL(itemActivated()), + * this, SLOT(paletteHistogramRangeModeEnumComboBoxItemActivated())); + * + * Update the selection: + * m_paletteHistogramRangeModeEnumComboBox->setSelectedItem(NEW_VALUE); + * + * Read the selection: + * const PaletteHistogramRangeModeEnum::Enum VARIABLE = m_paletteHistogramRangeModeEnumComboBox->getSelectedItem(); + * + */ + +/** + * Constructor. + * + * @param enumValue + * An enumerated value. + * @param name + * Name of enumerated value. + * + * @param guiName + * User-friendly name for use in user-interface. + */ +PaletteHistogramRangeModeEnum::PaletteHistogramRangeModeEnum(const Enum enumValue, + const AString& name, + const AString& guiName) +{ + this->enumValue = enumValue; + this->integerCode = integerCodeCounter++; + this->name = name; + this->guiName = guiName; +} + +/** + * Destructor. + */ +PaletteHistogramRangeModeEnum::~PaletteHistogramRangeModeEnum() +{ +} + +/** + * Initialize the enumerated metadata. + */ +void +PaletteHistogramRangeModeEnum::initialize() +{ + if (initializedFlag) { + return; + } + initializedFlag = true; + + enumData.push_back(PaletteHistogramRangeModeEnum(PALETTE_HISTOGRAM_RANGE_ALL, + "PALETTE_HISTOGRAM_RANGE_ALL", + "All")); + + enumData.push_back(PaletteHistogramRangeModeEnum(PALETTE_HISTOGRAM_RANGE_MATCH_PALETTE, + "PALETTE_HISTOGRAM_RANGE_MATCH_PALETTE", + "Palette")); + +} + +/** + * Find the data for and enumerated value. + * @param enumValue + * The enumerated value. + * @return Pointer to data for this enumerated type + * or NULL if no data for type or if type is invalid. + */ +const PaletteHistogramRangeModeEnum* +PaletteHistogramRangeModeEnum::findData(const Enum enumValue) +{ + if (initializedFlag == false) initialize(); + + size_t num = enumData.size(); + for (size_t i = 0; i < num; i++) { + const PaletteHistogramRangeModeEnum* d = &enumData[i]; + if (d->enumValue == enumValue) { + return d; + } + } + + return NULL; +} + +/** + * Get a string representation of the enumerated type. + * @param enumValue + * Enumerated value. + * @return + * String representing enumerated value. + */ +AString +PaletteHistogramRangeModeEnum::toName(Enum enumValue) { + if (initializedFlag == false) initialize(); + + const PaletteHistogramRangeModeEnum* enumInstance = findData(enumValue); + return enumInstance->name; +} + +/** + * Get an enumerated value corresponding to its name. + * @param name + * Name of enumerated value. + * @param isValidOut + * If not NULL, it is set indicating that a + * enum value exists for the input name. + * @return + * Enumerated value. + */ +PaletteHistogramRangeModeEnum::Enum +PaletteHistogramRangeModeEnum::fromName(const AString& name, bool* isValidOut) +{ + if (initializedFlag == false) initialize(); + + bool validFlag = false; + Enum enumValue = PaletteHistogramRangeModeEnum::enumData[0].enumValue; + + for (std::vector::iterator iter = enumData.begin(); + iter != enumData.end(); + iter++) { + const PaletteHistogramRangeModeEnum& d = *iter; + if (d.name == name) { + enumValue = d.enumValue; + validFlag = true; + break; + } + } + + if (isValidOut != 0) { + *isValidOut = validFlag; + } + else if (validFlag == false) { + CaretAssertMessage(0, AString("Name " + name + "failed to match enumerated value for type PaletteHistogramRangeModeEnum")); + } + return enumValue; +} + +/** + * Get a GUI string representation of the enumerated type. + * @param enumValue + * Enumerated value. + * @return + * String representing enumerated value. + */ +AString +PaletteHistogramRangeModeEnum::toGuiName(Enum enumValue) { + if (initializedFlag == false) initialize(); + + const PaletteHistogramRangeModeEnum* enumInstance = findData(enumValue); + return enumInstance->guiName; +} + +/** + * Get an enumerated value corresponding to its GUI name. + * @param s + * Name of enumerated value. + * @param isValidOut + * If not NULL, it is set indicating that a + * enum value exists for the input name. + * @return + * Enumerated value. + */ +PaletteHistogramRangeModeEnum::Enum +PaletteHistogramRangeModeEnum::fromGuiName(const AString& guiName, bool* isValidOut) +{ + if (initializedFlag == false) initialize(); + + bool validFlag = false; + Enum enumValue = PaletteHistogramRangeModeEnum::enumData[0].enumValue; + + for (std::vector::iterator iter = enumData.begin(); + iter != enumData.end(); + iter++) { + const PaletteHistogramRangeModeEnum& d = *iter; + if (d.guiName == guiName) { + enumValue = d.enumValue; + validFlag = true; + break; + } + } + + if (isValidOut != 0) { + *isValidOut = validFlag; + } + else if (validFlag == false) { + CaretAssertMessage(0, AString("guiName " + guiName + "failed to match enumerated value for type PaletteHistogramRangeModeEnum")); + } + return enumValue; +} + +/** + * Get the integer code for a data type. + * + * @return + * Integer code for data type. + */ +int32_t +PaletteHistogramRangeModeEnum::toIntegerCode(Enum enumValue) +{ + if (initializedFlag == false) initialize(); + const PaletteHistogramRangeModeEnum* enumInstance = findData(enumValue); + return enumInstance->integerCode; +} + +/** + * Find the data type corresponding to an integer code. + * + * @param integerCode + * Integer code for enum. + * @param isValidOut + * If not NULL, on exit isValidOut will indicate if + * integer code is valid. + * @return + * Enum for integer code. + */ +PaletteHistogramRangeModeEnum::Enum +PaletteHistogramRangeModeEnum::fromIntegerCode(const int32_t integerCode, bool* isValidOut) +{ + if (initializedFlag == false) initialize(); + + bool validFlag = false; + Enum enumValue = PaletteHistogramRangeModeEnum::enumData[0].enumValue; + + for (std::vector::iterator iter = enumData.begin(); + iter != enumData.end(); + iter++) { + const PaletteHistogramRangeModeEnum& enumInstance = *iter; + if (enumInstance.integerCode == integerCode) { + enumValue = enumInstance.enumValue; + validFlag = true; + break; + } + } + + if (isValidOut != 0) { + *isValidOut = validFlag; + } + else if (validFlag == false) { + CaretAssertMessage(0, AString("Integer code " + AString::number(integerCode) + "failed to match enumerated value for type PaletteHistogramRangeModeEnum")); + } + return enumValue; +} + +/** + * Get all of the enumerated type values. The values can be used + * as parameters to toXXX() methods to get associated metadata. + * + * @param allEnums + * A vector that is OUTPUT containing all of the enumerated values. + */ +void +PaletteHistogramRangeModeEnum::getAllEnums(std::vector& allEnums) +{ + if (initializedFlag == false) initialize(); + + allEnums.clear(); + + for (std::vector::iterator iter = enumData.begin(); + iter != enumData.end(); + iter++) { + allEnums.push_back(iter->enumValue); + } +} + +/** + * Get all of the names of the enumerated type values. + * + * @param allNames + * A vector that is OUTPUT containing all of the names of the enumerated values. + * @param isSorted + * If true, the names are sorted in alphabetical order. + */ +void +PaletteHistogramRangeModeEnum::getAllNames(std::vector& allNames, const bool isSorted) +{ + if (initializedFlag == false) initialize(); + + allNames.clear(); + + for (std::vector::iterator iter = enumData.begin(); + iter != enumData.end(); + iter++) { + allNames.push_back(PaletteHistogramRangeModeEnum::toName(iter->enumValue)); + } + + if (isSorted) { + std::sort(allNames.begin(), allNames.end()); + } +} + +/** + * Get all of the GUI names of the enumerated type values. + * + * @param allNames + * A vector that is OUTPUT containing all of the GUI names of the enumerated values. + * @param isSorted + * If true, the names are sorted in alphabetical order. + */ +void +PaletteHistogramRangeModeEnum::getAllGuiNames(std::vector& allGuiNames, const bool isSorted) +{ + if (initializedFlag == false) initialize(); + + allGuiNames.clear(); + + for (std::vector::iterator iter = enumData.begin(); + iter != enumData.end(); + iter++) { + allGuiNames.push_back(PaletteHistogramRangeModeEnum::toGuiName(iter->enumValue)); + } + + if (isSorted) { + std::sort(allGuiNames.begin(), allGuiNames.end()); + } +} + diff --git a/src/Palette/PaletteHistogramRangeModeEnum.h b/src/Palette/PaletteHistogramRangeModeEnum.h new file mode 100644 index 0000000000000000000000000000000000000000..5566dfeac76ec5140f11119f7e154c2086b3dc10 --- /dev/null +++ b/src/Palette/PaletteHistogramRangeModeEnum.h @@ -0,0 +1,104 @@ +#ifndef __PALETTE_HISTOGRAM_RANGE_MODE_ENUM_H__ +#define __PALETTE_HISTOGRAM_RANGE_MODE_ENUM_H__ + +/*LICENSE_START*/ +/* + * Copyright (C) 2017 Washington University School of Medicine + * + * 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 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ +/*LICENSE_END*/ + + +#include +#include +#include "AString.h" + +namespace caret { + +class PaletteHistogramRangeModeEnum { + +public: + /** + * Enumerated values. + */ + enum Enum { + /** Use all data for histogram plot */ + PALETTE_HISTOGRAM_RANGE_ALL, + /** Use data matching palette selections */ + PALETTE_HISTOGRAM_RANGE_MATCH_PALETTE + }; + + + ~PaletteHistogramRangeModeEnum(); + + static AString toName(Enum enumValue); + + static Enum fromName(const AString& name, bool* isValidOut); + + static AString toGuiName(Enum enumValue); + + static Enum fromGuiName(const AString& guiName, bool* isValidOut); + + static int32_t toIntegerCode(Enum enumValue); + + static Enum fromIntegerCode(const int32_t integerCode, bool* isValidOut); + + static void getAllEnums(std::vector& allEnums); + + static void getAllNames(std::vector& allNames, const bool isSorted); + + static void getAllGuiNames(std::vector& allGuiNames, const bool isSorted); + +private: + PaletteHistogramRangeModeEnum(const Enum enumValue, + const AString& name, + const AString& guiName); + + static const PaletteHistogramRangeModeEnum* findData(const Enum enumValue); + + /** Holds all instance of enum values and associated metadata */ + static std::vector enumData; + + /** Initialize instances that contain the enum values and metadata */ + static void initialize(); + + /** Indicates instance of enum values and metadata have been initialized */ + static bool initializedFlag; + + /** Auto generated integer codes */ + static int32_t integerCodeCounter; + + /** The enumerated type value for an instance */ + Enum enumValue; + + /** The integer code associated with an enumerated value */ + int32_t integerCode; + + /** The name, a text string that is identical to the enumerated value */ + AString name; + + /** A user-friendly name that is displayed in the GUI */ + AString guiName; +}; + +#ifdef __PALETTE_HISTOGRAM_RANGE_MODE_ENUM_DECLARE__ +std::vector PaletteHistogramRangeModeEnum::enumData; +bool PaletteHistogramRangeModeEnum::initializedFlag = false; +int32_t PaletteHistogramRangeModeEnum::integerCodeCounter = 0; +#endif // __PALETTE_HISTOGRAM_RANGE_MODE_ENUM_DECLARE__ + +} // namespace +#endif //__PALETTE_HISTOGRAM_RANGE_MODE_ENUM_H__ diff --git a/src/Palette/PaletteInvertModeEnum.cxx b/src/Palette/PaletteInvertModeEnum.cxx new file mode 100644 index 0000000000000000000000000000000000000000..11b09e5ca1bf73f8e81fed61123a9d930096c75f --- /dev/null +++ b/src/Palette/PaletteInvertModeEnum.cxx @@ -0,0 +1,388 @@ + +/*LICENSE_START*/ +/* + * Copyright (C) 2018 Washington University School of Medicine + * + * 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 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ +/*LICENSE_END*/ + +#include +#define __PALETTE_INVERT_MODE_ENUM_DECLARE__ +#include "PaletteInvertModeEnum.h" +#undef __PALETTE_INVERT_MODE_ENUM_DECLARE__ + +#include "CaretAssert.h" + +using namespace caret; + + +/** + * \class caret::PaletteInvertModeEnum + * \brief Inversion mode for palettes. + * + * Using this enumerated type in the GUI with an EnumComboBoxTemplate + * + * Header File (.h) + * Forward declare the data type: + * class EnumComboBoxTemplate; + * + * Declare the member: + * EnumComboBoxTemplate* m_paletteInvertModeEnumComboBox; + * + * Declare a slot that is called when user changes selection + * private slots: + * void paletteInvertModeEnumComboBoxItemActivated(); + * + * Implementation File (.cxx) + * Include the header files + * #include "EnumComboBoxTemplate.h" + * #include "PaletteInvertModeEnum.h" + * + * Instatiate: + * m_paletteInvertModeEnumComboBox = new EnumComboBoxTemplate(this); + * m_paletteInvertModeEnumComboBox->setup(); + * + * Get notified when the user changes the selection: + * QObject::connect(m_paletteInvertModeEnumComboBox, SIGNAL(itemActivated()), + * this, SLOT(paletteInvertModeEnumComboBoxItemActivated())); + * + * Update the selection: + * m_paletteInvertModeEnumComboBox->setSelectedItem(NEW_VALUE); + * + * Read the selection: + * const PaletteInvertModeEnum::Enum VARIABLE = m_paletteInvertModeEnumComboBox->getSelectedItem(); + * + */ + +/** + * Constructor. + * + * @param enumValue + * An enumerated value. + * @param name + * Name of enumerated value. + * + * @param guiName + * User-friendly name for use in user-interface. + */ +PaletteInvertModeEnum::PaletteInvertModeEnum(const Enum enumValue, + const AString& name, + const AString& guiName) +{ + this->enumValue = enumValue; + this->integerCode = integerCodeCounter++; + this->name = name; + this->guiName = guiName; +} + +/** + * Destructor. + */ +PaletteInvertModeEnum::~PaletteInvertModeEnum() +{ +} + +/** + * Initialize the enumerated metadata. + */ +void +PaletteInvertModeEnum::initialize() +{ + if (initializedFlag) { + return; + } + initializedFlag = true; + + enumData.push_back(PaletteInvertModeEnum(OFF, + "OFF", + "Off")); + + enumData.push_back(PaletteInvertModeEnum(POSITIVE_WITH_NEGATIVE, + "POSITIVE_WITH_NEGATIVE", + "Swap Pos with Neg")); + + enumData.push_back(PaletteInvertModeEnum(POSITIVE_NEGATIVE_SEPARATE, + "POSITIVE_NEGATIVE_SEPARATE", + "Swap Pos/Neg Separate")); +} + +/** + * Find the data for and enumerated value. + * @param enumValue + * The enumerated value. + * @return Pointer to data for this enumerated type + * or NULL if no data for type or if type is invalid. + */ +const PaletteInvertModeEnum* +PaletteInvertModeEnum::findData(const Enum enumValue) +{ + if (initializedFlag == false) initialize(); + + size_t num = enumData.size(); + for (size_t i = 0; i < num; i++) { + const PaletteInvertModeEnum* d = &enumData[i]; + if (d->enumValue == enumValue) { + return d; + } + } + + return NULL; +} + +/** + * Get a string representation of the enumerated type. + * @param enumValue + * Enumerated value. + * @return + * String representing enumerated value. + */ +AString +PaletteInvertModeEnum::toName(Enum enumValue) { + if (initializedFlag == false) initialize(); + + const PaletteInvertModeEnum* enumInstance = findData(enumValue); + return enumInstance->name; +} + +/** + * Get an enumerated value corresponding to its name. + * @param name + * Name of enumerated value. + * @param isValidOut + * If not NULL, it is set indicating that a + * enum value exists for the input name. + * @return + * Enumerated value. + */ +PaletteInvertModeEnum::Enum +PaletteInvertModeEnum::fromName(const AString& nameIn, bool* isValidOut) +{ + if (initializedFlag == false) initialize(); + + /* + * Fix misspelled name that may be in some files + * and test for obsolete "NONE separate" + */ + AString name = nameIn; + if (name == "POSITIVE_NEGATIVE_SEPARATE NONE") { + name = "POSITIVE_NEGATIVE_SEPARATE_NONE"; + } + if (name == "POSITIVE_NEGATIVE_SEPARATE_NONE") { + name = "POSITIVE_NEGATIVE_SEPARATE"; + } + + bool validFlag = false; + Enum enumValue = PaletteInvertModeEnum::enumData[0].enumValue; + + for (std::vector::iterator iter = enumData.begin(); + iter != enumData.end(); + iter++) { + const PaletteInvertModeEnum& d = *iter; + if (d.name == name) { + enumValue = d.enumValue; + validFlag = true; + break; + } + } + + if (isValidOut != 0) { + *isValidOut = validFlag; + } + else if (validFlag == false) { + CaretAssertMessage(0, AString("Name " + name + "failed to match enumerated value for type PaletteInvertModeEnum")); + } + return enumValue; +} + +/** + * Get a GUI string representation of the enumerated type. + * @param enumValue + * Enumerated value. + * @return + * String representing enumerated value. + */ +AString +PaletteInvertModeEnum::toGuiName(Enum enumValue) { + if (initializedFlag == false) initialize(); + + const PaletteInvertModeEnum* enumInstance = findData(enumValue); + return enumInstance->guiName; +} + +/** + * Get an enumerated value corresponding to its GUI name. + * @param s + * Name of enumerated value. + * @param isValidOut + * If not NULL, it is set indicating that a + * enum value exists for the input name. + * @return + * Enumerated value. + */ +PaletteInvertModeEnum::Enum +PaletteInvertModeEnum::fromGuiName(const AString& guiName, bool* isValidOut) +{ + if (initializedFlag == false) initialize(); + + bool validFlag = false; + Enum enumValue = PaletteInvertModeEnum::enumData[0].enumValue; + + for (std::vector::iterator iter = enumData.begin(); + iter != enumData.end(); + iter++) { + const PaletteInvertModeEnum& d = *iter; + if (d.guiName == guiName) { + enumValue = d.enumValue; + validFlag = true; + break; + } + } + + if (isValidOut != 0) { + *isValidOut = validFlag; + } + else if (validFlag == false) { + CaretAssertMessage(0, AString("guiName " + guiName + "failed to match enumerated value for type PaletteInvertModeEnum")); + } + return enumValue; +} + +/** + * Get the integer code for a data type. + * + * @return + * Integer code for data type. + */ +int32_t +PaletteInvertModeEnum::toIntegerCode(Enum enumValue) +{ + if (initializedFlag == false) initialize(); + const PaletteInvertModeEnum* enumInstance = findData(enumValue); + return enumInstance->integerCode; +} + +/** + * Find the data type corresponding to an integer code. + * + * @param integerCode + * Integer code for enum. + * @param isValidOut + * If not NULL, on exit isValidOut will indicate if + * integer code is valid. + * @return + * Enum for integer code. + */ +PaletteInvertModeEnum::Enum +PaletteInvertModeEnum::fromIntegerCode(const int32_t integerCode, bool* isValidOut) +{ + if (initializedFlag == false) initialize(); + + bool validFlag = false; + Enum enumValue = PaletteInvertModeEnum::enumData[0].enumValue; + + for (std::vector::iterator iter = enumData.begin(); + iter != enumData.end(); + iter++) { + const PaletteInvertModeEnum& enumInstance = *iter; + if (enumInstance.integerCode == integerCode) { + enumValue = enumInstance.enumValue; + validFlag = true; + break; + } + } + + if (isValidOut != 0) { + *isValidOut = validFlag; + } + else if (validFlag == false) { + CaretAssertMessage(0, AString("Integer code " + AString::number(integerCode) + "failed to match enumerated value for type PaletteInvertModeEnum")); + } + return enumValue; +} + +/** + * Get all of the enumerated type values. The values can be used + * as parameters to toXXX() methods to get associated metadata. + * + * @param allEnums + * A vector that is OUTPUT containing all of the enumerated values. + */ +void +PaletteInvertModeEnum::getAllEnums(std::vector& allEnums) +{ + if (initializedFlag == false) initialize(); + + allEnums.clear(); + + for (std::vector::iterator iter = enumData.begin(); + iter != enumData.end(); + iter++) { + allEnums.push_back(iter->enumValue); + } +} + +/** + * Get all of the names of the enumerated type values. + * + * @param allNames + * A vector that is OUTPUT containing all of the names of the enumerated values. + * @param isSorted + * If true, the names are sorted in alphabetical order. + */ +void +PaletteInvertModeEnum::getAllNames(std::vector& allNames, const bool isSorted) +{ + if (initializedFlag == false) initialize(); + + allNames.clear(); + + for (std::vector::iterator iter = enumData.begin(); + iter != enumData.end(); + iter++) { + allNames.push_back(PaletteInvertModeEnum::toName(iter->enumValue)); + } + + if (isSorted) { + std::sort(allNames.begin(), allNames.end()); + } +} + +/** + * Get all of the GUI names of the enumerated type values. + * + * @param allNames + * A vector that is OUTPUT containing all of the GUI names of the enumerated values. + * @param isSorted + * If true, the names are sorted in alphabetical order. + */ +void +PaletteInvertModeEnum::getAllGuiNames(std::vector& allGuiNames, const bool isSorted) +{ + if (initializedFlag == false) initialize(); + + allGuiNames.clear(); + + for (std::vector::iterator iter = enumData.begin(); + iter != enumData.end(); + iter++) { + allGuiNames.push_back(PaletteInvertModeEnum::toGuiName(iter->enumValue)); + } + + if (isSorted) { + std::sort(allGuiNames.begin(), allGuiNames.end()); + } +} + diff --git a/src/Palette/PaletteInvertModeEnum.h b/src/Palette/PaletteInvertModeEnum.h new file mode 100644 index 0000000000000000000000000000000000000000..2854efddfbbae67d9b89de056d143c366a607c62 --- /dev/null +++ b/src/Palette/PaletteInvertModeEnum.h @@ -0,0 +1,106 @@ +#ifndef __PALETTE_INVERT_MODE_ENUM_H__ +#define __PALETTE_INVERT_MODE_ENUM_H__ + +/*LICENSE_START*/ +/* + * Copyright (C) 2018 Washington University School of Medicine + * + * 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 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ +/*LICENSE_END*/ + + +#include +#include +#include "AString.h" + +namespace caret { + +class PaletteInvertModeEnum { + +public: + /** + * Enumerated values. + */ + enum Enum { + /** Off */ + OFF, + /** Invert entire palette flipping colors and sign of scalars */ + POSITIVE_WITH_NEGATIVE, + /** Invert within negative separately within positive */ + POSITIVE_NEGATIVE_SEPARATE + }; + + + ~PaletteInvertModeEnum(); + + static AString toName(Enum enumValue); + + static Enum fromName(const AString& name, bool* isValidOut); + + static AString toGuiName(Enum enumValue); + + static Enum fromGuiName(const AString& guiName, bool* isValidOut); + + static int32_t toIntegerCode(Enum enumValue); + + static Enum fromIntegerCode(const int32_t integerCode, bool* isValidOut); + + static void getAllEnums(std::vector& allEnums); + + static void getAllNames(std::vector& allNames, const bool isSorted); + + static void getAllGuiNames(std::vector& allGuiNames, const bool isSorted); + +private: + PaletteInvertModeEnum(const Enum enumValue, + const AString& name, + const AString& guiName); + + static const PaletteInvertModeEnum* findData(const Enum enumValue); + + /** Holds all instance of enum values and associated metadata */ + static std::vector enumData; + + /** Initialize instances that contain the enum values and metadata */ + static void initialize(); + + /** Indicates instance of enum values and metadata have been initialized */ + static bool initializedFlag; + + /** Auto generated integer codes */ + static int32_t integerCodeCounter; + + /** The enumerated type value for an instance */ + Enum enumValue; + + /** The integer code associated with an enumerated value */ + int32_t integerCode; + + /** The name, a text string that is identical to the enumerated value */ + AString name; + + /** A user-friendly name that is displayed in the GUI */ + AString guiName; +}; + +#ifdef __PALETTE_INVERT_MODE_ENUM_DECLARE__ +std::vector PaletteInvertModeEnum::enumData; +bool PaletteInvertModeEnum::initializedFlag = false; +int32_t PaletteInvertModeEnum::integerCodeCounter = 0; +#endif // __PALETTE_INVERT_MODE_ENUM_DECLARE__ + +} // namespace +#endif //__PALETTE_INVERT_MODE_ENUM_H__ diff --git a/src/Palette/PaletteModifiedStatusEnum.cxx b/src/Palette/PaletteModifiedStatusEnum.cxx new file mode 100644 index 0000000000000000000000000000000000000000..ad975bfeb236d323a1a5c9f9297d7e9a584aa000 --- /dev/null +++ b/src/Palette/PaletteModifiedStatusEnum.cxx @@ -0,0 +1,377 @@ + +/*LICENSE_START*/ +/* + * Copyright (C) 2018 Washington University School of Medicine + * + * 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 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ +/*LICENSE_END*/ + +#include +#define __PALETTE_MODIFIED_STATUS_ENUM_DECLARE__ +#include "PaletteModifiedStatusEnum.h" +#undef __PALETTE_MODIFIED_STATUS_ENUM_DECLARE__ + +#include "CaretAssert.h" + +using namespace caret; + + +/** + * \class caret::PaletteModifiedStatusEnum + * \brief Enumerated type for palette modified status + * + * Using this enumerated type in the GUI with an EnumComboBoxTemplate + * + * Header File (.h) + * Forward declare the data type: + * class EnumComboBoxTemplate; + * + * Declare the member: + * EnumComboBoxTemplate* m_paletteModifiedStatusEnumComboBox; + * + * Declare a slot that is called when user changes selection + * private slots: + * void paletteModifiedStatusEnumComboBoxItemActivated(); + * + * Implementation File (.cxx) + * Include the header files + * #include "EnumComboBoxTemplate.h" + * #include "PaletteModifiedStatusEnum.h" + * + * Instatiate: + * m_paletteModifiedStatusEnumComboBox = new EnumComboBoxTemplate(this); + * m_paletteModifiedStatusEnumComboBox->setup(); + * + * Get notified when the user changes the selection: + * QObject::connect(m_paletteModifiedStatusEnumComboBox, SIGNAL(itemActivated()), + * this, SLOT(paletteModifiedStatusEnumComboBoxItemActivated())); + * + * Update the selection: + * m_paletteModifiedStatusEnumComboBox->setSelectedItem(NEW_VALUE); + * + * Read the selection: + * const PaletteModifiedStatusEnum::Enum VARIABLE = m_paletteModifiedStatusEnumComboBox->getSelectedItem(); + * + */ + +/** + * Constructor. + * + * @param enumValue + * An enumerated value. + * @param name + * Name of enumerated value. + * + * @param guiName + * User-friendly name for use in user-interface. + */ +PaletteModifiedStatusEnum::PaletteModifiedStatusEnum(const Enum enumValue, + const AString& name, + const AString& guiName) +{ + this->enumValue = enumValue; + this->integerCode = integerCodeCounter++; + this->name = name; + this->guiName = guiName; +} + +/** + * Destructor. + */ +PaletteModifiedStatusEnum::~PaletteModifiedStatusEnum() +{ +} + +/** + * Initialize the enumerated metadata. + */ +void +PaletteModifiedStatusEnum::initialize() +{ + if (initializedFlag) { + return; + } + initializedFlag = true; + + enumData.push_back(PaletteModifiedStatusEnum(MODIFIED, + "MODIFIED", + "Modified")); + + enumData.push_back(PaletteModifiedStatusEnum(MODIFIED_BY_SHOW_SCENE, + "MODIFIED_BY_SHOW_SCENE", + "Modified By Show Scene")); + + enumData.push_back(PaletteModifiedStatusEnum(UNMODIFIED, + "UNMODIFIED", + "Unmodified")); + +} + +/** + * Find the data for and enumerated value. + * @param enumValue + * The enumerated value. + * @return Pointer to data for this enumerated type + * or NULL if no data for type or if type is invalid. + */ +const PaletteModifiedStatusEnum* +PaletteModifiedStatusEnum::findData(const Enum enumValue) +{ + if (initializedFlag == false) initialize(); + + size_t num = enumData.size(); + for (size_t i = 0; i < num; i++) { + const PaletteModifiedStatusEnum* d = &enumData[i]; + if (d->enumValue == enumValue) { + return d; + } + } + + return NULL; +} + +/** + * Get a string representation of the enumerated type. + * @param enumValue + * Enumerated value. + * @return + * String representing enumerated value. + */ +AString +PaletteModifiedStatusEnum::toName(Enum enumValue) { + if (initializedFlag == false) initialize(); + + const PaletteModifiedStatusEnum* enumInstance = findData(enumValue); + return enumInstance->name; +} + +/** + * Get an enumerated value corresponding to its name. + * @param name + * Name of enumerated value. + * @param isValidOut + * If not NULL, it is set indicating that a + * enum value exists for the input name. + * @return + * Enumerated value. + */ +PaletteModifiedStatusEnum::Enum +PaletteModifiedStatusEnum::fromName(const AString& name, bool* isValidOut) +{ + if (initializedFlag == false) initialize(); + + bool validFlag = false; + Enum enumValue = PaletteModifiedStatusEnum::enumData[0].enumValue; + + for (std::vector::iterator iter = enumData.begin(); + iter != enumData.end(); + iter++) { + const PaletteModifiedStatusEnum& d = *iter; + if (d.name == name) { + enumValue = d.enumValue; + validFlag = true; + break; + } + } + + if (isValidOut != 0) { + *isValidOut = validFlag; + } + else if (validFlag == false) { + CaretAssertMessage(0, AString("Name " + name + "failed to match enumerated value for type PaletteModifiedStatusEnum")); + } + return enumValue; +} + +/** + * Get a GUI string representation of the enumerated type. + * @param enumValue + * Enumerated value. + * @return + * String representing enumerated value. + */ +AString +PaletteModifiedStatusEnum::toGuiName(Enum enumValue) { + if (initializedFlag == false) initialize(); + + const PaletteModifiedStatusEnum* enumInstance = findData(enumValue); + return enumInstance->guiName; +} + +/** + * Get an enumerated value corresponding to its GUI name. + * @param s + * Name of enumerated value. + * @param isValidOut + * If not NULL, it is set indicating that a + * enum value exists for the input name. + * @return + * Enumerated value. + */ +PaletteModifiedStatusEnum::Enum +PaletteModifiedStatusEnum::fromGuiName(const AString& guiName, bool* isValidOut) +{ + if (initializedFlag == false) initialize(); + + bool validFlag = false; + Enum enumValue = PaletteModifiedStatusEnum::enumData[0].enumValue; + + for (std::vector::iterator iter = enumData.begin(); + iter != enumData.end(); + iter++) { + const PaletteModifiedStatusEnum& d = *iter; + if (d.guiName == guiName) { + enumValue = d.enumValue; + validFlag = true; + break; + } + } + + if (isValidOut != 0) { + *isValidOut = validFlag; + } + else if (validFlag == false) { + CaretAssertMessage(0, AString("guiName " + guiName + "failed to match enumerated value for type PaletteModifiedStatusEnum")); + } + return enumValue; +} + +/** + * Get the integer code for a data type. + * + * @return + * Integer code for data type. + */ +int32_t +PaletteModifiedStatusEnum::toIntegerCode(Enum enumValue) +{ + if (initializedFlag == false) initialize(); + const PaletteModifiedStatusEnum* enumInstance = findData(enumValue); + return enumInstance->integerCode; +} + +/** + * Find the data type corresponding to an integer code. + * + * @param integerCode + * Integer code for enum. + * @param isValidOut + * If not NULL, on exit isValidOut will indicate if + * integer code is valid. + * @return + * Enum for integer code. + */ +PaletteModifiedStatusEnum::Enum +PaletteModifiedStatusEnum::fromIntegerCode(const int32_t integerCode, bool* isValidOut) +{ + if (initializedFlag == false) initialize(); + + bool validFlag = false; + Enum enumValue = PaletteModifiedStatusEnum::enumData[0].enumValue; + + for (std::vector::iterator iter = enumData.begin(); + iter != enumData.end(); + iter++) { + const PaletteModifiedStatusEnum& enumInstance = *iter; + if (enumInstance.integerCode == integerCode) { + enumValue = enumInstance.enumValue; + validFlag = true; + break; + } + } + + if (isValidOut != 0) { + *isValidOut = validFlag; + } + else if (validFlag == false) { + CaretAssertMessage(0, AString("Integer code " + AString::number(integerCode) + "failed to match enumerated value for type PaletteModifiedStatusEnum")); + } + return enumValue; +} + +/** + * Get all of the enumerated type values. The values can be used + * as parameters to toXXX() methods to get associated metadata. + * + * @param allEnums + * A vector that is OUTPUT containing all of the enumerated values. + */ +void +PaletteModifiedStatusEnum::getAllEnums(std::vector& allEnums) +{ + if (initializedFlag == false) initialize(); + + allEnums.clear(); + + for (std::vector::iterator iter = enumData.begin(); + iter != enumData.end(); + iter++) { + allEnums.push_back(iter->enumValue); + } +} + +/** + * Get all of the names of the enumerated type values. + * + * @param allNames + * A vector that is OUTPUT containing all of the names of the enumerated values. + * @param isSorted + * If true, the names are sorted in alphabetical order. + */ +void +PaletteModifiedStatusEnum::getAllNames(std::vector& allNames, const bool isSorted) +{ + if (initializedFlag == false) initialize(); + + allNames.clear(); + + for (std::vector::iterator iter = enumData.begin(); + iter != enumData.end(); + iter++) { + allNames.push_back(PaletteModifiedStatusEnum::toName(iter->enumValue)); + } + + if (isSorted) { + std::sort(allNames.begin(), allNames.end()); + } +} + +/** + * Get all of the GUI names of the enumerated type values. + * + * @param allNames + * A vector that is OUTPUT containing all of the GUI names of the enumerated values. + * @param isSorted + * If true, the names are sorted in alphabetical order. + */ +void +PaletteModifiedStatusEnum::getAllGuiNames(std::vector& allGuiNames, const bool isSorted) +{ + if (initializedFlag == false) initialize(); + + allGuiNames.clear(); + + for (std::vector::iterator iter = enumData.begin(); + iter != enumData.end(); + iter++) { + allGuiNames.push_back(PaletteModifiedStatusEnum::toGuiName(iter->enumValue)); + } + + if (isSorted) { + std::sort(allGuiNames.begin(), allGuiNames.end()); + } +} + diff --git a/src/Palette/PaletteModifiedStatusEnum.h b/src/Palette/PaletteModifiedStatusEnum.h new file mode 100644 index 0000000000000000000000000000000000000000..b02c1d22c2d6db2cad572c3b822f7885604bc698 --- /dev/null +++ b/src/Palette/PaletteModifiedStatusEnum.h @@ -0,0 +1,106 @@ +#ifndef __PALETTE_MODIFIED_STATUS_ENUM_H__ +#define __PALETTE_MODIFIED_STATUS_ENUM_H__ + +/*LICENSE_START*/ +/* + * Copyright (C) 2018 Washington University School of Medicine + * + * 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 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ +/*LICENSE_END*/ + + +#include +#include +#include "AString.h" + +namespace caret { + +class PaletteModifiedStatusEnum { + +public: + /** + * Enumerated values. + */ + enum Enum { + /** Modified */ + MODIFIED, + /** Modified by showing a scene */ + MODIFIED_BY_SHOW_SCENE, + /** Unmodified */ + UNMODIFIED + }; + + + ~PaletteModifiedStatusEnum(); + + static AString toName(Enum enumValue); + + static Enum fromName(const AString& name, bool* isValidOut); + + static AString toGuiName(Enum enumValue); + + static Enum fromGuiName(const AString& guiName, bool* isValidOut); + + static int32_t toIntegerCode(Enum enumValue); + + static Enum fromIntegerCode(const int32_t integerCode, bool* isValidOut); + + static void getAllEnums(std::vector& allEnums); + + static void getAllNames(std::vector& allNames, const bool isSorted); + + static void getAllGuiNames(std::vector& allGuiNames, const bool isSorted); + +private: + PaletteModifiedStatusEnum(const Enum enumValue, + const AString& name, + const AString& guiName); + + static const PaletteModifiedStatusEnum* findData(const Enum enumValue); + + /** Holds all instance of enum values and associated metadata */ + static std::vector enumData; + + /** Initialize instances that contain the enum values and metadata */ + static void initialize(); + + /** Indicates instance of enum values and metadata have been initialized */ + static bool initializedFlag; + + /** Auto generated integer codes */ + static int32_t integerCodeCounter; + + /** The enumerated type value for an instance */ + Enum enumValue; + + /** The integer code associated with an enumerated value */ + int32_t integerCode; + + /** The name, a text string that is identical to the enumerated value */ + AString name; + + /** A user-friendly name that is displayed in the GUI */ + AString guiName; +}; + +#ifdef __PALETTE_MODIFIED_STATUS_ENUM_DECLARE__ +std::vector PaletteModifiedStatusEnum::enumData; +bool PaletteModifiedStatusEnum::initializedFlag = false; +int32_t PaletteModifiedStatusEnum::integerCodeCounter = 0; +#endif // __PALETTE_MODIFIED_STATUS_ENUM_DECLARE__ + +} // namespace +#endif //__PALETTE_MODIFIED_STATUS_ENUM_H__ diff --git a/src/Quazip/CMakeLists.txt b/src/Quazip/CMakeLists.txt index f34f26958367a5bb9f81ec6cba4b8ba48a5d597b..e457f71eb8179cdd913500c4036f75f4bb7b325e 100644 --- a/src/Quazip/CMakeLists.txt +++ b/src/Quazip/CMakeLists.txt @@ -3,18 +3,33 @@ # PROJECT(Quazip) +IF(Qt5_FOUND) + include_directories(${Qt5Core_INCLUDE_DIRS}) +ENDIF() +IF (QT4_FOUND) + SET(QT_DONT_USE_QTGUI TRUE) + INCLUDE(${QT_USE_FILE}) +ENDIF () + # -# QT include files +# With AUTOMOC, do not need to specify files +# that contain Q_OBJECT macro for Qt to process with 'moc' +# (meta-object compiler). # -SET(QT_DONT_USE_QTGUI TRUE) - -INCLUDE(${QT_USE_FILE}) - -SET(MOC_INPUT_HEADER_FILES -quazipfile.h -) +IF(WORKBENCH_USE_CMAKE_AUTOMOC) + SET(CMAKE_AUTOMOC ON) +ELSE() + SET(MOC_INPUT_HEADER_FILES + quazipfile.h + ) -QT4_WRAP_CPP(MOC_SOURCE_FILES ${MOC_INPUT_HEADER_FILES}) + IF(Qt5_FOUND) + QT5_WRAP_CPP(MOC_SOURCE_FILES ${MOC_INPUT_HEADER_FILES}) + ENDIF() + IF (QT4_FOUND) + QT4_WRAP_CPP(MOC_SOURCE_FILES ${MOC_INPUT_HEADER_FILES}) + ENDIF () +ENDIF() # # Prevents dll linkage errors on windows @@ -67,5 +82,7 @@ unzip.c zip.c ) +TARGET_LINK_LIBRARIES(Quazip ${CARET_QT5_LINK}) + SET(QUAZIP_LIBRARIES Quazip PARENT_SCOPE) SET(QUAZIP_INCLUDE_DIRS ${CMAKE_CURRENT_SOURCE_DIR} PARENT_SCOPE) diff --git a/src/Quazip/quazipdir.cpp b/src/Quazip/quazipdir.cpp index d75075e274473b20959fb086bec86aa3c8eb9161..ba215da8034fd2868ecb16d449c9fe8dd38581ee 100644 --- a/src/Quazip/quazipdir.cpp +++ b/src/Quazip/quazipdir.cpp @@ -1,3 +1,4 @@ +#include #include "quazipdir.h" #include @@ -356,7 +357,11 @@ bool QuaZipDirPrivate::entryInfoList(QStringList nameFilters, == Qt::CaseInsensitive) srt |= QDir::IgnoreCase; QuaZipDirComparator lessThan(srt); +#ifdef WORKBENCH_REPLACE_QT_DEPRECATED + std::sort(list.begin(), list.end(), lessThan); +#else qSort(list.begin(), list.end(), lessThan); +#endif } QuaZipDir_convertInfoList(list, result); return true; diff --git a/src/Qwt/CMakeLists.txt b/src/Qwt/CMakeLists.txt index bfee9124857c59466973bd73fcaa2bab7e5383b0..2d0ec18c28ce23fbe102f05d7f996527d2075eb3 100644 --- a/src/Qwt/CMakeLists.txt +++ b/src/Qwt/CMakeLists.txt @@ -15,44 +15,56 @@ PROJECT (Qwt) # # QT include files # -INCLUDE(${QT_USE_FILE}) +if(Qt5_FOUND) + include_directories(${Qt5Concurrent_INCLUDE_DIRS}) + include_directories(${Qt5Core_INCLUDE_DIRS}) + include_directories(${Qt5Widgets_INCLUDE_DIRS}) +endif() +IF (QT4_FOUND) + INCLUDE(${QT_USE_FILE}) +ENDIF () add_definitions(-DQWT_NO_SVG -DQWT_NO_OPENGL) # -# Headers that must be processed with QT's "moc" -# Any class that derives from a QT class and contains -# the "Q_OBJECT" macro must be listed here. +# With AUTOMOC, do not need to specify files +# that contain Q_OBJECT macro for Qt to process with 'moc' +# (meta-object compiler). # -# Also LIST each of these headers in the -# source files section so that they show -# up in development tools such as XCode. -# -SET(MOC_INPUT_HEADER_FILES -qwt_abstract_legend.h -qwt_dyngrid_layout.h -qwt_legend.h -qwt_legend_label.h -qwt_magnifier.h -qwt_panner.h -qwt_picker.h -qwt_plot_canvas.h -qwt_plot.h -qwt_plot_magnifier.h -qwt_plot_panner.h -qwt_plot_picker.h -qwt_plot_renderer.h -qwt_plot_zoomer.h -qwt_sampling_thread.h -qwt_scale_widget.h -qwt_text_label.h -) +IF(WORKBENCH_USE_CMAKE_AUTOMOC) + SET(CMAKE_AUTOMOC ON) +ELSE() + SET(MOC_INPUT_HEADER_FILES + qwt_abstract_legend.h + qwt_dyngrid_layout.h + qwt_legend.h + qwt_legend_label.h + qwt_magnifier.h + qwt_panner.h + qwt_picker.h + qwt_plot_canvas.h + qwt_plot.h + qwt_plot_magnifier.h + qwt_plot_panner.h + qwt_plot_picker.h + qwt_plot_renderer.h + qwt_plot_zoomer.h + qwt_sampling_thread.h + qwt_scale_widget.h + qwt_text_label.h + ) + + IF(Qt5_FOUND) + QT5_WRAP_CPP(MOC_SOURCE_FILES ${MOC_INPUT_HEADER_FILES}) + ENDIF() + IF (QT4_FOUND) + QT4_WRAP_CPP(MOC_SOURCE_FILES ${MOC_INPUT_HEADER_FILES}) + ENDIF () +ENDIF() + -# Header files # -# If the header file is qt 'moc' file -# also list it in the section above -# named MOC_INPUT_HEADER_FILES +# Header files # SET(SOURCE_FILES qwt_abstract_legend.h @@ -225,15 +237,16 @@ qwt_widget_overlay.cpp # Process the header files with moc producing moc_*.cpp files # INCLUDE_DIRECTORIES(${CMAKE_CURRENT_BINARY_DIR}) -QT4_WRAP_CPP(MOC_SOURCE_FILES ${MOC_INPUT_HEADER_FILES}) # # Create the GUI library # ADD_LIBRARY(Qwt -${SOURCE_FILES} -${MOC_SOURCE_FILES} + ${SOURCE_FILES} + ${MOC_SOURCE_FILES} ) +TARGET_LINK_LIBRARIES(Qwt ${CARET_QT5_LINK}) + SET(Qwt_LIBRARIES Qwt PARENT_SCOPE) SET(Qwt_INCLUDE_DIRS ${CMAKE_CURRENT_SOURCE_DIR} PARENT_SCOPE) diff --git a/src/Qwt/qwt_clipper.cpp b/src/Qwt/qwt_clipper.cpp index 51614aa37f1c24f4e42478bb2e9eacf3794b3cb3..c826e7fb4b367b58a8b793a895fea5671e70670f 100644 --- a/src/Qwt/qwt_clipper.cpp +++ b/src/Qwt/qwt_clipper.cpp @@ -7,6 +7,7 @@ * modify it under the terms of the Qwt License, Version 1.0 *****************************************************************************/ +#include #include "qwt_clipper.h" #include "qwt_point_polar.h" #include @@ -351,7 +352,11 @@ QVector QwtCircleClipper::clipCircle( QList angles; for ( int i = 0; i < points.size(); i++ ) angles += toAngle( pos, points[i] ); +#ifdef WORKBENCH_REPLACE_QT_DEPRECATED + std::sort(angles.begin(), angles.end()); +#else qSort( angles ); +#endif const int in = d_rect.contains( qwtPolar2Pos( pos, radius, angles[0] + ( angles[1] - angles[0] ) / 2 ) ); diff --git a/src/Qwt/qwt_date.cpp b/src/Qwt/qwt_date.cpp index fc3ac4b2da4bcfd3bbcc45ffc92c1ea8042391a4..3bb0f8526c43b17846826bd49aeb8f0e2544a878 100644 --- a/src/Qwt/qwt_date.cpp +++ b/src/Qwt/qwt_date.cpp @@ -322,7 +322,7 @@ QDateTime QwtDate::ceil( const QDateTime &dateTime, IntervalType intervalType ) { qwtFloorTime( QwtDate::Second, dt ); if ( dt < dateTime ) - dt.addSecs( 1 ); + dt = dt.addSecs( 1 ); break; } @@ -330,7 +330,7 @@ QDateTime QwtDate::ceil( const QDateTime &dateTime, IntervalType intervalType ) { qwtFloorTime( QwtDate::Minute, dt ); if ( dt < dateTime ) - dt.addSecs( 60 ); + dt = dt.addSecs( 60 ); break; } @@ -338,7 +338,7 @@ QDateTime QwtDate::ceil( const QDateTime &dateTime, IntervalType intervalType ) { qwtFloorTime( QwtDate::Hour, dt ); if ( dt < dateTime ) - dt.addSecs( 3600 ); + dt = dt.addSecs( 3600 ); break; } @@ -371,7 +371,7 @@ QDateTime QwtDate::ceil( const QDateTime &dateTime, IntervalType intervalType ) dateTime.date().month() ) ); if ( dt < dateTime ) - dt.addMonths( 1 ); + dt = dt.addMonths( 1 ); break; } @@ -614,7 +614,11 @@ int QwtDate::utcOffset( const QDateTime &dateTime ) } case Qt::OffsetFromUTC: { +#ifdef WORKBENCH_REPLACE_QT_DEPRECATED + seconds = dateTime.offsetFromUtc(); +#else seconds = dateTime.utcOffset(); +#endif } default: { diff --git a/src/Qwt/qwt_date_scale_draw.cpp b/src/Qwt/qwt_date_scale_draw.cpp index cebde7b30a31c1b0dccd965801cd1a7b7dabad2b..4ef95e632fa3f118d9269240c56c5f8ff5615dff 100644 --- a/src/Qwt/qwt_date_scale_draw.cpp +++ b/src/Qwt/qwt_date_scale_draw.cpp @@ -271,7 +271,11 @@ QDateTime QwtDateScaleDraw::toDateTime( double value ) const if ( d_data->timeSpec == Qt::OffsetFromUTC ) { dt = dt.addSecs( d_data->utcOffset ); +#ifdef WORKBENCH_REPLACE_QT_DEPRECATED + dt.setOffsetFromUtc( d_data->utcOffset ); +#else dt.setUtcOffset( d_data->utcOffset ); +#endif } return dt; diff --git a/src/Qwt/qwt_date_scale_engine.cpp b/src/Qwt/qwt_date_scale_engine.cpp index 0c368a94c6bdcd581f487b357d907ec60a7f7a94..c16534c00faca23f786db568b677cb0b93004a13 100644 --- a/src/Qwt/qwt_date_scale_engine.cpp +++ b/src/Qwt/qwt_date_scale_engine.cpp @@ -1117,7 +1117,12 @@ QDateTime QwtDateScaleEngine::alignDate( if ( dateTime.timeSpec() == Qt::OffsetFromUTC ) { + dt = dt.addSecs( d_data->utcOffset ); +#ifdef WORKBENCH_REPLACE_QT_DEPRECATED + dt.setOffsetFromUtc( 0 ); +#else dt.setUtcOffset( 0 ); +#endif } switch( intervalType ) @@ -1275,7 +1280,12 @@ QDateTime QwtDateScaleEngine::alignDate( if ( dateTime.timeSpec() == Qt::OffsetFromUTC ) { + dt = dt.addSecs( d_data->utcOffset ); +#ifdef WORKBENCH_REPLACE_QT_DEPRECATED + dt.setOffsetFromUtc( dateTime.offsetFromUtc() ); +#else dt.setUtcOffset( dateTime.utcOffset() ); +#endif } return dt; @@ -1303,7 +1313,12 @@ QDateTime QwtDateScaleEngine::toDateTime( double value ) const if ( d_data->timeSpec == Qt::OffsetFromUTC ) { dt = dt.addSecs( d_data->utcOffset ); + dt = dt.addSecs( d_data->utcOffset ); +#ifdef WORKBENCH_REPLACE_QT_DEPRECATED + dt.setOffsetFromUtc( d_data->utcOffset ); +#else dt.setUtcOffset( d_data->utcOffset ); +#endif } return dt; diff --git a/src/Qwt/qwt_plot_canvas.cpp b/src/Qwt/qwt_plot_canvas.cpp index 0271713a8b50c44478dfcc6335d513e49c37e818..a1dc6e5076a8937fecdab912530fa020154a8261 100644 --- a/src/Qwt/qwt_plot_canvas.cpp +++ b/src/Qwt/qwt_plot_canvas.cpp @@ -937,7 +937,8 @@ void QwtPlotCanvas::drawBorder( QPainter *painter ) else { #if QT_VERSION >= 0x040500 - QStyleOptionFrameV3 opt; + //QStyleOptionFrameV3 opt; + QStyleOptionFrame opt; // Note QStyleOptionFrameV3 is typedef to QStyleOptionFrame opt.init(this); int frameShape = frameStyle() & QFrame::Shape_Mask; diff --git a/src/Qwt/qwt_plot_dict.cpp b/src/Qwt/qwt_plot_dict.cpp index 17c61ed4793e1c7dd117e77a35f1afc647394163..4e2e08a77a077a2da36c19b7ac1df59f9ab5c8f3 100644 --- a/src/Qwt/qwt_plot_dict.cpp +++ b/src/Qwt/qwt_plot_dict.cpp @@ -7,6 +7,7 @@ * modify it under the terms of the Qwt License, Version 1.0 *****************************************************************************/ +#include #include "qwt_plot_dict.h" class QwtPlotDict::PrivateData @@ -22,7 +23,11 @@ public: return; QList::iterator it = - qUpperBound( begin(), end(), item, LessZThan() ); +#ifdef WORKBENCH_REPLACE_QT_DEPRECATED + std::upper_bound( begin(), end(), item, LessZThan() ); +#else + qUpperBound( begin(), end(), item, LessZThan() ); +#endif insert( it, item ); } @@ -32,7 +37,11 @@ public: return; QList::iterator it = - qLowerBound( begin(), end(), item, LessZThan() ); +#ifdef WORKBENCH_REPLACE_QT_DEPRECATED + std::lower_bound( begin(), end(), item, LessZThan() ); +#else + qLowerBound( begin(), end(), item, LessZThan() ); +#endif for ( ; it != end(); ++it ) { diff --git a/src/Qwt/qwt_plot_renderer.cpp b/src/Qwt/qwt_plot_renderer.cpp index 3416f98062b59651ac11f544dda2a55c3360e783..b7bfb01cb65c54afc59093113999d42ff253b1c9 100644 --- a/src/Qwt/qwt_plot_renderer.cpp +++ b/src/Qwt/qwt_plot_renderer.cpp @@ -20,8 +20,13 @@ #include #include #include +#if QT_VERSION >= 0x050000 +#include +#include +#else // QT_VERSION #include #include +#endif // QT_VERSION #include #include #include diff --git a/src/Qwt/qwt_plot_spectrogram.cpp b/src/Qwt/qwt_plot_spectrogram.cpp index e7a9fecc79f6d9a9f6f4a07c7f9f07fa29f5fff0..bab844c75635e8b67083e8374f3dd98e01324a0b 100644 --- a/src/Qwt/qwt_plot_spectrogram.cpp +++ b/src/Qwt/qwt_plot_spectrogram.cpp @@ -7,6 +7,8 @@ * modify it under the terms of the Qwt License, Version 1.0 *****************************************************************************/ +#include + #include "qwt_plot_spectrogram.h" #include "qwt_painter.h" #include "qwt_interval.h" @@ -286,8 +288,12 @@ bool QwtPlotSpectrogram::testConrecFlag( void QwtPlotSpectrogram::setContourLevels( const QList &levels ) { d_data->contourLevels = levels; +#ifdef WORKBENCH_REPLACE_QT_DEPRECATED + std::sort(d_data->contourLevels.begin(), d_data->contourLevels.end()); +#else qSort( d_data->contourLevels ); - +#endif + legendChanged(); itemChanged(); } diff --git a/src/Resources/GeneralResources/Fonts/FtglFonts/VeraBd.ttf b/src/Resources/General/Fonts/FtglFonts/VeraBd.ttf similarity index 100% rename from src/Resources/GeneralResources/Fonts/FtglFonts/VeraBd.ttf rename to src/Resources/General/Fonts/FtglFonts/VeraBd.ttf diff --git a/src/Resources/GeneralResources/Fonts/FtglFonts/VeraSe.ttf b/src/Resources/General/Fonts/FtglFonts/VeraSe.ttf similarity index 100% rename from src/Resources/GeneralResources/Fonts/FtglFonts/VeraSe.ttf rename to src/Resources/General/Fonts/FtglFonts/VeraSe.ttf diff --git a/src/Resources/GeneralResources/Fonts/LinuxLibertine/GPL.txt b/src/Resources/General/Fonts/LinuxLibertine/GPL.txt similarity index 100% rename from src/Resources/GeneralResources/Fonts/LinuxLibertine/GPL.txt rename to src/Resources/General/Fonts/LinuxLibertine/GPL.txt diff --git a/src/Resources/GeneralResources/Fonts/LinuxLibertine/LICENCE.txt b/src/Resources/General/Fonts/LinuxLibertine/LICENCE.txt similarity index 100% rename from src/Resources/GeneralResources/Fonts/LinuxLibertine/LICENCE.txt rename to src/Resources/General/Fonts/LinuxLibertine/LICENCE.txt diff --git a/src/Resources/GeneralResources/Fonts/LinuxLibertine/LinLibertine_RBIah.ttf b/src/Resources/General/Fonts/LinuxLibertine/LinLibertine_RBIah.ttf similarity index 100% rename from src/Resources/GeneralResources/Fonts/LinuxLibertine/LinLibertine_RBIah.ttf rename to src/Resources/General/Fonts/LinuxLibertine/LinLibertine_RBIah.ttf diff --git a/src/Resources/GeneralResources/Fonts/LinuxLibertine/LinLibertine_RBah.ttf b/src/Resources/General/Fonts/LinuxLibertine/LinLibertine_RBah.ttf similarity index 100% rename from src/Resources/GeneralResources/Fonts/LinuxLibertine/LinLibertine_RBah.ttf rename to src/Resources/General/Fonts/LinuxLibertine/LinLibertine_RBah.ttf diff --git a/src/Resources/GeneralResources/Fonts/LinuxLibertine/LinLibertine_RIah.ttf b/src/Resources/General/Fonts/LinuxLibertine/LinLibertine_RIah.ttf similarity index 100% rename from src/Resources/GeneralResources/Fonts/LinuxLibertine/LinLibertine_RIah.ttf rename to src/Resources/General/Fonts/LinuxLibertine/LinLibertine_RIah.ttf diff --git a/src/Resources/GeneralResources/Fonts/LinuxLibertine/LinLibertine_Rah.ttf b/src/Resources/General/Fonts/LinuxLibertine/LinLibertine_Rah.ttf similarity index 100% rename from src/Resources/GeneralResources/Fonts/LinuxLibertine/LinLibertine_Rah.ttf rename to src/Resources/General/Fonts/LinuxLibertine/LinLibertine_Rah.ttf diff --git a/src/Resources/GeneralResources/Fonts/LinuxLibertine/OFL-1.1.txt b/src/Resources/General/Fonts/LinuxLibertine/OFL-1.1.txt similarity index 100% rename from src/Resources/GeneralResources/Fonts/LinuxLibertine/OFL-1.1.txt rename to src/Resources/General/Fonts/LinuxLibertine/OFL-1.1.txt diff --git a/src/Resources/GeneralResources/Fonts/LinuxLibertine/README b/src/Resources/General/Fonts/LinuxLibertine/README similarity index 100% rename from src/Resources/GeneralResources/Fonts/LinuxLibertine/README rename to src/Resources/General/Fonts/LinuxLibertine/README diff --git a/src/Resources/GeneralResources/Fonts/VeraFonts/COPYRIGHT.TXT b/src/Resources/General/Fonts/VeraFonts/COPYRIGHT.TXT similarity index 100% rename from src/Resources/GeneralResources/Fonts/VeraFonts/COPYRIGHT.TXT rename to src/Resources/General/Fonts/VeraFonts/COPYRIGHT.TXT diff --git a/src/Resources/GeneralResources/Fonts/VeraFonts/README.TXT b/src/Resources/General/Fonts/VeraFonts/README.TXT similarity index 100% rename from src/Resources/GeneralResources/Fonts/VeraFonts/README.TXT rename to src/Resources/General/Fonts/VeraFonts/README.TXT diff --git a/src/Resources/GeneralResources/Fonts/VeraFonts/RELEASENOTES.TXT b/src/Resources/General/Fonts/VeraFonts/RELEASENOTES.TXT similarity index 100% rename from src/Resources/GeneralResources/Fonts/VeraFonts/RELEASENOTES.TXT rename to src/Resources/General/Fonts/VeraFonts/RELEASENOTES.TXT diff --git a/src/Resources/GeneralResources/Fonts/VeraFonts/Vera.ttf b/src/Resources/General/Fonts/VeraFonts/Vera.ttf similarity index 100% rename from src/Resources/GeneralResources/Fonts/VeraFonts/Vera.ttf rename to src/Resources/General/Fonts/VeraFonts/Vera.ttf diff --git a/src/Resources/GeneralResources/Fonts/VeraFonts/VeraBI.ttf b/src/Resources/General/Fonts/VeraFonts/VeraBI.ttf similarity index 100% rename from src/Resources/GeneralResources/Fonts/VeraFonts/VeraBI.ttf rename to src/Resources/General/Fonts/VeraFonts/VeraBI.ttf diff --git a/src/Resources/GeneralResources/Fonts/VeraFonts/VeraBd.ttf b/src/Resources/General/Fonts/VeraFonts/VeraBd.ttf similarity index 100% rename from src/Resources/GeneralResources/Fonts/VeraFonts/VeraBd.ttf rename to src/Resources/General/Fonts/VeraFonts/VeraBd.ttf diff --git a/src/Resources/GeneralResources/Fonts/VeraFonts/VeraIt.ttf b/src/Resources/General/Fonts/VeraFonts/VeraIt.ttf similarity index 100% rename from src/Resources/GeneralResources/Fonts/VeraFonts/VeraIt.ttf rename to src/Resources/General/Fonts/VeraFonts/VeraIt.ttf diff --git a/src/Resources/GeneralResources/Fonts/VeraFonts/VeraMoBI.ttf b/src/Resources/General/Fonts/VeraFonts/VeraMoBI.ttf similarity index 100% rename from src/Resources/GeneralResources/Fonts/VeraFonts/VeraMoBI.ttf rename to src/Resources/General/Fonts/VeraFonts/VeraMoBI.ttf diff --git a/src/Resources/GeneralResources/Fonts/VeraFonts/VeraMoBd.ttf b/src/Resources/General/Fonts/VeraFonts/VeraMoBd.ttf similarity index 100% rename from src/Resources/GeneralResources/Fonts/VeraFonts/VeraMoBd.ttf rename to src/Resources/General/Fonts/VeraFonts/VeraMoBd.ttf diff --git a/src/Resources/GeneralResources/Fonts/VeraFonts/VeraMoIt.ttf b/src/Resources/General/Fonts/VeraFonts/VeraMoIt.ttf similarity index 100% rename from src/Resources/GeneralResources/Fonts/VeraFonts/VeraMoIt.ttf rename to src/Resources/General/Fonts/VeraFonts/VeraMoIt.ttf diff --git a/src/Resources/GeneralResources/Fonts/VeraFonts/VeraMono.ttf b/src/Resources/General/Fonts/VeraFonts/VeraMono.ttf similarity index 100% rename from src/Resources/GeneralResources/Fonts/VeraFonts/VeraMono.ttf rename to src/Resources/General/Fonts/VeraFonts/VeraMono.ttf diff --git a/src/Resources/GeneralResources/Fonts/VeraFonts/VeraSe.ttf b/src/Resources/General/Fonts/VeraFonts/VeraSe.ttf similarity index 100% rename from src/Resources/GeneralResources/Fonts/VeraFonts/VeraSe.ttf rename to src/Resources/General/Fonts/VeraFonts/VeraSe.ttf diff --git a/src/Resources/GeneralResources/Fonts/VeraFonts/VeraSeBd.ttf b/src/Resources/General/Fonts/VeraFonts/VeraSeBd.ttf similarity index 100% rename from src/Resources/GeneralResources/Fonts/VeraFonts/VeraSeBd.ttf rename to src/Resources/General/Fonts/VeraFonts/VeraSeBd.ttf diff --git a/src/Resources/GeneralResources/Fonts/VeraFonts/local.conf b/src/Resources/General/Fonts/VeraFonts/local.conf similarity index 100% rename from src/Resources/GeneralResources/Fonts/VeraFonts/local.conf rename to src/Resources/General/Fonts/VeraFonts/local.conf diff --git a/src/Resources/General/general_resources.qrc b/src/Resources/General/general_resources.qrc new file mode 100644 index 0000000000000000000000000000000000000000..470df32a7e5ac6e4fafff1e7f5f33904f20de9d9 --- /dev/null +++ b/src/Resources/General/general_resources.qrc @@ -0,0 +1,29 @@ + + +./Fonts/FtglFonts/VeraBd.ttf +./Fonts/FtglFonts/VeraSe.ttf +./Fonts/LinuxLibertine/GPL.txt +./Fonts/LinuxLibertine/LICENCE.txt +./Fonts/LinuxLibertine/LinLibertine_Rah.ttf +./Fonts/LinuxLibertine/LinLibertine_RBah.ttf +./Fonts/LinuxLibertine/LinLibertine_RBIah.ttf +./Fonts/LinuxLibertine/LinLibertine_RIah.ttf +./Fonts/LinuxLibertine/OFL-1.1.txt +./Fonts/LinuxLibertine/README +./Fonts/VeraFonts/COPYRIGHT.TXT +./Fonts/VeraFonts/local.conf +./Fonts/VeraFonts/README.TXT +./Fonts/VeraFonts/RELEASENOTES.TXT +./Fonts/VeraFonts/Vera.ttf +./Fonts/VeraFonts/VeraBd.ttf +./Fonts/VeraFonts/VeraBI.ttf +./Fonts/VeraFonts/VeraIt.ttf +./Fonts/VeraFonts/VeraMoBd.ttf +./Fonts/VeraFonts/VeraMoBI.ttf +./Fonts/VeraFonts/VeraMoIt.ttf +./Fonts/VeraFonts/VeraMono.ttf +./Fonts/VeraFonts/VeraSe.ttf +./Fonts/VeraFonts/VeraSeBd.ttf +./general_resources.qrc + + diff --git a/src/Resources/GeneralResources/About/hcp-logo.png b/src/Resources/Gui/About/hcp-logo.png similarity index 100% rename from src/Resources/GeneralResources/About/hcp-logo.png rename to src/Resources/Gui/About/hcp-logo.png diff --git a/src/Resources/Gui/BalsaUploadDialog/lock-closed.png b/src/Resources/Gui/BalsaUploadDialog/lock-closed.png new file mode 100644 index 0000000000000000000000000000000000000000..2b86ab2bd5b597ffbce6f2b4c3777f616aa22e45 Binary files /dev/null and b/src/Resources/Gui/BalsaUploadDialog/lock-closed.png differ diff --git a/src/Resources/Gui/BalsaUploadDialog/lock-open.png b/src/Resources/Gui/BalsaUploadDialog/lock-open.png new file mode 100644 index 0000000000000000000000000000000000000000..6442f8f7e989ba1c4f469cc7caf982984aef2c9c Binary files /dev/null and b/src/Resources/Gui/BalsaUploadDialog/lock-open.png differ diff --git a/src/Resources/GeneralResources/Cursor/pen_eraser.png b/src/Resources/Gui/Cursor/pen_eraser.png similarity index 100% rename from src/Resources/GeneralResources/Cursor/pen_eraser.png rename to src/Resources/Gui/Cursor/pen_eraser.png diff --git a/src/Resources/GeneralResources/LayersPanel/colorbar.png b/src/Resources/Gui/LayersPanel/colorbar.png similarity index 100% rename from src/Resources/GeneralResources/LayersPanel/colorbar.png rename to src/Resources/Gui/LayersPanel/colorbar.png diff --git a/src/Resources/GeneralResources/LayersPanel/construction.png b/src/Resources/Gui/LayersPanel/construction.png similarity index 100% rename from src/Resources/GeneralResources/LayersPanel/construction.png rename to src/Resources/Gui/LayersPanel/construction.png diff --git a/src/Resources/GeneralResources/LayersPanel/wrench.png b/src/Resources/Gui/LayersPanel/wrench.png similarity index 100% rename from src/Resources/GeneralResources/LayersPanel/wrench.png rename to src/Resources/Gui/LayersPanel/wrench.png diff --git a/src/Resources/GeneralResources/PaletteSettings/chain_link_icon.png b/src/Resources/Gui/PaletteSettings/chain_link_icon.png similarity index 100% rename from src/Resources/GeneralResources/PaletteSettings/chain_link_icon.png rename to src/Resources/Gui/PaletteSettings/chain_link_icon.png diff --git a/src/Resources/Gui/SceneFileDialog/caution.png b/src/Resources/Gui/SceneFileDialog/caution.png new file mode 100644 index 0000000000000000000000000000000000000000..8d77405385039f4d7c41b29a33fb47f378b4326c Binary files /dev/null and b/src/Resources/Gui/SceneFileDialog/caution.png differ diff --git a/src/Resources/GeneralResources/SpecFileDialog/delete_icon.png b/src/Resources/Gui/SpecFileDialog/delete_icon.png similarity index 100% rename from src/Resources/GeneralResources/SpecFileDialog/delete_icon.png rename to src/Resources/Gui/SpecFileDialog/delete_icon.png diff --git a/src/Resources/GeneralResources/SpecFileDialog/load_icon.png b/src/Resources/Gui/SpecFileDialog/load_icon.png similarity index 100% rename from src/Resources/GeneralResources/SpecFileDialog/load_icon.png rename to src/Resources/Gui/SpecFileDialog/load_icon.png diff --git a/src/Resources/GeneralResources/SpecFileDialog/options_icon.png b/src/Resources/Gui/SpecFileDialog/options_icon.png similarity index 100% rename from src/Resources/GeneralResources/SpecFileDialog/options_icon.png rename to src/Resources/Gui/SpecFileDialog/options_icon.png diff --git a/src/Resources/GeneralResources/SpecFileDialog/reload_icon.png b/src/Resources/Gui/SpecFileDialog/reload_icon.png similarity index 100% rename from src/Resources/GeneralResources/SpecFileDialog/reload_icon.png rename to src/Resources/Gui/SpecFileDialog/reload_icon.png diff --git a/src/Resources/GeneralResources/Splash/hcp.png b/src/Resources/Gui/Splash/hcp.png similarity index 100% rename from src/Resources/GeneralResources/Splash/hcp.png rename to src/Resources/Gui/Splash/hcp.png diff --git a/src/Resources/GeneralResources/Splash/startup_image.png b/src/Resources/Gui/Splash/startup_image.png similarity index 100% rename from src/Resources/GeneralResources/Splash/startup_image.png rename to src/Resources/Gui/Splash/startup_image.png diff --git a/src/Resources/GeneralResources/Splash/twitter.png b/src/Resources/Gui/Splash/twitter.png similarity index 100% rename from src/Resources/GeneralResources/Splash/twitter.png rename to src/Resources/Gui/Splash/twitter.png diff --git a/src/Resources/GeneralResources/ToolBar/clapboard.png b/src/Resources/Gui/ToolBar/clapboard.png similarity index 100% rename from src/Resources/GeneralResources/ToolBar/clapboard.png rename to src/Resources/Gui/ToolBar/clapboard.png diff --git a/src/Resources/GeneralResources/ToolBar/features_toolbox.png b/src/Resources/Gui/ToolBar/features_toolbox.png similarity index 100% rename from src/Resources/GeneralResources/ToolBar/features_toolbox.png rename to src/Resources/Gui/ToolBar/features_toolbox.png diff --git a/src/Resources/GeneralResources/ToolBar/help.png b/src/Resources/Gui/ToolBar/help.png similarity index 100% rename from src/Resources/GeneralResources/ToolBar/help.png rename to src/Resources/Gui/ToolBar/help.png diff --git a/src/Resources/GeneralResources/ToolBar/identify.png b/src/Resources/Gui/ToolBar/identify.png similarity index 100% rename from src/Resources/GeneralResources/ToolBar/identify.png rename to src/Resources/Gui/ToolBar/identify.png diff --git a/src/Resources/GeneralResources/ToolBar/info.png b/src/Resources/Gui/ToolBar/info.png similarity index 100% rename from src/Resources/GeneralResources/ToolBar/info.png rename to src/Resources/Gui/ToolBar/info.png diff --git a/src/Resources/GeneralResources/ToolBar/overlay_toolbox.png b/src/Resources/Gui/ToolBar/overlay_toolbox.png similarity index 100% rename from src/Resources/GeneralResources/ToolBar/overlay_toolbox.png rename to src/Resources/Gui/ToolBar/overlay_toolbox.png diff --git a/src/Resources/GeneralResources/ToolBar/toolbar.png b/src/Resources/Gui/ToolBar/toolbar.png similarity index 100% rename from src/Resources/GeneralResources/ToolBar/toolbar.png rename to src/Resources/Gui/ToolBar/toolbar.png diff --git a/src/Resources/GeneralResources/ToolBar/view-anterior.png b/src/Resources/Gui/ToolBar/view-anterior.png similarity index 100% rename from src/Resources/GeneralResources/ToolBar/view-anterior.png rename to src/Resources/Gui/ToolBar/view-anterior.png diff --git a/src/Resources/GeneralResources/ToolBar/view-dorsal.png b/src/Resources/Gui/ToolBar/view-dorsal.png similarity index 100% rename from src/Resources/GeneralResources/ToolBar/view-dorsal.png rename to src/Resources/Gui/ToolBar/view-dorsal.png diff --git a/src/Resources/GeneralResources/ToolBar/view-left-lateral.png b/src/Resources/Gui/ToolBar/view-left-lateral.png similarity index 100% rename from src/Resources/GeneralResources/ToolBar/view-left-lateral.png rename to src/Resources/Gui/ToolBar/view-left-lateral.png diff --git a/src/Resources/GeneralResources/ToolBar/view-left-medial.png b/src/Resources/Gui/ToolBar/view-left-medial.png similarity index 100% rename from src/Resources/GeneralResources/ToolBar/view-left-medial.png rename to src/Resources/Gui/ToolBar/view-left-medial.png diff --git a/src/Resources/GeneralResources/ToolBar/view-left.png b/src/Resources/Gui/ToolBar/view-left.png similarity index 100% rename from src/Resources/GeneralResources/ToolBar/view-left.png rename to src/Resources/Gui/ToolBar/view-left.png diff --git a/src/Resources/GeneralResources/ToolBar/view-plane-axial.png b/src/Resources/Gui/ToolBar/view-plane-axial.png similarity index 100% rename from src/Resources/GeneralResources/ToolBar/view-plane-axial.png rename to src/Resources/Gui/ToolBar/view-plane-axial.png diff --git a/src/Resources/GeneralResources/ToolBar/view-plane-coronal.png b/src/Resources/Gui/ToolBar/view-plane-coronal.png similarity index 100% rename from src/Resources/GeneralResources/ToolBar/view-plane-coronal.png rename to src/Resources/Gui/ToolBar/view-plane-coronal.png diff --git a/src/Resources/GeneralResources/ToolBar/view-plane-parasagittal.png b/src/Resources/Gui/ToolBar/view-plane-parasagittal.png similarity index 100% rename from src/Resources/GeneralResources/ToolBar/view-plane-parasagittal.png rename to src/Resources/Gui/ToolBar/view-plane-parasagittal.png diff --git a/src/Resources/GeneralResources/ToolBar/view-posterior.png b/src/Resources/Gui/ToolBar/view-posterior.png similarity index 100% rename from src/Resources/GeneralResources/ToolBar/view-posterior.png rename to src/Resources/Gui/ToolBar/view-posterior.png diff --git a/src/Resources/GeneralResources/ToolBar/view-right-lateral.png b/src/Resources/Gui/ToolBar/view-right-lateral.png similarity index 100% rename from src/Resources/GeneralResources/ToolBar/view-right-lateral.png rename to src/Resources/Gui/ToolBar/view-right-lateral.png diff --git a/src/Resources/GeneralResources/ToolBar/view-right-medial.png b/src/Resources/Gui/ToolBar/view-right-medial.png similarity index 100% rename from src/Resources/GeneralResources/ToolBar/view-right-medial.png rename to src/Resources/Gui/ToolBar/view-right-medial.png diff --git a/src/Resources/GeneralResources/ToolBar/view-right.png b/src/Resources/Gui/ToolBar/view-right.png similarity index 100% rename from src/Resources/GeneralResources/ToolBar/view-right.png rename to src/Resources/Gui/ToolBar/view-right.png diff --git a/src/Resources/GeneralResources/ToolBar/view-ventral.png b/src/Resources/Gui/ToolBar/view-ventral.png similarity index 100% rename from src/Resources/GeneralResources/ToolBar/view-ventral.png rename to src/Resources/Gui/ToolBar/view-ventral.png diff --git a/src/Resources/GeneralResources/ToolBar/volume-crosshair-pointer.png b/src/Resources/Gui/ToolBar/volume-crosshair-pointer.png similarity index 100% rename from src/Resources/GeneralResources/ToolBar/volume-crosshair-pointer.png rename to src/Resources/Gui/ToolBar/volume-crosshair-pointer.png diff --git a/src/Resources/GeneralResources/general_resources.qrc b/src/Resources/Gui/gui_resources.qrc similarity index 56% rename from src/Resources/GeneralResources/general_resources.qrc rename to src/Resources/Gui/gui_resources.qrc index 4e701940ea730aaec985525a164a025a78502391..c9a45154be84ffc6bfbb57192a9deed99b50adde 100644 --- a/src/Resources/GeneralResources/general_resources.qrc +++ b/src/Resources/Gui/gui_resources.qrc @@ -1,36 +1,14 @@ ./About/hcp-logo.png +./BalsaUploadDialog/lock-closed.png +./BalsaUploadDialog/lock-open.png ./Cursor/pen_eraser.png -./Fonts/FtglFonts/VeraBd.ttf -./Fonts/FtglFonts/VeraSe.ttf -./Fonts/LinuxLibertine/GPL.txt -./Fonts/LinuxLibertine/LICENCE.txt -./Fonts/LinuxLibertine/LinLibertine_Rah.ttf -./Fonts/LinuxLibertine/LinLibertine_RBah.ttf -./Fonts/LinuxLibertine/LinLibertine_RBIah.ttf -./Fonts/LinuxLibertine/LinLibertine_RIah.ttf -./Fonts/LinuxLibertine/OFL-1.1.txt -./Fonts/LinuxLibertine/README -./Fonts/VeraFonts/COPYRIGHT.TXT -./Fonts/VeraFonts/local.conf -./Fonts/VeraFonts/README.TXT -./Fonts/VeraFonts/RELEASENOTES.TXT -./Fonts/VeraFonts/Vera.ttf -./Fonts/VeraFonts/VeraBd.ttf -./Fonts/VeraFonts/VeraBI.ttf -./Fonts/VeraFonts/VeraIt.ttf -./Fonts/VeraFonts/VeraMoBd.ttf -./Fonts/VeraFonts/VeraMoBI.ttf -./Fonts/VeraFonts/VeraMoIt.ttf -./Fonts/VeraFonts/VeraMono.ttf -./Fonts/VeraFonts/VeraSe.ttf -./Fonts/VeraFonts/VeraSeBd.ttf -./general_resources.qrc ./LayersPanel/colorbar.png ./LayersPanel/construction.png ./LayersPanel/wrench.png ./PaletteSettings/chain_link_icon.png +./SceneFileDialog/caution.png ./SpecFileDialog/delete_icon.png ./SpecFileDialog/load_icon.png ./SpecFileDialog/options_icon.png diff --git a/src/Resources/GuiResources/HelpFiles/Annotations/Annotations.fld/image001.png b/src/Resources/GuiResources/HelpFiles/Annotations/Annotations.fld/image001.png deleted file mode 100644 index 332b19cf2b450ce5c6e0f50d3a4d36d260ea8f71..0000000000000000000000000000000000000000 Binary files a/src/Resources/GuiResources/HelpFiles/Annotations/Annotations.fld/image001.png and /dev/null differ diff --git a/src/Resources/GuiResources/HelpFiles/Annotations/Annotations.fld/image002.png b/src/Resources/GuiResources/HelpFiles/Annotations/Annotations.fld/image002.png deleted file mode 100644 index e220ec0757fabff51c28e19340e88949884b5206..0000000000000000000000000000000000000000 Binary files a/src/Resources/GuiResources/HelpFiles/Annotations/Annotations.fld/image002.png and /dev/null differ diff --git a/src/Resources/GuiResources/HelpFiles/Annotations/Annotations.fld/image003.png b/src/Resources/GuiResources/HelpFiles/Annotations/Annotations.fld/image003.png deleted file mode 100644 index b444e647a3473a7ee00e6cd9d961e8f0b6246ce7..0000000000000000000000000000000000000000 Binary files a/src/Resources/GuiResources/HelpFiles/Annotations/Annotations.fld/image003.png and /dev/null differ diff --git a/src/Resources/GuiResources/HelpFiles/Annotations/Annotations.fld/image014.png b/src/Resources/GuiResources/HelpFiles/Annotations/Annotations.fld/image014.png deleted file mode 100644 index b119e9b4e8852205206a4c8ff5620024ca51aad9..0000000000000000000000000000000000000000 Binary files a/src/Resources/GuiResources/HelpFiles/Annotations/Annotations.fld/image014.png and /dev/null differ diff --git a/src/Resources/GuiResources/HelpFiles/Annotations/Annotations.fld/image016.png b/src/Resources/GuiResources/HelpFiles/Annotations/Annotations.fld/image016.png deleted file mode 100644 index e38ecc4071d8b983459b18f9442da1807a07ced8..0000000000000000000000000000000000000000 Binary files a/src/Resources/GuiResources/HelpFiles/Annotations/Annotations.fld/image016.png and /dev/null differ diff --git a/src/Resources/GuiResources/HelpFiles/Annotations/Annotations.fld/image017.png b/src/Resources/GuiResources/HelpFiles/Annotations/Annotations.fld/image017.png deleted file mode 100644 index b7c44898c319a24d78e8d998dda763ee65bacccc..0000000000000000000000000000000000000000 Binary files a/src/Resources/GuiResources/HelpFiles/Annotations/Annotations.fld/image017.png and /dev/null differ diff --git a/src/Resources/GuiResources/HelpFiles/Annotations/Annotations.fld/image019.png b/src/Resources/GuiResources/HelpFiles/Annotations/Annotations.fld/image019.png deleted file mode 100644 index 1e2b419557238abbaddda200ac83274de8982761..0000000000000000000000000000000000000000 Binary files a/src/Resources/GuiResources/HelpFiles/Annotations/Annotations.fld/image019.png and /dev/null differ diff --git a/src/Resources/GuiResources/HelpFiles/Annotations/Annotations.fld/image020.png b/src/Resources/GuiResources/HelpFiles/Annotations/Annotations.fld/image020.png deleted file mode 100644 index 2aac9775269e5ef2ba0c507deb18b87c83d0087c..0000000000000000000000000000000000000000 Binary files a/src/Resources/GuiResources/HelpFiles/Annotations/Annotations.fld/image020.png and /dev/null differ diff --git a/src/Resources/GuiResources/HelpFiles/Annotations/Annotations.fld/image021.png b/src/Resources/GuiResources/HelpFiles/Annotations/Annotations.fld/image021.png deleted file mode 100644 index 60ed74a6b403eedfa8e8bf6051b0f1aba818cf2d..0000000000000000000000000000000000000000 Binary files a/src/Resources/GuiResources/HelpFiles/Annotations/Annotations.fld/image021.png and /dev/null differ diff --git a/src/Resources/GuiResources/HelpFiles/Annotations/Annotations.fld/image022.png b/src/Resources/GuiResources/HelpFiles/Annotations/Annotations.fld/image022.png deleted file mode 100644 index 904df7a977c73e6ea17b0c5e26df79821dddfd3b..0000000000000000000000000000000000000000 Binary files a/src/Resources/GuiResources/HelpFiles/Annotations/Annotations.fld/image022.png and /dev/null differ diff --git a/src/Resources/GuiResources/HelpFiles/Annotations/Annotations.html b/src/Resources/GuiResources/HelpFiles/Annotations/Annotations.html deleted file mode 100644 index 483012f3c001e47999349785376b33c5b5c18c8f..0000000000000000000000000000000000000000 --- a/src/Resources/GuiResources/HelpFiles/Annotations/Annotations.html +++ /dev/null @@ -1,2081 +0,0 @@ - - - - - - - - - - - - - -

    - -

    Guide to WB Annotations

    - -

     

    - -

    04 May 2016

    - -

     

    - -

    - -
    -
    - -

     

    - -

    Table of Contents

    - -

    Annotation -Toolbar and Example Annotations. 4

    - -

    Annotation -Coordinate Spaces. 5

    - -

    Stereotaxic -Coordinate Space. 5

    - -

    Surface -Coordinate Space. 5

    - -

    Tab -Coordinate Space. 6

    - -

    Window -Coordinate Space. 6

    - -

    Types of -Annotations. 7

    - -

    Box. 7

    - -

    Color Bars. 7

    - -

    Image. 7

    - -

    Line. 7

    - -

    Oval 7

    - -

    Text. 8

    - -

    Annotation -Properties. 8

    - -

    Annotation -Line Width and Color. 8

    - -

    Annotation -Fill Color. 9

    - -

    Annotation -Line Arrow Tips. 9

    - -

    Annotation -Text Characters. 10

    - -

    Annotation -Text Font Property. 10

    - -

    Annotation -Text Alignment Property. 10

    - -

    Annotation -Text Orientation.. 11

    - -

    Annotation -Coordinate Property. 11

    - -

    Annotation -Width and Height. 12

    - -

    Annotation -Rotation Property. 12

    - -

    Annotation -Files. 13

    - -

    Disk -Annotation File. 13

    - -

    Scene -Annotation File. 13

    - -

    Selecting -Annotations. 13

    - -

    Editing -Annotation Position, Size, and Rotation. 14

    - -

    Editing -(Redo and Undo). 15

    - -

    Inserting -Annotations. 15

    - -

    Deleting -Annotations. 17

    - -

    Format 17

    - -

    Context -Sensitive (Pop-up) Menu. 19

    - -

    Edit Menu. 20

    - -

    Display -Control of Annotations. 21

    - -

    Options. 21

    - -

    Selection of -Annotations in Features Toolbox. 22

    - -

    Content of -Annotations Tab in Features Toolbox. 22

    - -

    Annotation -Grouping. 23

    - -

    Mouse -Selection of Annotations. 23

    - -

    Creating a -User Group.. 23

    - -

    Removing a -User Group.. 24

    - -

    Recreating a -User Group.. 24

    - -

    Color Bar -Editing. 24

    - -

    Location and -Positioning. 25

    - -

    Data -Numerics. 26

    - -

     

    - -
    -
    - -

     

    - -

     

    - -

    Annotation Toolbar and Example Annotations

    - -

     

    - -

     
    -

    - -

     

    - -

    Annotation Coordinate Spaces

    - -

     

    - -

    Several coordinate spaces are -available for annotations.  The coordinate spaces control the movement of -placement of annotations.

    - -

     

    - -

    Stereotaxic Coordinate Space

    - -

     

    - -

    An annotation in stereotaxic -space will appear in any display of a surface or a volume.  A particular -annotation in stereotaxic space may appear in more than one location depending -upon the models being viewed.

    - -

     

    - -

    Stereotaxic space is the -three-dimensional coordinate system in which surface and volume models are -displayed.  In Workbench, the stereotaxic space is in an LPI orientation (negative -X is Left, negative Y is Posterior, and negative Z is Inferior).  Annotations -in Stereotaxic Space will move as the viewed model is panned, rotated, or -zoomed.  At times, annotations in Stereotaxic Space may not be visible -when they are behind a model.  Annotations in Stereotaxic Space are always -drawn in a plane parallel to the screen.

    - -

     

    - -

    Note that in a surface -montage, text annotations in stereotaxic space are sized using the height of -the tab, not the sub-region containing the surface.

    - -

     

    - -

    Surface Coordinate Space

    - -

     

    - -

    An annotation in surface -coordinate space will appear in any view of the surface to which the annotation -is attached.  When a surface space is used, the annotation is associated -with surface by the combination of a structure identifier, a vertex index, the -number of vertices in the surface, and a surface offset.  When an -annotation is drawn in Surface Coordinate Space, the annotation will maintain -its position relative to its surface vertex as the surface is panned, rotated, -or zoomed.  The surface offset allows one to move the annotation away from -the surface along the vertexÕs normal vector or a vector that originates and -the surface center and points to the surface vertex.  At times, -annotations in Surface Coordinate Space may not be visible when they are behind -a model.  Annotations in Surface Space are always drawn in a plane -parallel to the screen.  Width and Height properties are normalized to the -tab containing the Annotation in Surface Space. 

    - -

     

    - -

    Two notes about annotations in -Surface Space.  First, when inserting a surface annotation, the new -annotation may move away from its location and the reason is explained in the -section for creating annotations.  Second in a surface montage, text -annotations in surface space are sized using the height of the tab not the -sub-region containing just the surface.

    - -

     

    - -

     

    - -

    Tab Coordinate Space

    - -

     

    - -

    An annotation in tab -coordinate space is displayed in one tab region and never more than once.  -Tab Coordinate Space is a two-dimensional space specified with the X- and -Y-coordinates that are a normalized within the tab.  The Z-coordinate can -be used to stack annotations (control the overlap of annotations).  A -normalized coordinate ranges from zero to one.  For the X-coordinate, zero -is at the left side of the tabÕs drawing region and one is at the right side of -the tabÕs drawing region.  For the Y-coordinate, zero is at the bottom of -the tabÕs drawing region and one is at the top of the tabÕs drawing -region.  Since the coordinate is ÒnormalizedÓ, the annotation will -maintain its relative position as the tabÕs region changes in size.  In -addition to the X- and Y-coordinate, the Tab Coordinate Space also contains a -tab number that indicates in which tab the annotation is drawn.  Thus, -annotations in Tab Coordinate Space are confined to one tab and may not span -into another tab.  Tab annotations cannot be placed in a tabÕs -margin.  If one desires annotations in a tabÕs margin, create the -annotation in Window Space.

    - -

     

    - -

    Window Coordinate Space

    - -

     

    - -

    The window coordinate space -is very similar to the Tab Coordinate Space except that the normalized -coordinate is for the window (graphics region).  In addition to the X- and -Y-coordinate, the Window Coordinate Space also contains a window number that -indicates in which window the annotation is drawn.

    - -

     

    - -

    Types of Annotations

    - -

     

    - -

    Box

    - -

     

    - -

    A box annotation is -positioned with one coordinate located at the center of the box.  The -boxÕs width and height are ÔnormalizedÕ relative to the size of with and height -of the tab or window containing the box.  Thus a size of one will fill the -tab/window horizontally.  Boxes support all Coordinate Spaces.  Boxes -are colored with both a Line and/or a Fill color.

    - -

     

    - -

    Color Bars

    - -

     

    - -

    Color bars are a special case -of annotations that pictorially describe the mapping of numeric data into a -color palette.  Color bars are not created like other annotation types but -are enabled for display by pressing the color bar button for a row in the -Overlay ToolBoxÕs Layers tab.  Positioning of color bars is performed -automatically unless the user chooses to manually position them.  Editing -of color bars is described at the end of this document.

    - -

     

    - -

    Image

    - -

     

    - -

    An image annotation is -positioned with one coordinate located at the center of the image.  The -height of image is specified as a percentage of the region containing the -image.  As the region changes in height, a corresponding change will occur -in the height of the image.  The width of the image will scale with the -height of the image using the aspect ratio of the image when it was associated -with the orientation.

    - -

     

    - -

    Line

    - -

     

    - -

    A line annotation contains -two coordinates with one located at each end point.  Both coordinates are -always in the same Coordinate Space.  Support all coordinate spaces.  -Lines are colored using the Line Color.  Arrows may be added to each of -the end points.

    - -

     

    - -

    Oval

    - -

     

    - -

    With the exception of the its -shape, an oval is identical to a box.

    - -

     

    - -

    Text

    - -

     

    - -

    Text annotations contain one -coordinate and the text alignment properties control the location of the text -relative to the coordinate.  Text annotations support all Coordinate -Spaces.  Text characters are drawn in the Line Color.  If a Fill -Color is valid (not None), a rectangle is drawn behind the text in the Fill -Color.  The height of text is specified as a percentage of the region -containing the text.  As the region changes in height, a corresponding -change will occur in the size of the text.

    - -

     

    - -

    Annotation Properties

    - -

     

    - -

     

    - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
    -

    Property

    -
    -

    Box

    -
    -

    Color Bar

    -
    -

    Image

    -
    -

    Line

    -
    -

    Oval

    -
    -

    Text

    -
    -

    Arrow Tips

    -
    -

     

    -
    -

     

    -
    -

     

    -
    -

    Y

    -
    -

     

    -
    -

     

    -
    -

    Line Width

    -
    -

    Y

    -
    -

     

    -
    -

     

    -
    -

    Y

    -
    -

    Y

    -
    -

    Y

    -
    -

    Line Color

    -
    -

    Y

    -
    -

     

    -
    -

     

    -
    -

    Y

    -
    -

    Y

    -
    -

    Y

    -
    -

    Fill Color

    -
    -

    Y

    -
    -

    Y

    -
    -

     

    -
    -

     

    -
    -

    Y

    -
    -

    Y

    -
    -

    Text Color

    -
    -

     

    -
    -

    Y

    -
    -

     

    -
    -

     

    -
    -

     

    -
    -

    Y

    -
    -

    Text Characters

    -
    -

     

    -
    -

    Y

    -
    -

     

    -
    -

     

    -
    -

     

    -
    -

    Y

    -
    -

    Font Name

    -
    -

     

    -
    -

    Y

    -
    -

     

    -
    -

     

    -
    -

     

    -
    -

    Y

    -
    -

    Font Size

    -
    -

     

    -
    -

    Y

    -
    -

     

    -
    -

     

    -
    -

     

    -
    -

    Y

    -
    -

    Font Style

    -
    -

     

    -
    -

     

    -
    -

     

    -
    -

     

    -
    -

     

    -
    -

    Y

    -
    -

    Horizontal Alignment

    -
    -

     

    -
    -

     

    -
    -

     

    -
    -

     

    -
    -

     

    -
    -

    Y

    -
    -

    Vertical Alignment

    -
    -

     

    -
    -

     

    -
    -

     

    -
    -

     

    -
    -

     

    -
    -

    Y

    -
    -

    Orientation

    -
    -

     

    -
    -

     

    -
    -

     

    -
    -

     

    -
    -

     

    -
    -

    Y

    -
    -

    Width

    -
    -

    Y

    -
    -

    Y

    -
    -

    Y

    -
    -

     

    -
    -

    Y

    -
    -

     

    -
    -

    Height

    -
    -

    Y

    -
    -

    Y

    -
    -

    Y

    -
    -

     

    -
    -

    Y

    -
    -

     

    -
    -

    Rotation

    -
    -

    Y

    -
    -

    Y

    -
    -

    Y

    -
    -

    Y

    -
    -

    Y

    -
    -

    Y

    -
    - -

     

    - -

     

    - -

    Annotation Line Width and Color

    - -

     

    - -

    The Line Width Property controls -the line width of Line annotations and the outline width of Box and Oval -annotations.  The maximum line width may vary on different -computers.  When multiple annotations are selected with different line -widths, the smallest selected line width is displayed followed by a plus symbol -(+).

    - -

     

    - -

    The Line Color Property -controls the outline color of a box or oval.  It is also used to draw a -box around text annotations.  To change the color, one presses the small -arrow to display a menu from which a named color or a special option is -selected.  The two special selections are Custom and None.  Choosing -Custom displays a dialog that allow the user to create a color by adjusting red, -green, and blue color components.  None inhibits the drawing of a lineÕs -or the box or ovalÕs outline.

    - -

     

    - -

    Annotation Fill Color

    - -

     

    - -

    The Fill Color Property -controls the color of a text background or the fill color for a box or -oval.  Like the Line Color Property there are both Custom and None -options.

    - -

     

    - -

    - -

     

    - -

    Annotation Line Arrow Tips

    - -

     

    - -

    - -

     

    - -

    The Annotation Line Arrow -Tips section allows the user to add arrow to tips to either or both end points -of a a line annotation.  The top arrow button adds an arrow to the lineÕs -start and the bottom arrow button adds an arrow to the lineÕs end.

    - -

    Annotation Text Characters

    - -

     

    - -

    The Text Edit Group allows -the user to edit the properties of the selected Text Annotation (when one and -only one annotation is selected).  Multiple lines of text are separated by -a Ò\nÓ character sequence.  One may double-click the text to edit the text -in a multi-line editor. In the second row is a combo box allows the user to -connect a text annotation in Surface Space to the surface vertex with a line or -an arrow.  When connecting a text annotation to the surface, the offset in -the surface coordinate will need adjustment.

    - -

     

    - -

    - -

     

    - -

    Annotation Text Font Property

    - -

     

    - -

    Font Properties associated -with Text Annotations include the Font Name, the Font Size, the Text Color and -options for Bold, Italic, and Underline.  When multiple annotations are -selected, the first matching font and smallest font size is selected.  The -Bold, Italic, and Underline buttons are selected only if all selected text -annotations have the attribute enabled.

    - -

     

    - -

    - -

     

    - -

    Annotation Text Alignment Property

    - -

     

    - -

    Text Alignment Properties are -available for both horizontal and vertical alignment. 

    - -

     

    - -

    Horizontal Text Alignment -selections include Left, Center, and Right.  With Left, the annotationÕs X-coordinate -is on the left side of the text.  With Center, the annotationÕs -X-coordinate is in the horizontal center of the text.  With Right, the -annotationÕs X-coordinate is on the right side of the text.

    - -

     

    - -

    Vertical Text Alignment -selections include Bottom, Middle, and Top.  With Bottom, the annotationÕs -Y-coordinate is located at the bottom of the text.  With Middle, the -annotationÕs Y-coordinate is located in the vertical center of the text.  -With Top, the annotationÕs Y-coordinate is located at the top of the text.

    - -

     

    - -

    When there are multiple text -annotations selected with different alignments, none of the alignment buttons -are shown as selected.

    - -

     

    - -

    - -

     

    - -

    Annotation Text Orientation

    - -

     

    - -

    The text orientation property -includes two selections, Horizontal and Stacked.   Text characters -drawn in Horizontal Orientation are drawn in a left-to-right sequence.  -Text characters drawn in Stacked Orientation are drawn in a top-to-bottom -sequence.  If there are multiple text annotations selected with differing -orientations, neither of the orientation buttons is shown as selected.

    - -

     

    - -

    - -

     

    - -

     

    - -

    Annotation Coordinate Property

    - -

     

    - -

    The Coordinate Adjustment -Group allows the user to adjust the XYZ-coordinates of an annotation in Stereotaxic, -Tab, or Window Space.  If the annotation is in Surface Space, selections -are provided for the Structure, Vertex Index, and Offset.  The offset is -the distance of the annotation from its assigned surface vertex along the -surface vertexÕs normal vector.  Line annotations contain coordinates for -each endpoint and both sets of Coordinate controls are enabled.  Box, -Oval, and Text Annotations use one coordinate so only the first set of -coordinate controls is enabled.

    - -

     

    - -

    - -

     

    - -

    - -

     

    - -

    Located on the left side of -the coordinate controls are several alphanumeric characters that indicate the -coordinate space of the selected annotation(s).  If all of the selected -annotations are in the same coordinate space, the letter will be Sf (Surface), St -(Stereotaxic) T (Tab, followed by tab numbers), and W (Window).  If -multiple annotations are selected and they are in different coordinate spaces, -a plus symbol (+) is displayed.  The coordinate controls are enabled only -when a single annotation is selected.

    - -

    Annotation Width and Height

    - -

     

    - -

    The Annotation Width and Height -Properties control the width and height of Box, Image, and Oval Annotations.   -Both the width and height are normalized values that range from zero to one -where one is the full width/height of the tab or window.  For image -annotations, adjusting the width (or height) will also change the height (or -width) of the image so that the imageÕs aspect ratio is preserved to prevent -distortion of the image.

    - -

     

    - -

    - -

     

    - -

    Annotation Rotation Property

    - -

     

    - -

    The Annotation Rotation -property controls the rotation annotations.  The rotation value is in -degrees and a positive value rotates clockwise.

    - -

     

    - -

    - -

     

    - -

     

    - -

     

    - -

    Annotation Files

    - -

     

    - -

    There two mechanisms for -saving annotations.

    - -

    Disk Annotation File

    - -

     

    - -

    Disk Annotation Files can be -opened, saved, added to a Spec File just like other Workbench data files.

    - -

     

    - -

    Scene Annotation File

    - -

     

    - -

    The Scene Annotation File is -maintained Òin memoryÓ and when the user creates a Scene, these annotations -become part of the scene.  Thus, these annotations can only be displayed -when the scene is loaded.  A special entry is provided on the Manage/Save -Files Window to remove all active Scene Annotations.

    - -

     

    - -

    Selecting Annotations

    - -

     

    - -

    To select a single annotation -while in Annotations mode, simply move the mouse over the annotation (the -cursor will become a Òfour arrowsÓ symbol) and click the mouse.

    - -

     

    - -

    To select multiple -annotations, hold down the Shift Key while clicking the mouse over an -annotation.  When the mouse is clicked with the Shift Key down, any other -selected annotations remain selected. 

    - -


    -If an annotation that is a member of a group is selected, all annotations in -the group become selected. 

    - -

     

    - -

    To deselect all annotations, -click the mouse in a region where there is no annotation.  If there are -multiple annotations selected and one wants to deselect an annotation, click -the mouse over the annotation while holding down the Shift Key.

    - -

     

    - -

    When an annotation is in the -selected state, sizing handles are displayed around the annotation.  For a -Line Annotation, sizing handle symbols are drawn at the end points with a -slightly larger square symbol at the first coordinate (head of the -arrow).  For a Box or Oval Annotation, a box is drawn in the foreground -color with sizing handles distributed about the box.  When the mouse is -moved over a sizing handle, the mouse cursor will change to a symbol that -indicates how moving the size handle with the mouse will change the shape of -the annotation.

    - -

     

    - -

    Editing Annotation Position, Size, and Rotation

    - -

     

    - -

    To move an annotation, first -select the annotation and move the mouse over the annotation so that the cursor -is the four arrows symbol.  Hold down the left mouse button and then drag -the mouse to move the annotation to its new location.

    - -

     

    - -

    To resize a line annotation, -first select the line annotation.  Next, move the mouse over a sizing -handle at either end of the line so that the cursor becomes a Òtwo arrowÓ -symbol.  Now, hold down the left mouse button and drag the line end point -to its new location.

    - -

     

    - -

    To resize a box or oval -annotation, first select the annotation.  Next, move the mouse over one of -the sizing symbols so that the cursor becomes a Òtwo arrowÓ symbol.  Now, -hold down the left mouse button and drag to change the width and/or height of -the shape.  Dragging the circular symbol at the corners changes both the -width and height of the shape.  Dragging the square symbol on a side -changes width or height but not both.

    - -

     

    - -

    - -

     

    - -

    To rotate a box, image, oval, -or text annotation, first select the annotation.  Next, move the mouse -over the small rotation handle at the top of the shape (small black circle -attached to box around TEXT) at which time the cursor will become a rotation -symbol.  Now, to rotate the annotation, hold down the left mouse button -and drag the mouse away from the annotation to the desired orientation.  Think -of the rotation handle as a compass pointer and the the mouse pointer as -magnetic north.  The rotation handle will always point towards the mouse -pointer.

    - -

     

    - -

     

    - -

    - -

     

    - -

    Annotations may also be -moved, resized, and rotated using the annotation toolbarÕs controls while a -shape is selected.

    - -

     

    - -

    The text in a text annotation -may be edited by double-clicking the mouse over the text annotation.

    - -

     

    - -

    Editing (Redo and Undo)

    - -

     

    - -

     

    - -

     

    - -

    When editing annotations, the -user may mistakenly modify or delete an annotation.  For these instances, -pressing the Undo button will ÒundoÓ the last change to an annotation.  -Redo and Undo can also be used with grouping operations (described later in -this document).

    - -

    Inserting Annotations

    - -

     

    - -

     

    - -

    - -

    - -

     

    - -

     

    - -

    When creating annotations, -new annotations are placed in either ÒScene AnnotationsÓ or a Annotation File -stored to disk.  Scene Annotations are not saved to an annotation file -file but instead are added to a scene when a a new scene is created.  Note -that the user will need to save the scene file.  Disk annotation files are -saved and opened like other data files.

    - -

     

    - -

    To create a new annotation, -user must first choose the file that will contain the newly created -annotation.  Second, the user user must choose the Space from the top row -of buttons (the selected space is highlighted).  Third, the user must -press on of the Type buttons in the bottom row to choose the type of annotation -that is created.  Lastly, the user moves the mouse into the graphics -region and the mouse pointer will be a small ÔplusÕ symbol.  The user must -either click the mouse in the graphics region to insert a default annotation -for the type or drag the mouse to create a rectangular region that bounds the -new annotation.  If the location of the new annotation is incompatible -with the selected annotation space, a dialog pops up that allows the user to -choose a different space or to cancel creation of the annotation. When entering -a text annotation, a dialog will always pop-up for entry of the text.

    - -

     

    - -

    BEWARE that when drawing -annotations in surface space, the annotation may move a short distance from -where it was drawn.  This is caused by the surface offset attribute of the -surface coordinate.  While the annotation is attached to the surface -vertex closest to the mouse click, the annotation is offset by a vector.  -It is this offset vector that causes the annotation to move.  Without the -offset vector, surface annotations may be partially or even fully obscured by -the surface.

    - -

    Deleting Annotations

    - -

     

    - -

    - -

     

    - -

    Individual annotations are -deleted while in Annotation Mode by clicking the Trash Can Icon in the -Annotation Toolbar.  One may remove an entire file of Annotations on the -Manage/Save Files window.

    - -

     

    - -

    Format

    - -

     

    - -

    - -

     

    - -

    The arrange menu is used to -arrange and format multiple annotations.  At least two annotations must be -selected for an alignment operation.  At least three annotations must be -selected for a distribute operation.  Alignment and Distribution -operations may only be performed on annotations in Tab or Window space.

    - -

     

    - -

    á      -Align Left Ð The selected -annotations are moved so that the left edge of each annotation is at the same -X-coordinate as the left-most annotation.

    - -

    á      -Align Center Ð The selected -annotations are moved so that center X-coordinate of each annotation is at the -average X-coordinate of the annotations.

    - -

    á      -Align Right Ð The selected -annotations are moved so that the right edge of each annotation is at the same -X-coordinate as the right-most annotation.

    - -

    á      -Align Top - The selected -annotations are moved so that the top edge of each annotation is at the same Y-coordinate -as the top-most annotation.

    - -

    á      -Align Middle Ð The selected -annotations are moved so that the center Y-coordinate of each annotation is at -the average Y-coordinate of the annotations.

    - -

    á      -Align Bottom - The selected -annotations are moved so that the bottom edge of each annotation is at the same -Y-coordinate as the bottom-most annotation.

    - -

    á      -Distribute Horizontally Ð The -left-most and right-most annotations do not move.  The other annotations -are moved so there is equal horizontal spacing between adjacent annotations.

    - -

    á      -Distribute Vertically - The -top-most and bottom-most annotations do not move.  The other annotations -are moved so there is equal vertical spacing between adjacent annotations.

    - -

    á      -Group - See the description in -Annotation Grouping.

    - -

    á      -Regroup - See the description in -Annotation Grouping.

    - -

    á      -Ungroup - See the description in -Annotation Grouping.

    - -

     

    - -

    Context Sensitive (Pop-up) Menu

    - -

     

    - -

    A pop-up menu is available -when the Mode (mouse) is set to Annotate.  Items in the menu are enabled -only in certain conditions.

    - -

     

    - -

    Items in the pop-up menu are:

    - -

    á      -Cut Ð Removes the annotation under -the mouse and places it onto the annotation clipboard.

    - -

    á      -Copy Ð Copies the annotation under -the mouse and places it onto the annotation clipboard.

    - -

    á      -Delete Ð Deletes the annotation -under the mouse.

    - -

    á      -Paste Ð Inserts a copy of the -annotation on the clipboard at the mouse location.  If the mouse location -is incompatible with the annotationÕs space, a dialog is pops up to change the -space or cancel the paste operation.

    - -

    á      -Paste and Change Space Ð Pops up a -dialog to choose the space and then pastes a copy of the annotation from the -clipboard in the selected space.

    - -

    á      -Edit Text Ð Pops up a dialog for -changing the text in a text annotation.

    - -

    á      -Group Ð See the description in -Annotation Grouping.

    - -

    á      -Ungroup Ð See the description in -Annotation Grouping.

    - -

    á      -Regroup Ð See the description in -Annotation Grouping.

    - -

     

    - -

    Edit Menu

    - -

     

    - -

    The WindowÕs Edit Menu is available -for some operations.

    - -

     

    - -

    á      -Undo Ð Rolls back the last -annotation modification (additional text is added to the Undo item providing -information about the modification).

    - -

    á      -Redo Ð Rolls back the last ÒundoÓ -(additional text is added to the Redo item providing information about the -modification).

    - -

    á      -Cut Ð See Cut under Context -Sensitive (Pop-up) Menu.

    - -

    á      -Copy Ð See Copy under Context -Sensitive (Pop-up) Menu.

    - -

    á      -Paste Ð See Paste under Context -Sensitive (Pop-up) Menu.

    - -

    á      -Paste Special Ð See Paste and -Change Space under Context Sensitive (Pop-up) Menu.

    - -

    á      -Delete Ð See Delete under Context -Sensitive (Pop-up) Menu.

    - -
    -
    - -

     

    - -

    Display Control of Annotations

    - -

     

    - -

    - -

     

    - -

    Controlling the display of -annotation is performed using the Annot section in the Features Toolbox.

    - -

     

    - -

    Options

    - -

     

    - -

    Display Annotations Ð Enables -and disables the display of annotations in all open windows.

    - -

     

    - -

    Show Window Annotations in -Single Tab View Ð When checked, annotations in Window space are always -displayed.  When unchecked, annotations in Window space are only displayed -when Tile Tabs is enabled.

    - -

     

    - -

    Group Ð Group allows the user -to easily switch between several different display selections of -annotations. 

    - -

     

    - -

    The display selections for -annotations in window space function different than other spaces.  Because -the ÔGroupÕ selection is a property of each browser window ÒtabÓ, it is not -available when drawing Window annotations.  Therefore, there is only one -display selection for annotations in window space.

    - -

     

    - -

    Selection of Annotations in Features Toolbox

    - -

     

    - -

    To select an annotation in -the Features Toolbox, the user clicks the mouse over the name of the -annotation.  The annotation name is selected and all other annotations are -deselected.  The selected annotation is highlighted in the graphics -region.  The user may select multiple annotations in the features toolbox -by holding down the CTRL (Apple) key or the Shift key.  If the CTRL key is -held down while the mouse is clicked, the annotation under the mouse is added -to the selected annotations.  If the Shift key is held down while the -mouse is clicked, all annotation from the last selected annotation to the -annotations under the mouse are selected.

    - -

     

    - -

    If the user clicks the name -of either group type, Space or User, all annotations in the group are selected. - If the user selects the name of annotation that is in a User Group, all -annotations in the User Group are selected.

    - -

     

    - -

     

    - -

    Content of Annotations Tab in Features Toolbox

    - -

     

    - -

    In the Features Toolbox, -annotations are grouped into a hierarchy of File, Group, and Annotations. - The hierarchy is collapsed and expanded using the small triangles on the -left side of the annotation listing.  There are three checkbox states, -unchecked, partially checked, and checked.  An annotation is either -unchecked (not displayed) or checked (is displayed).  An Annotation Group -(and File) is unchecked when all annotations in the group (file) are unchecked -(not displayed); partially checked when some, but not all annotations in the -group are checked (displayed), and the group (file) is checked when all -annotations in the group are checked (displayed).

    - -

     

    - -

    For each annotation in the -features toolbox, an icon (small image) is displayed between the checkbox and -the annotationÕs name.  This icon may consist of several colors.  If -the annotation's ÔFill ColorÕ is enabled, the background of the icon is the -fill color.  If the annotationÕs line color is enabled, the edges of the -icon are drawn in the line color.  If the annotation is a text annotation, -a small square consisting of the text characterÕs color is in the center of -icon.

    - -

     

    - -

    Annotation Grouping

    - -

     

    - -

    Annotations are organized -into a hierarchy consisting of File->Group->Annotation.  The are two -types of groups: Space Groups and User Groups.  By default, an annotation -is assigned to a Space Group.  The user may move annotations from a Space -Group to a User Group by performing a ÔGroupÕ operation.  Annotations may -also be moved from a User Group to a Space Group by performing an ÔUngroupÕ -operation. 

    - -

     

    - -

    Both types of groups may only -contain annotations that are in the same coordinate space.  Stereotaxic -annotations are assigned to one group.  Surface annotations are also in -one group.  There is a group for each tab and a group for each window.

    - -

     

    - -

    Mouse Selection of Annotations

    - -

     

    - -

    To select an annotation with -the mouse, the user simply clicks the mouse over an annotation.  The -annotation under the mouse is selected and any other annotations are -deselected.  To select multiple annotations the user clicks the annotation -with the mouse while the Shift key is held down.  To deselect all annotations, -the user clicks the mouse in an empty region (not over an annotation).

    - -

     

    - -

    If the user selects an -annotation with the mouse and the annotation is in a User Group, all -annotations in the User Group are selected.

    - -

     

    - -

     

    - -

    Creating a User Group

    - -

     

    - -

    Creation of a user group is -allowed when two or more annotations are selected and all of the selected -annotations are in the same Space Group.  If these conditions are met, the -user may create a User Group by selected Group from the Annotation ToolbarÕs -Format->Arrange Menu.  The user may also right click the mouse and -select Group from the pop-up menu.

    - -

     

    - -

    Removing a User Group

    - -

     

    - -

    To remove a User Group, the -annotations in the User Group must be selected (recall that clicking any -annotation in a User Group selects all annotations in the User Group). - The annotations are returned to a Space Group by selecting -Ungroup from the Annotation ToolbarÕs Format->Arrange Menu.  The -user may also right click the mouse and select Ungroup from the pop-up menu.

    - -

     

    - -

     

    - -

    Recreating a User Group

    - -

     

    - -

    To recreate a User Group, the -user must select at least one annotation that is currently in a Space Group but -was previously in a User Group.  If these conditions are met, the user may -recreate the User Group by selected Regroup from the Annotation ToolbarÕs -Format->Arrange Menu.  The user may also right click the mouse and -select Regroup from the pop-up menu.  All annotations that were previously -in the User Group are moved back to the User Group.

    - -

     

    - -

     

    - -

    Color Bar Editing

    - -

     

    - -

    Each overlay contains one -color bar that is available when the selected overlay is mapped using a color -palette (typically for floating point data).  To show a color bar, press -the rainbow button under Settings in Overlay ToolBox->Layers.  To Edit -the color bar, press the wrench icon.

    - -

     

    - -

     

    - -

    - -

     

    - -

     

    - -

    - -

     

    - -

    Location and Positioning

    - -

     

    - -

    This section controls the -coordinate space and location within the space for the color bar.

    - -

     

    - -

    Show Color Bar in Sets the allowable space for the color bar.

    - -


    -Tab Ð Color bar is restricted to the region in which the tabÕs -data is drawn.

    - -

     

    - -

    Window Ð -Color bar may be anywhere in the window.

    - -

     

    - -

    Positioning

    - -

     

    - -

    Auto Ð Colorbar is automatically positioned in the bottom -left corner of the tab/window. If there is more than one color bar, they are -vertically stacked.

    - -

     

    - -

    Manual Ð Allows the user to manually position the color bar -by selected the color bar (click it in Annotations Editing Mode) and dragging -it with the mouse or using the coordinate controls in the annotationÕs editing -toolbar.  It is not necessary to switch the Positioning Mode to Manual to -move the color bar.  Simply select the color bar and drag it with the -mouse and the Positioning will immediately change to Manual.

    - -

     

    - -

    Data Numerics

    - -

     

    - -

    Data Mode Controls format of numeric above color bar

    - -

     

    - -

    Data Numeric text above the color bar reflect values -within the data.

    - -

     

    - -

    Percentile Numeric text above the color bar reflect the -percentiles within the data.

    - -

     

    - -

    Sign Only Numeric text above the color bar simply indicate the -sign (negative/positive of the data).

    - -

     

    - -

    Numeric Format Controls the formatting of numeric text above the -color bar.

    - -

     

    - -

    Auto Numeric text will automatically choose decimal or -scientific notation and digits right of the decimal point to best represent the -data values.

    - -

     

    - -

    Decimal Numeric text will be in decimal format.

    - -

     

    - -

    Scientific Numeric text will be in scientific notation.

    - -

     

    - -

    Dec/Sci Decimals Enabled when Numeric Format is Decimal or Scientific -and controls the number of digits right of the decimal point.

    - -

     

    - -

    Subdivisions By default, the numeric text above the color bar -consists of a negative value, zero, and a positive value.  Subdivisions is -used to add additional values between zero and negative/positive.

    - -

     

    - -

    Show Tick Marks If checked, a small tick mark is displayed below each -numeric text value to indicate its exact position in the color bar.

    - -

     

    - -

     

    - -

     

    - -

     

    - -

     

    - -
    - - - - diff --git a/src/Resources/GuiResources/HelpFiles/Features_Toolbox/Borders/Borders.html b/src/Resources/GuiResources/HelpFiles/Features_Toolbox/Borders/Borders.html deleted file mode 100644 index f690185c7874d786daa8688988aada34a5e2c9bb..0000000000000000000000000000000000000000 --- a/src/Resources/GuiResources/HelpFiles/Features_Toolbox/Borders/Borders.html +++ /dev/null @@ -1,109 +0,0 @@ - - - - - Borders - - - - - Borders
    - The Borders Tab in the Features Toolbox - contains options for display and selection of loaded and newly - created borders on brain surfaces. -
      -
    • Display Borders toggles border - display on and off. When Border Mode - is turned on, Display Borders is turned on by default.
    • -
    -
      -
    • Group: identifies the Border - group for the Active Tab. Tabs assigned to the same Group will - display the same borders with the same attributes.
    • -
    - -
      -
    • Borders Attributes contains - options for viewing borders.
    • -
    -
      -
        -
      • Contralateral check box toggles - on display of borders that are currently being displayed on - one hemisphere to be displayed on the contralateral - hemisphere (on top of that hemisphere's own displayed - borders, if any).
        -
      • -
      -
    -
      -
        -
      • Draw As sets the shape of the - border components (Lines, Spheres, Squares, Spheres and - Lines).
        -
      • -
      -
    -
      -
        -
      • Coloring sets the Class, Name, or Standard Color - as the border color source.
        -
      • -
      -
    -
      -
        -
      • Standard ColorSelects the color when Coloring is set to Standard Color.
        -
      • -
      -
    -
      -
        -
      • Line Diameter controls the - border thickness if line is selected as the border - component.
        -
      • -
      -
    -
      -
        -
      • Symbol Diameter controls the - size of the sphere or square symbol border components.
        -
      • -
      -
    -
      -
        -
      • Unstretched Lines is used - to correctly display a border from a 3D surface on a flat - surface when the border crosses a surface cut from - flattening. The distance indicates the limit for how much a - border can be stretched across a cut surface before it is - NOT displayed. Specifically, if the value of [(length of - border on flat surface) / (length of border of 3d - volume-interaction surface)] exceeds the Unstretched Lines - value then the border is NOT displayed.
        -
      • -
      -
    -
      -
    -
    -
    - -
      -
    • Borders Selection controls group - and individual display selection for borders. If a higher - level group is selected on or off, all borders below that - level will be similarly selected on/off. The All On/Off - buttons allow for quick toggling of all borders on or off.
      -
    • -
    -
    -
    - - - diff --git a/src/Resources/GuiResources/HelpFiles/Features_Toolbox/Borders_Attributes.png b/src/Resources/GuiResources/HelpFiles/Features_Toolbox/Borders_Attributes.png deleted file mode 100644 index 1f41432bf2e5003c67976f633c823931ce9591f9..0000000000000000000000000000000000000000 Binary files a/src/Resources/GuiResources/HelpFiles/Features_Toolbox/Borders_Attributes.png and /dev/null differ diff --git a/src/Resources/GuiResources/HelpFiles/Features_Toolbox/Features_Toolbox.html b/src/Resources/GuiResources/HelpFiles/Features_Toolbox/Features_Toolbox.html deleted file mode 100644 index de90e361d98aff488068861865fadedcb81f80ea..0000000000000000000000000000000000000000 --- a/src/Resources/GuiResources/HelpFiles/Features_Toolbox/Features_Toolbox.html +++ /dev/null @@ -1,90 +0,0 @@ - - - - - Features Toolbox - - - Features Toolbox
    - The Features Toolbox contains controls for - displaying features including Annotations, Borders, - Fibers, Foci, Images, and Labels onto the data - being displayed on structures.
    -  
    - The Features Toolbox is not displayed by default, but is turned on - with the button (or with View - > Features Toolbox) and attached to the right side of - the Viewing - - - - - - - - - - - - Area. It can also be
    detached from the Workbench Window by left click - + drag on the top of the - Features Toolbox. To reattach, double click on the - top of the - Features Toolbox.
    -
    - There are 6 tabs in the Features Toolbox, each controlling a - different kind of feature's display:
    -
      -
    • Annotations
    • -
    -
      -
    • Borders controls - attributes and selection of borders on surfaces.
    • -
    -
      -
    • Fibers controls attributes, selection, and samples of fibers on - surfaces and in volumes.
    • -
    -
      -
    • Foci - controls attributes and selection - of foci on surfaces and in volumes.
    • -
    -
      -
    • Images - controls selection - of images in the background.
    • -
    -
      -
    • Labels controls attributes and selection - of labels on surfaces and in - volumes.
      -
    • -
      -
    - These tabs are inactive (grayed-out) if no - files are loaded containing a particular Feature.
    -
    -
      -
      -

      - -
        -
      -
    - - diff --git a/src/Resources/GuiResources/HelpFiles/Features_Toolbox/Foci/Foci_Attributes.png b/src/Resources/GuiResources/HelpFiles/Features_Toolbox/Foci/Foci_Attributes.png deleted file mode 100644 index 1406665544e9dd239e1d2cdf27b2bf9fc56e1b93..0000000000000000000000000000000000000000 Binary files a/src/Resources/GuiResources/HelpFiles/Features_Toolbox/Foci/Foci_Attributes.png and /dev/null differ diff --git a/src/Resources/GuiResources/HelpFiles/Features_Toolbox/Images/Images.html b/src/Resources/GuiResources/HelpFiles/Features_Toolbox/Images/Images.html deleted file mode 100644 index f09d1287678243ecf7ab6a708526489f3cb498c5..0000000000000000000000000000000000000000 --- a/src/Resources/GuiResources/HelpFiles/Features_Toolbox/Images/Images.html +++ /dev/null @@ -1,31 +0,0 @@ - - - - - Borders - - - - - Images
    - The Images Tab in the Features Toolbox - contains options for display and selection of images in the background. -
      -
    • Group: identifies the Image - group for the Active Tab. Tabs assigned to the same Group will - display the same images with the same attributes.
    • -
    - -
      -
    • Display Image toggles image - display on and off.
    • -
    -
      -
    • Images Selection Chooses image displayed in the background. -
    -
    - - - diff --git a/src/Resources/GuiResources/HelpFiles/Features_Toolbox/Images/Images_Selection.png b/src/Resources/GuiResources/HelpFiles/Features_Toolbox/Images/Images_Selection.png deleted file mode 100644 index 43a2e26fb88be8e02bdaa982f061820200005187..0000000000000000000000000000000000000000 Binary files a/src/Resources/GuiResources/HelpFiles/Features_Toolbox/Images/Images_Selection.png and /dev/null differ diff --git a/src/Resources/GuiResources/HelpFiles/Information_Window/Properties/Information_Properties.png b/src/Resources/GuiResources/HelpFiles/Information_Window/Properties/Information_Properties.png deleted file mode 100644 index b2908905ae8efdc092e344b901980d6c0be7f883..0000000000000000000000000000000000000000 Binary files a/src/Resources/GuiResources/HelpFiles/Information_Window/Properties/Information_Properties.png and /dev/null differ diff --git a/src/Resources/GuiResources/HelpFiles/Menus/Data_Menu/Data_Menu.html b/src/Resources/GuiResources/HelpFiles/Menus/Data_Menu/Data_Menu.html deleted file mode 100644 index b897079411f01923e5f89bb0465daedfad6e3921..0000000000000000000000000000000000000000 --- a/src/Resources/GuiResources/HelpFiles/Menus/Data_Menu/Data_Menu.html +++ /dev/null @@ -1,25 +0,0 @@ - - - - - Data Menu - - - Data Menu
    -
      -
    • Split Multi-Structure Border Files Older border files - may contain borders for more than one surface structure. This menu item allows the user - split the old border file into single structure border files.
    • -
    -
      -
    • Project fixed distance above - surface(s) option allows the user to project foci a - fixed distance above or below the volume interaction surface. - Any offset of the foci from the surface is NOT preserved.
      -
    • -
    -
    -
    - - - diff --git a/src/Resources/GuiResources/HelpFiles/Menus/Edit_Menu/Edit_Menu.html b/src/Resources/GuiResources/HelpFiles/Menus/Edit_Menu/Edit_Menu.html deleted file mode 100644 index 300bcd445df24ce3298cdf84425a376daeddf233..0000000000000000000000000000000000000000 --- a/src/Resources/GuiResources/HelpFiles/Menus/Edit_Menu/Edit_Menu.html +++ /dev/null @@ -1,23 +0,0 @@ - - - - - Window Menu - - - Edit Menu
    - Items on the Edit Menu and their text will vary depending upon the state of wb_view. -
    -
      -
    • Undo Undo a previous operation.
    • -
    • Redo Redo an operation that was undone.
    • -
    • Cut Remove and place the selected item onto the clipboard.
    • -
    • Copy Place a copy of the selected item onto the clipboard.
    • -
    • Paste Paste the item from the clipboard at the mouse cursor location.
    • -
    • Paste Special A special mode to paste the item from the clipboard at the mouse cursor location.
    • -
    • Delete Delete the selected item
    • -
    -  
    -  
    - - diff --git a/src/Resources/GuiResources/HelpFiles/Menus/Edit_Menu/Edit_Menu.png b/src/Resources/GuiResources/HelpFiles/Menus/Edit_Menu/Edit_Menu.png deleted file mode 100644 index 4eeee7078592a11d2bf12b0bdc4af58ddeb34683..0000000000000000000000000000000000000000 Binary files a/src/Resources/GuiResources/HelpFiles/Menus/Edit_Menu/Edit_Menu.png and /dev/null differ diff --git a/src/Resources/GuiResources/HelpFiles/Menus/File_Menu/Preferences/Preferences.html b/src/Resources/GuiResources/HelpFiles/Menus/File_Menu/Preferences/Preferences.html deleted file mode 100644 index b4489ebfdf37d60f4e577c5f53c5586c8821aae1..0000000000000000000000000000000000000000 --- a/src/Resources/GuiResources/HelpFiles/Menus/File_Menu/Preferences/Preferences.html +++ /dev/null @@ -1,126 +0,0 @@ - - - - - Preferences - - - Preferences
    - Preferences, accessible under the wb_view menu - (Mac) or File menu - (PC or Linux), allows one to tailor the Workbench viewing - environment to one's preference. Changes to Preferences are saved - between Workbench sessions. Options include:
    - -
    Colors   - - - -  
    -
      -
    • Buttons to set the Foreground - (chart axes, colorbar labels, etc.) and Background - colors for All types of views, or Chart, Surface, or Volume - views separately.
      -
    • -
    -
    -  
    -
    -

    - - ID     -
      -
    • Show Surface ID Symbols to enable display of - identification symbols on surfaces. -
    • -
    • Show Volume ID Symbols to enable display of - identification symbols on volume slices. -
    • -
      -  
      - - Misc     -
        -
      • Logging Level to set the logging - default for - the terminal window (sometimes useful for examining - bugs).
        -
      • -
      • Save/Manage View Files to set - which files are listed upon opening the Save/Manage - - - - - - Files dialog (in the File Menu): All, - Loaded, Loaded:Modified, Loaded:Not Modified, Not Loaded.
        -
      • -
      • New Tabs Yoked to Group A to set - On/Off whether newly created tabs are by default Yoked - to the Group A yoking group.
        -
      • -
      • Settings to show/hide the Develop - Menu and show/not show the Splash - - - - - Screen at Startup. 
        -
      • -
      -
      -
      -  
      -
      - OpenGL  
       
      -
        -
      • Setting for the Image Capture - Method (OpenGL computer code) used by wb_view to - capture an image of the Viewing Window. Changing this setting - might resolve problems with image capturing on your system.
        -
      • -
      -
      -  
      -
      -
      - Volume 
         
      -
        -
      • Volume Axes Crosshairs to set - On/Off display of axes lines in all Volume views.
        -
      • -
      • Volume Axes Labels to set On/Off - display of superior (S)/inferior (I) and left (L)/right (R) - axes in all Volume views. 
        -
      • -
      • Volume Identification for New Tabs, - to set in newly created Tabs, On/Off movement of the volume slice/crosshairs - displayed to the same plane as the brainordinate selected in - that tab (or a yoked surface/volume tab).
        -
      • -
      • Volume Montage Slice Coord to - set On/Off display of x, y, or z coordinate slice position in - all Volume slice plane views.
        -
      • -
      • Volume Montage Precision to set - the decimal point precision of x, y, or z coordinate slice - position values in all Volume slice plane views.
        -
      • -
      -
      -  
      - - diff --git a/src/Resources/GuiResources/HelpFiles/Menus/File_Menu/Preferences/Preferences_Colors.png b/src/Resources/GuiResources/HelpFiles/Menus/File_Menu/Preferences/Preferences_Colors.png deleted file mode 100644 index 45ee2844000e444a771031e91b5f701da7b51ffa..0000000000000000000000000000000000000000 Binary files a/src/Resources/GuiResources/HelpFiles/Menus/File_Menu/Preferences/Preferences_Colors.png and /dev/null differ diff --git a/src/Resources/GuiResources/HelpFiles/Menus/File_Menu/Preferences/Preferences_ID.png b/src/Resources/GuiResources/HelpFiles/Menus/File_Menu/Preferences/Preferences_ID.png deleted file mode 100644 index 35d08304b1baa8e585926f9e4dd641fda5e44342..0000000000000000000000000000000000000000 Binary files a/src/Resources/GuiResources/HelpFiles/Menus/File_Menu/Preferences/Preferences_ID.png and /dev/null differ diff --git a/src/Resources/GuiResources/HelpFiles/Menus/File_Menu/Preferences/Preferences_Misc.png b/src/Resources/GuiResources/HelpFiles/Menus/File_Menu/Preferences/Preferences_Misc.png deleted file mode 100644 index 7ec74ed4184b1dc6d721f5c23df125c11585705e..0000000000000000000000000000000000000000 Binary files a/src/Resources/GuiResources/HelpFiles/Menus/File_Menu/Preferences/Preferences_Misc.png and /dev/null differ diff --git a/src/Resources/GuiResources/HelpFiles/Menus/File_Menu/Preferences/Preferences_OpenGL.png b/src/Resources/GuiResources/HelpFiles/Menus/File_Menu/Preferences/Preferences_OpenGL.png deleted file mode 100644 index 7742a685deaf62460915995c63c1b805b1998cc1..0000000000000000000000000000000000000000 Binary files a/src/Resources/GuiResources/HelpFiles/Menus/File_Menu/Preferences/Preferences_OpenGL.png and /dev/null differ diff --git a/src/Resources/GuiResources/HelpFiles/Menus/Window_Menu/Window_Menu.png b/src/Resources/GuiResources/HelpFiles/Menus/Window_Menu/Window_Menu.png deleted file mode 100644 index 219612c18405690792e61ef243dba1cf6fc879ba..0000000000000000000000000000000000000000 Binary files a/src/Resources/GuiResources/HelpFiles/Menus/Window_Menu/Window_Menu.png and /dev/null differ diff --git a/src/Resources/GuiResources/HelpFiles/Overlay_Toolbox/Overlay_Toolbox_horiz.png b/src/Resources/GuiResources/HelpFiles/Overlay_Toolbox/Overlay_Toolbox_horiz.png deleted file mode 100644 index f2e199ff9944d7d7790869ee065314c0432545a2..0000000000000000000000000000000000000000 Binary files a/src/Resources/GuiResources/HelpFiles/Overlay_Toolbox/Overlay_Toolbox_horiz.png and /dev/null differ diff --git a/src/Resources/GuiResources/HelpFiles/Overlay_Toolbox/Overlay_Toolbox_vert.png b/src/Resources/GuiResources/HelpFiles/Overlay_Toolbox/Overlay_Toolbox_vert.png deleted file mode 100644 index 4f43ca4264b13b318c8657d960c87b3a7b88962c..0000000000000000000000000000000000000000 Binary files a/src/Resources/GuiResources/HelpFiles/Overlay_Toolbox/Overlay_Toolbox_vert.png and /dev/null differ diff --git a/src/Resources/GuiResources/HelpFiles/Scenes_Window/Create_New_Scene_File/Create_New_Scene.png b/src/Resources/GuiResources/HelpFiles/Scenes_Window/Create_New_Scene_File/Create_New_Scene.png deleted file mode 100644 index 0b7fb88ed795db0496fe0b23bf9b981cb4797db4..0000000000000000000000000000000000000000 Binary files a/src/Resources/GuiResources/HelpFiles/Scenes_Window/Create_New_Scene_File/Create_New_Scene.png and /dev/null differ diff --git a/src/Resources/GuiResources/HelpFiles/Scenes_Window/Scenes_Window.html b/src/Resources/GuiResources/HelpFiles/Scenes_Window/Scenes_Window.html deleted file mode 100644 index ed0b7b31318e331d3a97b318a0d5fdb8966711e2..0000000000000000000000000000000000000000 --- a/src/Resources/GuiResources/HelpFiles/Scenes_Window/Scenes_Window.html +++ /dev/null @@ -1,111 +0,0 @@ - - - - - Scenes Window - - - Scenes Window
      - The Scenes Window is the interface to - save the current Workbench session (loaded files and settings) as - a Scene - (in a .scene file).  Scenes allow one to save a snapshot of a - Workbench session that contains a record of all of the files - loaded and view settings, making it easy to get to a specific - display.  Using scenes, one can "pick up where they left off" - when reopening Workbench. -
        -
      • Scene File selection is used to - choose among loaded Scene files. Use File>Open - File... to load an existing scene file and have it - show up in this selector.
      • -
      -
        -
      • BALSA Study ID contains the - Study ID that is provided when uploading the scene file to - the BALSA Database (currently in development). Press the - Edit button to change this value.
      • -
      -
        -
      • New... button creates a new - Scene File.  A new Scene File is created with no scenes - within it. Add any number of scenes with the Add - button.
      • -
      -
        -
      • Show - loads the active (highlighted in blue) scene.  Double - clicking on a scene's title/description will also show the - scene.
      • -
      -
        -
      • Preview... opens a separate window with an enlarged - display of the highlighted scene's thumbnail along with the - name and description.
      • -
      -
        -
      • Add... creates a new scene - within the current Scene File and adds it to the bottom of - the list of scenes.  The scene needs to be set up - before clicking this button.
      • -
      -
        -
      • Insert... creates a new scene - that is added to the scene list above the currently - highlighted scene.
      • -
      -
        -
      • Replace... is used to modify an existing scene.  The highlighted scene - will be overwritten at the time the Replace button - is clicked, therefore make scene changes before clicking - this button.   When a scene is replaced, the - name/description of the scene can also be edited. 
      • -
      -
        -
      • Move Up moves the selected scene - up one position in the list of scenes.
      • -
      -
        -
      • Move Down moves the selected scene - down one position in the list of scenes.
      • -
      -
        -
      • Delete... allows one to delete a - highlighted scene from the Scene File.
      • -
      -
        -
      • Show Scene Options
      • -
          -
        • Use background and foreground colors from scene when - a scene is created, the - foreground and background colors are added to the scene and will be used when the - scene is displayed. Note that the background and foreground colors are adjusted - using the Preferences Dialog. When a scene is loaded that contains background and - foreground colors, they will override the user's Preferences colors until a - different scene is loaded, a spec file is loaded, or this checkbox is unchecked. -
        • -
        -
      - Note: All additions and modifications to a - scene file are not ultimately saved (to disk) until one clicks Save - - - - - - checked files in Save/Manage - - files (accessed from the File menu).
      -
      -
      -
      -
        -
      -
      - - diff --git a/src/Resources/GuiResources/HelpFiles/Scenes_Window/Scenes_Window.png b/src/Resources/GuiResources/HelpFiles/Scenes_Window/Scenes_Window.png deleted file mode 100644 index 701a0a98ec94547bca5d24580cea23158f558f9c..0000000000000000000000000000000000000000 Binary files a/src/Resources/GuiResources/HelpFiles/Scenes_Window/Scenes_Window.png and /dev/null differ diff --git a/src/Resources/GuiResources/HelpFiles/Toolbar/All_View/All_Toolbar.png b/src/Resources/GuiResources/HelpFiles/Toolbar/All_View/All_Toolbar.png deleted file mode 100644 index b36ace42e35e5ca0636997d1576b04c2438f1368..0000000000000000000000000000000000000000 Binary files a/src/Resources/GuiResources/HelpFiles/Toolbar/All_View/All_Toolbar.png and /dev/null differ diff --git a/src/Resources/GuiResources/HelpFiles/Toolbar/All_View/All_View.html b/src/Resources/GuiResources/HelpFiles/Toolbar/All_View/All_View.html deleted file mode 100644 index 4c53a0e94d86df6647b6b1709d8f48a42f9a3d7c..0000000000000000000000000000000000000000 --- a/src/Resources/GuiResources/HelpFiles/Toolbar/All_View/All_View.html +++ /dev/null @@ -1,184 +0,0 @@ - - - - - All View - - - All View
      - All View displays the left and right hemisphere surfaces (and - cerebellum if loaded) and all 3 volume planes in one Viewing - - - - - - - - - - - - - - - Tab. In All View, the Toolbar looks like this:
      -
      - -
        -
      • Orientation contains buttons to - set orthogonal and User-defined views of the brain - surfaces/volume displayed.
      • -
      -
        -
      -
      -
        -
      • Orthogonal view buttons are labeled: - L (left), R (right), D (dorsal), V - (ventral), A (anterior), and P - (posterior). 
      • -
      -
      -
        -
          -
        -
      -
      -
        -
      -
      -
      -
        -
      • The Reset button resets the - orientation/zoom to the default.
      • -
      -
      -
      - -
      -
        -
      • Surface Viewing contains - pull-downs and check-boxes to set the surfaces to view: L - (left hemisphere), R (right hemisphere), C - (cerebellum).
      • -
      -
        -
      -
      -
        -
      • The top pull-down menu changes the - type of surface: Anatomical, Inflated or Very Inflated.
      • -
      -
      -
      -
        -
      • The check-boxes to the left of the L, - R and C turn on/off the display of that structure.
      • -
      -
      -
      -
        -
      • To change the structure's surface - file, click on the L, R or C button and a list of loaded - files will appear for selection.
      • -
      -
      -
      -
        -
      • The scroll boxes - to the right, control the spacing between left and right - hemispheres (relative to anatomical spacing = 0) and the - spacing of the cerebellum away from the cortex.
      • - -
      -
      - -
        -
      • Slice Indices/Coords - contains toggles to turn off/on volume planes (P = - parasagittal, C = coronal, A = axial) and - settings for the slice index and stereotaxic (Talairach) - coordinate to be viewed. 
      • -
      -
        -
      -
      -
        -
      • The vertical Origin button - rests the slice indices to the default (centered at the - AC).
      • -
      -
      -
      -
      -
        - -
      • The  button (default - on) activates movement of the crosshairs/volume slice to the - same plane as the brainordinate selected in any of the - Viewing Tabs in the same yoking group as the All tab.
      • -
      -
      -
      -
        -
      • The pull-down at the bottom toggles - between Orthogonal and Oblique - volume viewing.
      • -
      -
      - -
        -
      • Clipping - contains settings for cutting down Surface, Volume, or - Features data to be viewed.
      • -
      -
        -
      • Tab - contains cross-tab functions for yoking the display of - two or more Viewing Tabs.
        -
      • -
      -
      - - diff --git a/src/Resources/GuiResources/HelpFiles/Toolbar/All_View/All_view.png b/src/Resources/GuiResources/HelpFiles/Toolbar/All_View/All_view.png deleted file mode 100644 index 20d81640b2385520905c08c9cfdf7f86a03ab9db..0000000000000000000000000000000000000000 Binary files a/src/Resources/GuiResources/HelpFiles/Toolbar/All_View/All_view.png and /dev/null differ diff --git a/src/Resources/GuiResources/HelpFiles/Toolbar/All_View/Thumbs.db b/src/Resources/GuiResources/HelpFiles/Toolbar/All_View/Thumbs.db deleted file mode 100755 index b524168bd45b2ca775756d9689c2cf56e1ffc65d..0000000000000000000000000000000000000000 Binary files a/src/Resources/GuiResources/HelpFiles/Toolbar/All_View/Thumbs.db and /dev/null differ diff --git a/src/Resources/GuiResources/HelpFiles/Toolbar/Chart_View/ChartView_dtseries_DataSeries.png b/src/Resources/GuiResources/HelpFiles/Toolbar/Chart_View/ChartView_dtseries_DataSeries.png deleted file mode 100644 index 835e3e15fc0f8d7f0e7d993dd3d45c77537d7ec8..0000000000000000000000000000000000000000 Binary files a/src/Resources/GuiResources/HelpFiles/Toolbar/Chart_View/ChartView_dtseries_DataSeries.png and /dev/null differ diff --git a/src/Resources/GuiResources/HelpFiles/Toolbar/Chart_View/ChartView_pscalar_Matrix.png b/src/Resources/GuiResources/HelpFiles/Toolbar/Chart_View/ChartView_pscalar_Matrix.png deleted file mode 100644 index 59451022763a81c8690304990b1ca14f6943bc93..0000000000000000000000000000000000000000 Binary files a/src/Resources/GuiResources/HelpFiles/Toolbar/Chart_View/ChartView_pscalar_Matrix.png and /dev/null differ diff --git a/src/Resources/GuiResources/HelpFiles/Toolbar/Chart_View/Chart_View.html b/src/Resources/GuiResources/HelpFiles/Toolbar/Chart_View/Chart_View.html deleted file mode 100644 index 2653190bd7a98cb179b9b1092c1ab94f61ab876d..0000000000000000000000000000000000000000 --- a/src/Resources/GuiResources/HelpFiles/Toolbar/Chart_View/Chart_View.html +++ /dev/null @@ -1,93 +0,0 @@ - - - - - Chart View - - - - Chart View
      - Chart View displays 2-dimensional graphs or - matrices of Workbench-chartable files.
      -
      - Chart Type, determines which kind of - graph is viewed. If one or more Workbench-chartable files are - loaded, one or more Chart Types will be available for selection. - Chartable files for the selected Chart Type are listed and can be - selected for display in the Overlay Toolbox Charting - tab.
      -  
      - Chart Types currently available:
      - Data/Time - - Series (top image) chartable files are graphed - for a brainordinate identified in a non-Chart view Viewing Tab, - showing a data value (y-axis) for each Map Index/unit of time - (x-axis). The following file types are available for Data/Time - Series charting, as long as they contain more than one - map: -
      -
      - * CIFTI - Data Series (.dtseries.nii)
      - * - CIFTI Parcel Scalar (.pscalar.nii)
      - * - CIFTI Parcel Series (.ptseries.nii)
      - * - CIFTI Scalar (.dscalar.nii)
      - * - Metric (.func.gii, .shape.gii)
      - * - Volume (.nii, .nii.gz)
      -
      - Matrix - (bottom image) chartable files:
      -
      - * - CIFTI Parcel to - parcel connectivity (.pconn.nii) -- matrix - - - - - with parcels as the y-axis rows, parcels as the x-axis columns, - and the colored cells indicate the correlation (connectivity) - values between the parcel pair.
      - * - CIFTI Parcel Scalar (.pscalar.nii) -- matrix - - - - with parcels as the y-axis rows, map indices as the x-axis - columns, and the colored cells indicate the scalar data value - for a parcel/map pair in a particular parcel - (identified in a non-Chart view Viewing Tab). 
      -
      -
      -
      -  
      -
      - - - diff --git a/src/Resources/GuiResources/HelpFiles/Toolbar/Chart_View/Data-Time_Series_Chart_View/ChartDataSeries.png b/src/Resources/GuiResources/HelpFiles/Toolbar/Chart_View/Data-Time_Series_Chart_View/ChartDataSeries.png deleted file mode 100644 index aabcbe790c4f18dea75bf36e18ce5d65aba5af59..0000000000000000000000000000000000000000 Binary files a/src/Resources/GuiResources/HelpFiles/Toolbar/Chart_View/Data-Time_Series_Chart_View/ChartDataSeries.png and /dev/null differ diff --git a/src/Resources/GuiResources/HelpFiles/Toolbar/Chart_View/Data-Time_Series_Chart_View/ChartView_DataSeries_Toolbar.png b/src/Resources/GuiResources/HelpFiles/Toolbar/Chart_View/Data-Time_Series_Chart_View/ChartView_DataSeries_Toolbar.png deleted file mode 100644 index 0b66e93232eec7284edecad324f3561a1c161692..0000000000000000000000000000000000000000 Binary files a/src/Resources/GuiResources/HelpFiles/Toolbar/Chart_View/Data-Time_Series_Chart_View/ChartView_DataSeries_Toolbar.png and /dev/null differ diff --git a/src/Resources/GuiResources/HelpFiles/Toolbar/Chart_View/Data-Time_Series_Chart_View/ChartView_dtseries_DataSeries.png b/src/Resources/GuiResources/HelpFiles/Toolbar/Chart_View/Data-Time_Series_Chart_View/ChartView_dtseries_DataSeries.png deleted file mode 100644 index aabcbe790c4f18dea75bf36e18ce5d65aba5af59..0000000000000000000000000000000000000000 Binary files a/src/Resources/GuiResources/HelpFiles/Toolbar/Chart_View/Data-Time_Series_Chart_View/ChartView_dtseries_DataSeries.png and /dev/null differ diff --git a/src/Resources/GuiResources/HelpFiles/Toolbar/Chart_View/Data-Time_Series_Chart_View/ChartView_pscalar_Matrix.png b/src/Resources/GuiResources/HelpFiles/Toolbar/Chart_View/Data-Time_Series_Chart_View/ChartView_pscalar_Matrix.png deleted file mode 100644 index f27ca15cf1f79d18ebc9a82528d57b052216d256..0000000000000000000000000000000000000000 Binary files a/src/Resources/GuiResources/HelpFiles/Toolbar/Chart_View/Data-Time_Series_Chart_View/ChartView_pscalar_Matrix.png and /dev/null differ diff --git a/src/Resources/GuiResources/HelpFiles/Toolbar/Chart_View/Data-Time_Series_Chart_View/Data-Time_Series_Chart_View.html b/src/Resources/GuiResources/HelpFiles/Toolbar/Chart_View/Data-Time_Series_Chart_View/Data-Time_Series_Chart_View.html deleted file mode 100644 index 1f0a8bc93435625c02d43af8d341d1f0f5460bfa..0000000000000000000000000000000000000000 --- a/src/Resources/GuiResources/HelpFiles/Toolbar/Chart_View/Data-Time_Series_Chart_View/Data-Time_Series_Chart_View.html +++ /dev/null @@ -1,61 +0,0 @@ - - - - - Data/Time Series Chart View - - - Data/Time Series Chart View
      - When Data Series or Time Series is selected as - the Chart Type, the Toolbar - appears like this:
      -
      -
      -
        -
      • The only difference between a Data Series - or Time Series file is that in a Time Series, the Map Index is - a unit of time (each map in the series shows data at a - particular timepoint).
      • -
      -
        -
      • Select a Data/Time Series chartable - file in the Overlay Toolbox Charting - tab.
      • -
      -
        -
      • Charts are viewed for a particular - brainordinate, therefore you must click on a grayordinate in a - volume or surface Viewing - - - - Tab, then click back to the Chart tab to - view the graph for that location. The color of the graph - should match the color of the brainordinate ID sphere, as - shown in the image:
        -
      • -
      -
      -
      -
        -
      • Chart Axes:  Allows - adjustment of the minimum and maximum values for each axis. - The Auto checkboxes indicate that the range for the - axis is automatically set to the min and max of the data being - displayed.
      • -
      -
        -
      • Chart Attributes: To adjust - attributes of the chart display, such as the width of the - graph line.
      • -
      -
        -
      • Tab: These across Tab - functions do not affect Chart View.
        -
      • -
      - - diff --git a/src/Resources/GuiResources/HelpFiles/Toolbar/Chart_View/Matrix_Chart_View/ChartView_Matrix_Toolbar.png b/src/Resources/GuiResources/HelpFiles/Toolbar/Chart_View/Matrix_Chart_View/ChartView_Matrix_Toolbar.png deleted file mode 100644 index 544d5dd814e72db75331aea0b01fdcfaf5185c1d..0000000000000000000000000000000000000000 Binary files a/src/Resources/GuiResources/HelpFiles/Toolbar/Chart_View/Matrix_Chart_View/ChartView_Matrix_Toolbar.png and /dev/null differ diff --git a/src/Resources/GuiResources/HelpFiles/Toolbar/Chart_View/Matrix_Chart_View/Matrix_Chart_View.html b/src/Resources/GuiResources/HelpFiles/Toolbar/Chart_View/Matrix_Chart_View/Matrix_Chart_View.html deleted file mode 100644 index 0fe62655f3208bcb5547dadbcbf27e563a1723d7..0000000000000000000000000000000000000000 --- a/src/Resources/GuiResources/HelpFiles/Toolbar/Chart_View/Matrix_Chart_View/Matrix_Chart_View.html +++ /dev/null @@ -1,97 +0,0 @@ - - - - - Matrix Chart View - - - Matrix Chart View
      - When Matrix is selected as the Chart Type, the - Toolbar appears - like this:
      -
      -
      -
        -
      • Select a Matrix chartable file in the Overlay Toolbox Charting - tab.
      • -
      -
        -
      • Matrix charts do not dynamically change, - instead they show data that is dynamically accessible for - viewing on the surface/volume in other viewing tabs.
      • -
      -
        -
      -
        -
      • When viewing a CIFTI - Parcel to parcel connectivity (.pconn.nii) file matrix, left - click on a row to select a parcel (will be outlined with a horizontal box - in the matrix). Parcel connectivity for - the selected parcel will be displayed on Viewing - - - - - - Tabs that are showing the parcel - connectivity file as a layer.
      • -
      -
        -
      • A parcel can also be - selected by clicking on a surface/volume in another - Viewing Tab and the corresponding parcel row will be - outlined in the matrix.
        -
      • - -
      -
      - -
      - - -
        -
      • When viewing a CIFTI Parcel Scalar (.pscalar.nii) file - matrix, the y-axis rows are parcels and the x-axis columns - are Map indices. When the map index is set in Overlay - - - - - Toolbox: Layers for any non-Chart - Viewing Tab, a vertical box appears in the matrix around - the column for the selected map index.
        -
      • -
      -
      -
      -
        -
      • Chart Attributes: To adjust - attributes of the matrix chart display, such as the cell - width and cell height. The default setting fills the - Viewing Area with the matrix cells. The Reset - button resets the cell size to the default.
      • -
      -
        -
      • Tab: These across Tab - functions do not affect Chart View.
        -

        -
      • -
      -
      - - diff --git a/src/Resources/GuiResources/HelpFiles/Toolbar/Chart_View/Matrix_Chart_View/pconn_matrix_montage.png b/src/Resources/GuiResources/HelpFiles/Toolbar/Chart_View/Matrix_Chart_View/pconn_matrix_montage.png deleted file mode 100644 index f803f94e9ffa10ff4438be14ced40764d599e109..0000000000000000000000000000000000000000 Binary files a/src/Resources/GuiResources/HelpFiles/Toolbar/Chart_View/Matrix_Chart_View/pconn_matrix_montage.png and /dev/null differ diff --git a/src/Resources/GuiResources/HelpFiles/Toolbar/Chart_View/Matrix_Chart_View/pscalar_matrix_surface.png b/src/Resources/GuiResources/HelpFiles/Toolbar/Chart_View/Matrix_Chart_View/pscalar_matrix_surface.png deleted file mode 100644 index 931be2b9d3388dcac4787c0c2d3c82c6f68adc31..0000000000000000000000000000000000000000 Binary files a/src/Resources/GuiResources/HelpFiles/Toolbar/Chart_View/Matrix_Chart_View/pscalar_matrix_surface.png and /dev/null differ diff --git a/src/Resources/GuiResources/HelpFiles/Toolbar/Chart_View/Montage_toolbar.png b/src/Resources/GuiResources/HelpFiles/Toolbar/Chart_View/Montage_toolbar.png deleted file mode 100755 index 965784a1d99fed0afb43e1da86f277f7adf5eaaf..0000000000000000000000000000000000000000 Binary files a/src/Resources/GuiResources/HelpFiles/Toolbar/Chart_View/Montage_toolbar.png and /dev/null differ diff --git a/src/Resources/GuiResources/HelpFiles/Toolbar/Chart_View/Thumbs.db b/src/Resources/GuiResources/HelpFiles/Toolbar/Chart_View/Thumbs.db deleted file mode 100755 index 17db5526fa2bd65911e16082729ebb74f39f1614..0000000000000000000000000000000000000000 Binary files a/src/Resources/GuiResources/HelpFiles/Toolbar/Chart_View/Thumbs.db and /dev/null differ diff --git a/src/Resources/GuiResources/HelpFiles/Toolbar/Montage_View/Montage_View.html b/src/Resources/GuiResources/HelpFiles/Toolbar/Montage_View/Montage_View.html deleted file mode 100644 index a45e12482455dbb52f3b2424577a04283d3c601f..0000000000000000000000000000000000000000 --- a/src/Resources/GuiResources/HelpFiles/Toolbar/Montage_View/Montage_View.html +++ /dev/null @@ -1,99 +0,0 @@ - - - - - Montage View - - - Montage View
      - Montage View displays the medial and lateral sides of left and - right hemisphere surfaces in one
      Viewing Tab. - In Montage View the Toolbar looks like this:
      -
      -
      -
        -
      • Orientation contains buttons to - set orthogonal and User-defined views of the brain surface - displayed.
      • -
      -
        -
          -
        • Orthogonal view buttons are labeled: LM - (lateral-medial), DV (dorsal-ventral), AP - (anterior-posterior). 
          -
        • -
        • Using the Mouse - - - - Controls, in Montage view, rotation is - mirrored between hemisphere lateral/medial sides and panning - moves hemisphere sides together or away from each other.
          -
        • -
        • The Reset button resets the - orientation/zoom to the default.
          -
        • -
        • Custom - - - - Orientation allows one to set and save (or - not) a specific transform (pan, rotate, oblique rotate, - zoom) for a surface or volume.
        • -
        -
      -
        -
      • Montage Selection contains - pull-downs and checkboxes to set the surface montage to view.
      • -
      -
        -
          -
        • Top Left pull-down sets the montage - structure: Cerebral Cortex, Cerebellar Cortex, or Flat. A - structure is only viewable if the appropriate surface is - loaded.
          -
        • -
        • Top Right pull-down sets the page - orientation of the surfaces displayed. Pull-downs in the - middle set the surface file for each hemisphere from the - list of loaded files. Up to 2 left and 2 right surfaces can - be displayed at once. The checkboxes indicate if the set - surfaces are being displayed.
          -
        • -
        • Left, Lateral, Medial, - and Right checkboxes indicate if these structures - are being displayed.
        • -
        -
      - -
        -
      • Clipping - contains settings for cutting down Surface, Volume, or - Features data to be viewed.
      • -
      -
        -
      • Tab - contains cross-tab functions for yoking the display of two or - more Viewing Tabs.
        -
      • -
      - - diff --git a/src/Resources/GuiResources/HelpFiles/Toolbar/Montage_View/Montage_toolbar.png b/src/Resources/GuiResources/HelpFiles/Toolbar/Montage_View/Montage_toolbar.png deleted file mode 100644 index 1dd41f6e7f0559846cab400defc5f20acd121c5e..0000000000000000000000000000000000000000 Binary files a/src/Resources/GuiResources/HelpFiles/Toolbar/Montage_View/Montage_toolbar.png and /dev/null differ diff --git a/src/Resources/GuiResources/HelpFiles/Toolbar/Montage_View/Thumbs.db b/src/Resources/GuiResources/HelpFiles/Toolbar/Montage_View/Thumbs.db deleted file mode 100755 index ba23ca41602fa213c5e714ca9f76e557b76ab16d..0000000000000000000000000000000000000000 Binary files a/src/Resources/GuiResources/HelpFiles/Toolbar/Montage_View/Thumbs.db and /dev/null differ diff --git a/src/Resources/GuiResources/HelpFiles/Toolbar/Surface_View/SurfaceView.png b/src/Resources/GuiResources/HelpFiles/Toolbar/Surface_View/SurfaceView.png deleted file mode 100644 index 944e124fc12977be94176e0702c8e779cb24d1e6..0000000000000000000000000000000000000000 Binary files a/src/Resources/GuiResources/HelpFiles/Toolbar/Surface_View/SurfaceView.png and /dev/null differ diff --git a/src/Resources/GuiResources/HelpFiles/Toolbar/Surface_View/Surface_View.html b/src/Resources/GuiResources/HelpFiles/Toolbar/Surface_View/Surface_View.html deleted file mode 100644 index c8c4b7b8379268bf8229a06ed9640a2708e5787c..0000000000000000000000000000000000000000 --- a/src/Resources/GuiResources/HelpFiles/Toolbar/Surface_View/Surface_View.html +++ /dev/null @@ -1,67 +0,0 @@ - - - - - Surface View - - - Surface View
      - Surface View displays the medial and lateral sides of left and - right hemisphere surfaces in one
      Viewing - Tab. - In Surface View the Toolbar looks like this:
      -
      -
      -
        -
      • Orientation contains buttons to - set orthogonal and User-defined views of the brain surface - displayed: L (lateral), M (medial), D (dorsal), - V (ventral), A - (anterior), and P (posterior). 
      • -
      -
        -
          -
        • The Reset button resets the - orientation/zoom to the default.
          -
        • -
        • Custom - - Orientation allows one to set and save (or - not) a specific transform (pan, rotate, oblique rotate, - zoom) for a surface or volume.
        • -
        -
      -
        -
      • Selection contains pull-downs to - set the brain structure (top) and surface (bottom) to view.
      • -
      - -
        -
      • Clipping - contains settings for cutting down Surface, Volume, or - Features data to be viewed.
      • -
      -
        -
      • Tab - contains cross-tab functions for yoking the display of two or - more Viewing Tabs.
        -
      • -
      - - diff --git a/src/Resources/GuiResources/HelpFiles/Toolbar/Surface_View/Thumbs.db b/src/Resources/GuiResources/HelpFiles/Toolbar/Surface_View/Thumbs.db deleted file mode 100755 index 90deb5aa231288a4dd0e46193c8202a710797b54..0000000000000000000000000000000000000000 Binary files a/src/Resources/GuiResources/HelpFiles/Toolbar/Surface_View/Thumbs.db and /dev/null differ diff --git a/src/Resources/GuiResources/HelpFiles/Toolbar/Toolbar.html b/src/Resources/GuiResources/HelpFiles/Toolbar/Toolbar.html deleted file mode 100644 index 51e27d445394fe4ce584a35d70b95378cee1e886..0000000000000000000000000000000000000000 --- a/src/Resources/GuiResources/HelpFiles/Toolbar/Toolbar.html +++ /dev/null @@ -1,73 +0,0 @@ - - - - - Toolbar - - - Toolbar
      - Toolbar is the section at the top of the Workbench - Window that contains viewing settings for the - structure you are displaying in the Active - Tab and access to - Workbench functional modes (border drawing, region of interest - definition, etc.). - -
      -
        - -
          -
        • The Toolbar can be hidden or shown - using the button at the top - right of the Workbench Window or by clicking on Toolbar in - the View Menu.
          -
        • -
        • Tooltips - (available by hovering mouse over buttons/elements) are an - easy way to see information on the buttons and functions - on the Toolbar (or throughout wb_view).
          -
        • -
        -  
        -

        -
        -
      - - diff --git a/src/Resources/GuiResources/HelpFiles/Toolbar/Volume_View/Thumbs.db b/src/Resources/GuiResources/HelpFiles/Toolbar/Volume_View/Thumbs.db deleted file mode 100755 index 5848d8f3b4fe600a11d3e52aa67e973e76ec4f48..0000000000000000000000000000000000000000 Binary files a/src/Resources/GuiResources/HelpFiles/Toolbar/Volume_View/Thumbs.db and /dev/null differ diff --git a/src/Resources/GuiResources/HelpFiles/Toolbar/Volume_View/VolumeView.png b/src/Resources/GuiResources/HelpFiles/Toolbar/Volume_View/VolumeView.png deleted file mode 100644 index 58a448ae09ee44f62e0fe46af539654bc3b5412f..0000000000000000000000000000000000000000 Binary files a/src/Resources/GuiResources/HelpFiles/Toolbar/Volume_View/VolumeView.png and /dev/null differ diff --git a/src/Resources/GuiResources/HelpFiles/Toolbar/Volume_View/Volume_ID_button.png b/src/Resources/GuiResources/HelpFiles/Toolbar/Volume_View/Volume_ID_button.png deleted file mode 100644 index 7411ae4c1c592bb434d19bec7b26bafc6cdfd5aa..0000000000000000000000000000000000000000 Binary files a/src/Resources/GuiResources/HelpFiles/Toolbar/Volume_View/Volume_ID_button.png and /dev/null differ diff --git a/src/Resources/GuiResources/HelpFiles/Toolbar/Volume_View/Volume_View.html b/src/Resources/GuiResources/HelpFiles/Toolbar/Volume_View/Volume_View.html deleted file mode 100644 index 4301c0d5dbfeffb854389cf4ecb2b70e8bc8c726..0000000000000000000000000000000000000000 --- a/src/Resources/GuiResources/HelpFiles/Toolbar/Volume_View/Volume_View.html +++ /dev/null @@ -1,132 +0,0 @@ - - - - - Volume View - - - Volume View
      - Volume View displays brain volume slices. In - Volume View the Toolbar looks like this:
      -
      -
      -
      -
        -
      • Slice - - - - - - Plane contains buttons to set which volume slice planes - are displayed: P (parasagittal), C (coronal), - A (axial), All (all 3 planes). 
      • -
      -
      -
        -
          -
        -
      -
      -
        -
      -
      -
      -
        -
      • The Reset button resets the - orientation/zoom to the default.
      • -
      -
      -
      -
        -
      • Custom - - - - - Orientation allows one to set and save (or - not) a specific transform (pan, rotate, oblique rotate, - zoom) for a surface or volume.
      • -
      -
      -
        -
      • Montage contains settings for - viewing multiple volume slices from a single slice plane at - once. Rows/Cols set the number of Rows or - Columns of slices to display. Step sets the index - spacing between slices in the volume slice montage. 
      • -
      -

      -
        - -
      • Slice Indices/Coords - contains - settings for the slice index and stereotaxic (Talairach) - coordinate to be viewed for each slice plane.  -
      • -
        -
      - -
      -
        -
      • The vertical Origin button - rests the slice indices to the default (centered at the - AC).
      • -
      -
      -
      -
      -
        - -
      • The  button (default on) - activates movement of the crosshairs/volume slice to the same - plane as the brainordinate selected in any of the Viewing - Tabs in the same yoking group as the Volume tab.
      • -
      -
      -
      -
        -
      • The pull-down at the bottom toggles - between Orthogonal and Oblique volume - viewing.
      • -
      -
      -
        -
      -
      -
        -
      -
      - -
        -
      • Clipping - contains settings for cutting down Surface, Volume, or - Features data to be viewed.
      • -
      -
        -
      • Tab - contains cross-tab functions for yoking the display of two or - more Viewing Tabs.
      • -
      - - diff --git a/src/Resources/GuiResources/HelpFiles/Workbench_Window/WBwindow_Horiz_Labels.png b/src/Resources/GuiResources/HelpFiles/Workbench_Window/WBwindow_Horiz_Labels.png deleted file mode 100755 index fe4c8b0509d99e0fd752a60e8dffe8c4b4a4e20f..0000000000000000000000000000000000000000 Binary files a/src/Resources/GuiResources/HelpFiles/Workbench_Window/WBwindow_Horiz_Labels.png and /dev/null differ diff --git a/src/Resources/Help/HelpFiles/Annotations/Annotations_1.3.fld/filelist.xml b/src/Resources/Help/HelpFiles/Annotations/Annotations_1.3.fld/filelist.xml new file mode 100644 index 0000000000000000000000000000000000000000..1d57342ef5214d6740b257da67b41f3372875abf --- /dev/null +++ b/src/Resources/Help/HelpFiles/Annotations/Annotations_1.3.fld/filelist.xml @@ -0,0 +1,43 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/src/Resources/Help/HelpFiles/Annotations/Annotations_1.3.fld/header.html b/src/Resources/Help/HelpFiles/Annotations/Annotations_1.3.fld/header.html new file mode 100644 index 0000000000000000000000000000000000000000..c9d89f1acd0a22c79e5cbf8b0f48881732b663e3 --- /dev/null +++ b/src/Resources/Help/HelpFiles/Annotations/Annotations_1.3.fld/header.html @@ -0,0 +1,116 @@ + + + + + + + + + + + + + + +
      + +

      + +


      + +

      + +
      + +
      + +

      + +


      + +

      + +
      + +
      + +

      + +


      + +

      + +
      + +
      + +

      + +


      + +

      + +
      + +
      + +
      + + + + + +
      +

      +
      + +
      + +

       

      + +
      + +
      + +
      + + + + + +
      +

      2

      +
      + +
      + +

       

      + +
      + + + + diff --git a/src/Resources/Help/HelpFiles/Annotations/Annotations_1.3.fld/image001.png b/src/Resources/Help/HelpFiles/Annotations/Annotations_1.3.fld/image001.png new file mode 100644 index 0000000000000000000000000000000000000000..579cba013a1fd15da057b6e029ba10fdb3717109 Binary files /dev/null and b/src/Resources/Help/HelpFiles/Annotations/Annotations_1.3.fld/image001.png differ diff --git a/src/Resources/Help/HelpFiles/Annotations/Annotations_1.3.fld/image002.png b/src/Resources/Help/HelpFiles/Annotations/Annotations_1.3.fld/image002.png new file mode 100644 index 0000000000000000000000000000000000000000..43aa24b1f6fefc940b0500290baa10cfb213a269 Binary files /dev/null and b/src/Resources/Help/HelpFiles/Annotations/Annotations_1.3.fld/image002.png differ diff --git a/src/Resources/Help/HelpFiles/Annotations/Annotations_1.3.fld/image003.png b/src/Resources/Help/HelpFiles/Annotations/Annotations_1.3.fld/image003.png new file mode 100644 index 0000000000000000000000000000000000000000..842b425d085c8054dcd1467635565e35d23eb49c Binary files /dev/null and b/src/Resources/Help/HelpFiles/Annotations/Annotations_1.3.fld/image003.png differ diff --git a/src/Resources/Help/HelpFiles/Annotations/Annotations_1.3.fld/image004.png b/src/Resources/Help/HelpFiles/Annotations/Annotations_1.3.fld/image004.png new file mode 100644 index 0000000000000000000000000000000000000000..77bb61cee0c5439cc44d53cd873e356e94fed3bc Binary files /dev/null and b/src/Resources/Help/HelpFiles/Annotations/Annotations_1.3.fld/image004.png differ diff --git a/src/Resources/Help/HelpFiles/Annotations/Annotations_1.3.fld/image005.png b/src/Resources/Help/HelpFiles/Annotations/Annotations_1.3.fld/image005.png new file mode 100644 index 0000000000000000000000000000000000000000..116c5666c841053b12f5a7ea38af75434fc4fd2b Binary files /dev/null and b/src/Resources/Help/HelpFiles/Annotations/Annotations_1.3.fld/image005.png differ diff --git a/src/Resources/Help/HelpFiles/Annotations/Annotations_1.3.fld/image006.png b/src/Resources/Help/HelpFiles/Annotations/Annotations_1.3.fld/image006.png new file mode 100644 index 0000000000000000000000000000000000000000..2ab80839ff47532695d645e957d06c7a989effe3 Binary files /dev/null and b/src/Resources/Help/HelpFiles/Annotations/Annotations_1.3.fld/image006.png differ diff --git a/src/Resources/Help/HelpFiles/Annotations/Annotations_1.3.fld/image007.png b/src/Resources/Help/HelpFiles/Annotations/Annotations_1.3.fld/image007.png new file mode 100644 index 0000000000000000000000000000000000000000..7de71cf7cbe305773b83ee7dd8e16bdb053f3bc3 Binary files /dev/null and b/src/Resources/Help/HelpFiles/Annotations/Annotations_1.3.fld/image007.png differ diff --git a/src/Resources/Help/HelpFiles/Annotations/Annotations_1.3.fld/image008.png b/src/Resources/Help/HelpFiles/Annotations/Annotations_1.3.fld/image008.png new file mode 100644 index 0000000000000000000000000000000000000000..a13630c5ffe9b7c993fd55e83cc65f4811ddbca9 Binary files /dev/null and b/src/Resources/Help/HelpFiles/Annotations/Annotations_1.3.fld/image008.png differ diff --git a/src/Resources/Help/HelpFiles/Annotations/Annotations_1.3.fld/image009.png b/src/Resources/Help/HelpFiles/Annotations/Annotations_1.3.fld/image009.png new file mode 100644 index 0000000000000000000000000000000000000000..c3b93f4e887f9ea0a5867b7805647b945a8689c5 Binary files /dev/null and b/src/Resources/Help/HelpFiles/Annotations/Annotations_1.3.fld/image009.png differ diff --git a/src/Resources/Help/HelpFiles/Annotations/Annotations_1.3.fld/image010.png b/src/Resources/Help/HelpFiles/Annotations/Annotations_1.3.fld/image010.png new file mode 100644 index 0000000000000000000000000000000000000000..e828658acde98c01faf7f0e226e1753fd0e301e7 Binary files /dev/null and b/src/Resources/Help/HelpFiles/Annotations/Annotations_1.3.fld/image010.png differ diff --git a/src/Resources/Help/HelpFiles/Annotations/Annotations_1.3.fld/image011.png b/src/Resources/Help/HelpFiles/Annotations/Annotations_1.3.fld/image011.png new file mode 100644 index 0000000000000000000000000000000000000000..6516015af3c88733354a4b3fa5e832e384c5844b Binary files /dev/null and b/src/Resources/Help/HelpFiles/Annotations/Annotations_1.3.fld/image011.png differ diff --git a/src/Resources/Help/HelpFiles/Annotations/Annotations_1.3.fld/image012.png b/src/Resources/Help/HelpFiles/Annotations/Annotations_1.3.fld/image012.png new file mode 100644 index 0000000000000000000000000000000000000000..6a2928a3a9080c3c4f27f62f7ce68fee19225df4 Binary files /dev/null and b/src/Resources/Help/HelpFiles/Annotations/Annotations_1.3.fld/image012.png differ diff --git a/src/Resources/Help/HelpFiles/Annotations/Annotations_1.3.fld/image013.png b/src/Resources/Help/HelpFiles/Annotations/Annotations_1.3.fld/image013.png new file mode 100644 index 0000000000000000000000000000000000000000..2f6075815c9df155afe458983bc65d94265e6d78 Binary files /dev/null and b/src/Resources/Help/HelpFiles/Annotations/Annotations_1.3.fld/image013.png differ diff --git a/src/Resources/Help/HelpFiles/Annotations/Annotations_1.3.fld/image014.png b/src/Resources/Help/HelpFiles/Annotations/Annotations_1.3.fld/image014.png new file mode 100644 index 0000000000000000000000000000000000000000..10d15547f78c7fa33856f7f2eee17685b43e5629 Binary files /dev/null and b/src/Resources/Help/HelpFiles/Annotations/Annotations_1.3.fld/image014.png differ diff --git a/src/Resources/GuiResources/HelpFiles/Annotations/Annotations.fld/image004.png b/src/Resources/Help/HelpFiles/Annotations/Annotations_1.3.fld/image015.png similarity index 100% rename from src/Resources/GuiResources/HelpFiles/Annotations/Annotations.fld/image004.png rename to src/Resources/Help/HelpFiles/Annotations/Annotations_1.3.fld/image015.png diff --git a/src/Resources/GuiResources/HelpFiles/Annotations/Annotations.fld/image005.png b/src/Resources/Help/HelpFiles/Annotations/Annotations_1.3.fld/image016.png similarity index 100% rename from src/Resources/GuiResources/HelpFiles/Annotations/Annotations.fld/image005.png rename to src/Resources/Help/HelpFiles/Annotations/Annotations_1.3.fld/image016.png diff --git a/src/Resources/GuiResources/HelpFiles/Annotations/Annotations.fld/image006.png b/src/Resources/Help/HelpFiles/Annotations/Annotations_1.3.fld/image017.png similarity index 100% rename from src/Resources/GuiResources/HelpFiles/Annotations/Annotations.fld/image006.png rename to src/Resources/Help/HelpFiles/Annotations/Annotations_1.3.fld/image017.png diff --git a/src/Resources/GuiResources/HelpFiles/Annotations/Annotations.fld/image007.png b/src/Resources/Help/HelpFiles/Annotations/Annotations_1.3.fld/image018.png similarity index 100% rename from src/Resources/GuiResources/HelpFiles/Annotations/Annotations.fld/image007.png rename to src/Resources/Help/HelpFiles/Annotations/Annotations_1.3.fld/image018.png diff --git a/src/Resources/GuiResources/HelpFiles/Annotations/Annotations.fld/image008.png b/src/Resources/Help/HelpFiles/Annotations/Annotations_1.3.fld/image019.png similarity index 100% rename from src/Resources/GuiResources/HelpFiles/Annotations/Annotations.fld/image008.png rename to src/Resources/Help/HelpFiles/Annotations/Annotations_1.3.fld/image019.png diff --git a/src/Resources/GuiResources/HelpFiles/Annotations/Annotations.fld/image009.png b/src/Resources/Help/HelpFiles/Annotations/Annotations_1.3.fld/image020.png similarity index 100% rename from src/Resources/GuiResources/HelpFiles/Annotations/Annotations.fld/image009.png rename to src/Resources/Help/HelpFiles/Annotations/Annotations_1.3.fld/image020.png diff --git a/src/Resources/GuiResources/HelpFiles/Annotations/Annotations.fld/image010.png b/src/Resources/Help/HelpFiles/Annotations/Annotations_1.3.fld/image021.png similarity index 100% rename from src/Resources/GuiResources/HelpFiles/Annotations/Annotations.fld/image010.png rename to src/Resources/Help/HelpFiles/Annotations/Annotations_1.3.fld/image021.png diff --git a/src/Resources/GuiResources/HelpFiles/Annotations/Annotations.fld/image011.png b/src/Resources/Help/HelpFiles/Annotations/Annotations_1.3.fld/image022.png similarity index 100% rename from src/Resources/GuiResources/HelpFiles/Annotations/Annotations.fld/image011.png rename to src/Resources/Help/HelpFiles/Annotations/Annotations_1.3.fld/image022.png diff --git a/src/Resources/Help/HelpFiles/Annotations/Annotations_1.3.fld/image023.png b/src/Resources/Help/HelpFiles/Annotations/Annotations_1.3.fld/image023.png new file mode 100644 index 0000000000000000000000000000000000000000..818aca104be328a81b71f6b532e70070cd267a94 Binary files /dev/null and b/src/Resources/Help/HelpFiles/Annotations/Annotations_1.3.fld/image023.png differ diff --git a/src/Resources/GuiResources/HelpFiles/Annotations/Annotations.fld/image012.png b/src/Resources/Help/HelpFiles/Annotations/Annotations_1.3.fld/image024.png similarity index 100% rename from src/Resources/GuiResources/HelpFiles/Annotations/Annotations.fld/image012.png rename to src/Resources/Help/HelpFiles/Annotations/Annotations_1.3.fld/image024.png diff --git a/src/Resources/GuiResources/HelpFiles/Annotations/Annotations.fld/image013.png b/src/Resources/Help/HelpFiles/Annotations/Annotations_1.3.fld/image025.png similarity index 100% rename from src/Resources/GuiResources/HelpFiles/Annotations/Annotations.fld/image013.png rename to src/Resources/Help/HelpFiles/Annotations/Annotations_1.3.fld/image025.png diff --git a/src/Resources/GuiResources/HelpFiles/Annotations/Annotations.fld/image015.png b/src/Resources/Help/HelpFiles/Annotations/Annotations_1.3.fld/image026.png similarity index 100% rename from src/Resources/GuiResources/HelpFiles/Annotations/Annotations.fld/image015.png rename to src/Resources/Help/HelpFiles/Annotations/Annotations_1.3.fld/image026.png diff --git a/src/Resources/Help/HelpFiles/Annotations/Annotations_1.3.fld/image027.png b/src/Resources/Help/HelpFiles/Annotations/Annotations_1.3.fld/image027.png new file mode 100644 index 0000000000000000000000000000000000000000..71f118ecc5796e1f2bdc366f52f596510d348422 Binary files /dev/null and b/src/Resources/Help/HelpFiles/Annotations/Annotations_1.3.fld/image027.png differ diff --git a/src/Resources/Help/HelpFiles/Annotations/Annotations_1.3.fld/image028.png b/src/Resources/Help/HelpFiles/Annotations/Annotations_1.3.fld/image028.png new file mode 100644 index 0000000000000000000000000000000000000000..3f65675a010d4814151ba82d36aa25b897e864f9 Binary files /dev/null and b/src/Resources/Help/HelpFiles/Annotations/Annotations_1.3.fld/image028.png differ diff --git a/src/Resources/GuiResources/HelpFiles/Annotations/Annotations.fld/image018.png b/src/Resources/Help/HelpFiles/Annotations/Annotations_1.3.fld/image029.png similarity index 100% rename from src/Resources/GuiResources/HelpFiles/Annotations/Annotations.fld/image018.png rename to src/Resources/Help/HelpFiles/Annotations/Annotations_1.3.fld/image029.png diff --git a/src/Resources/Help/HelpFiles/Annotations/Annotations_1.3.fld/image030.png b/src/Resources/Help/HelpFiles/Annotations/Annotations_1.3.fld/image030.png new file mode 100644 index 0000000000000000000000000000000000000000..92655d09e68b3c2d775cea77a36bfa77e7e3c1f6 Binary files /dev/null and b/src/Resources/Help/HelpFiles/Annotations/Annotations_1.3.fld/image030.png differ diff --git a/src/Resources/Help/HelpFiles/Annotations/Annotations_1.3.fld/image031.png b/src/Resources/Help/HelpFiles/Annotations/Annotations_1.3.fld/image031.png new file mode 100644 index 0000000000000000000000000000000000000000..2221970ed47466e5ac7169afef51cd6f83aeee36 Binary files /dev/null and b/src/Resources/Help/HelpFiles/Annotations/Annotations_1.3.fld/image031.png differ diff --git a/src/Resources/Help/HelpFiles/Annotations/Annotations_1.3.fld/image032.png b/src/Resources/Help/HelpFiles/Annotations/Annotations_1.3.fld/image032.png new file mode 100644 index 0000000000000000000000000000000000000000..9665df1d0b7b865a45e08344264a09055ca83c1c Binary files /dev/null and b/src/Resources/Help/HelpFiles/Annotations/Annotations_1.3.fld/image032.png differ diff --git a/src/Resources/Help/HelpFiles/Annotations/Annotations_1.3.fld/image033.png b/src/Resources/Help/HelpFiles/Annotations/Annotations_1.3.fld/image033.png new file mode 100644 index 0000000000000000000000000000000000000000..9f10b114486ea08517b9481da87692ff52e3f855 Binary files /dev/null and b/src/Resources/Help/HelpFiles/Annotations/Annotations_1.3.fld/image033.png differ diff --git a/src/Resources/Help/HelpFiles/Annotations/Annotations_1.3.fld/image034.png b/src/Resources/Help/HelpFiles/Annotations/Annotations_1.3.fld/image034.png new file mode 100644 index 0000000000000000000000000000000000000000..237c3638a9e3abd9a0d9e9ed671851b2cab950bb Binary files /dev/null and b/src/Resources/Help/HelpFiles/Annotations/Annotations_1.3.fld/image034.png differ diff --git a/src/Resources/Help/HelpFiles/Annotations/Annotations_1.3.fld/image035.png b/src/Resources/Help/HelpFiles/Annotations/Annotations_1.3.fld/image035.png new file mode 100644 index 0000000000000000000000000000000000000000..8b1cc291abaa918c605d813dd5c6c9e05bbae4b6 Binary files /dev/null and b/src/Resources/Help/HelpFiles/Annotations/Annotations_1.3.fld/image035.png differ diff --git a/src/Resources/Help/HelpFiles/Annotations/Annotations_1.3.fld/item0001.xml b/src/Resources/Help/HelpFiles/Annotations/Annotations_1.3.fld/item0001.xml new file mode 100644 index 0000000000000000000000000000000000000000..348b072e7ff39f4c3a9707720e64774ad6c8e0ad --- /dev/null +++ b/src/Resources/Help/HelpFiles/Annotations/Annotations_1.3.fld/item0001.xml @@ -0,0 +1 @@ + diff --git a/src/Resources/Help/HelpFiles/Annotations/Annotations_1.3.fld/props002.xml b/src/Resources/Help/HelpFiles/Annotations/Annotations_1.3.fld/props002.xml new file mode 100644 index 0000000000000000000000000000000000000000..9f6d8c9ca9945b53434b74f43c512838240a26b7 --- /dev/null +++ b/src/Resources/Help/HelpFiles/Annotations/Annotations_1.3.fld/props002.xml @@ -0,0 +1,2 @@ + + \ No newline at end of file diff --git a/src/Resources/Help/HelpFiles/Annotations/Annotations_1.3.fld/themedata.thmx b/src/Resources/Help/HelpFiles/Annotations/Annotations_1.3.fld/themedata.thmx new file mode 100644 index 0000000000000000000000000000000000000000..bb4c689b430fe90bd33b98ee0e85cb91f265f3d7 Binary files /dev/null and b/src/Resources/Help/HelpFiles/Annotations/Annotations_1.3.fld/themedata.thmx differ diff --git a/src/Resources/Help/HelpFiles/Annotations/Annotations_1.3.html b/src/Resources/Help/HelpFiles/Annotations/Annotations_1.3.html new file mode 100644 index 0000000000000000000000000000000000000000..dbe1e2cbecdd750a9dd0200df43d9298a06001d0 --- /dev/null +++ b/src/Resources/Help/HelpFiles/Annotations/Annotations_1.3.html @@ -0,0 +1,5781 @@ + + + + + + + + + + + + + + + + + + + + + +
      + +

      Guide to WB Annotations

      + +

       

      + +

      07 May 2018

      + +

       

      + +

      + +
      +
      + +

       

      + + +

      Table of Contents

      +

      Understanding the Need + for Locking the Aspect Ratio. 6

      +

      Scenes. 8

      +

      Annotation Toolbar and + Example Annotations. 9

      +

      Annotation Coordinate + Spaces. 11

      +

      Chart Data Space (Ch). 11

      +

      Stereotaxic Coordinate + Space (St). 11

      +

      Surface Coordinate Space + (Sf). 11

      +

      Tab Coordinate Space (T). 12

      +

      Window Coordinate Space + (W). 12

      +

      Types of Annotations. 13

      +

      Box. 13

      +

      Color Bars. 14

      +

      Image. 14

      +

      Line. 14

      +

      Oval 14

      +

      Text. 14

      +

      Annotation Properties. 15

      +

      Annotation Line Width, + Line Color, and Fill Color. 15

      +

      Annotation Line Arrow + Tips. 16

      +

      Annotation Text + Characters. 16

      +

      Annotation Text Font + Property. 17

      +

      Annotation Text Alignment + Property. 17

      +

      Annotation Text + Orientation.. 18

      +

      Annotation Coordinate + Property. 18

      +

      Annotation Width and + Height. 19

      +

      Annotation Rotation + Property. 19

      +

      Annotation Files. 19

      +

      Disk Annotation File. 19

      +

      Scene Annotation File. 20

      +

      Selecting Annotations. 20

      +

      Editing Annotation + Position, Size, and Rotation. 21

      +

      Editing (Redo and Undo). 22

      +

      Inserting Annotations. 22

      +

      Deleting Annotations. 24

      +

      Format 24

      +

      Context Sensitive + (Pop-up) Menu. 25

      +

      Edit Menu. 27

      +

      Display Control of + Annotations. 28

      +

      Options. 28

      +

      Selection of Annotations + in Features Toolbox. 29

      +

      Content of Annotations + Tab in Features Toolbox. 29

      +

      Annotation Grouping. 30

      +

      Mouse Selection of + Annotations. 30

      +

      Creating a User Group.. 31

      +

      Removing a User Group.. 31

      +

      Recreating a User Group.. 31

      +

      Color Bar Editing. 31

      +

      Location and Positioning. 32

      +

      Data Numerics. 33

      +

       

      +

      +
      + +

      + +

      Figure 1: Single Tab View................................................................................................... 5

      + +

      Figure 2: Tile Tabs View..................................................................................................... 6

      + +

      Figure 3: Annotations on Surface........................................................................................ 7

      + +

      Figure 4: Width Increased with Unlocked Aspect Ratio...................................................... 8

      + +

      Figure 5: Width increased with a Locked Aspect Ratio........................................................ 8

      + +

      Figure 6: Figure from A multimodal parcellation of human cerebral cortex +(Glasser et al).. 9

      + +

      Figure 7: Window with Annotation ToolBar..................................................................... 10

      + +

      Figure 8: Surface with Annotations................................................................................... 13

      + +

      Figure 9: Window Width Increased with Unlocked Aspect Ratio...................................... 13

      + +

      Figure 10: Window Width Increased After Locking Aspect Ratio..................................... 13

      + +

      Figure 11: Line Width and Line/Fill Color Controls.......................................................... 16

      + +

      Figure 12: Line Arrow Tip Controls.................................................................................. 16

      + +

      Figure 13: Text Characters and Offset (Surface Space only) Controls............................... 17

      + +

      Figure 14: Text Font Attributes.......................................................................................... 17

      + +

      Figure 15: Text Alignment Controls.................................................................................. 17

      + +

      Figure 16: Text Orientation Controls................................................................................. 18

      + +

      Figure 17: Coordinate Controls (Chart, Stereotaxic, Tab, Window).................................. 18

      + +

      Figure 18: Annotation Coordinate (Chart, Stereotaxic, Tab, or Window).......................... 18

      + +

      Figure 19: Surface Coordinate Controls............................................................................. 18

      + +

      Figure 20: Width and Height Controls............................................................................... 19

      + +

      Figure 21: Rotation Control............................................................................................... 19

      + +

      Figure 22: Box in Red that is Selected for Editing............................................................. 21

      + +

      Figure 23: Editing Redo and Undo Controls...................................................................... 22

      + +

      Figure 24: Insert New Annotation Controls....................................................................... 22

      + +

      Figure 25: Annotation File Selection Controls................................................................... 23

      + +

      Figure 26: Delete Annotation Control................................................................................ 24

      + +

      Figure 27: Format Menu Items.......................................................................................... 24

      + +

      Figure 28: Features ToolBox Annotation Controls............................................................ 28

      + +

      Figure 29: Overlay ToolBox.............................................................................................. 32

      + +

      Figure 30: Overlay and Map Settings Color Bar Controls................................................. 32

      + +


      + 

      + +

       

      + +

      + +

      Figure 1: Single Tab View

      + +

       

      + +

       

      + +

      + +

      Figure 2: Tile Tabs View

      + +

      Understanding the Need for Locking the Aspect Ratio

      + +

       

      + +

      To create an optimal view of +a brain model, the user may adjust the size of the window and optionally remove +the Toolbar, Overlay Toolbox, and the Features Toolbox.  As the windowÕs shape changes, the size +of displayed brain model is adjusted to best fill the available space.  For a cerebral cortex model in a default +lateral view, the height of the cortex is set to be slightly smaller than the +height of its available area in the window.  If this causes parts of the cortex to +exceed the width of the available are, the cortex is shrunk so that all of it +is visible. 

      + +

       

      + +

      Allowing an independent width +and height for the window is known as a variable aspect ratio.  An aspect ratio is the ratio of height +to width (height divided by width).  +Due to this variable aspect ratio, annotations in Tab and Window +Coordinate Space use a special two-dimensional coordinate system consisting of +percentages.  In this percentage +coordinate system, the range for X-percentage coordinates range from 0% at the +left edge of the graphics region to 100% at the right edge.  Correspondingly, Y-percentage +coordinates range from 0% at the bottom edge to 100% at the top edge.  Thus, if one places an annotation at an +X-coordinate of 100%, this annotation will always be at the right edge of the +window, regardless of the windowÕs width.

      + +

       

      + +

      While this percentage +coordinate system works well with a variable aspect ratio, it has a major shortcoming.  If one places an annotation in Tab or +Window Space over an anatomical feature of a brain model and increases the +width (or height) of the window, the annotation may ÒdriftÓ and no longer overlay +the anatomical feature.  This anomaly +occurs because the size of the brain model does not increase in the same +proportion as the size of the window.  +To correct this problem, wb_view provides (and strongly recommends) +locking of the aspect ratio.  When +aspect locking is enabled, the aspect ratio at the time of locking is saved and +used when drawing brain models.  If +the window is changed to a size that does not result in the same aspect ratio +as the locked aspect ratio, padding is added to the horizontal or vertical +sides of the window to limit the graphics region in a way that matches the +locked aspect ratio.   For +example, suppose one creates two annotations in tab coordinate space, a red +oval and a fuchsia box as in Figure 3.  Now suppose one increases +the width of the window while the aspect ratio is unlocked.  Notice that the annotations no longer +overlay the original anatomical regions, Figure 4.  However, if one locks the +aspect ratio and then increases the width of the window, padding is added to the +side of the window to restrict the model to an area with the aspect ratio from +the time of locking, Figure 5.

      + +

       

      + +

       

      + +

      + +

      Figure 3: Annotations on Surface

      + +

       

      + +

      + +

      Figure 4: Width Increased with Unlocked Aspect Ratio

      + +

       

      + +

       

      + +

      + +

      Figure 5: Width increased with a Locked Aspect Ratio

      + +

       

      + +

       

      + +

       

      + +

       

      + +

      In conclusion, locking the +aspect ratio should always be performed prior to creating annotations.  In addition, once the aspect ratio is +locked, it should never be unlocked.  +Otherwise, it will cause annotations to drift from their desired +locations.

      + +

       

      + +

      Scenes

      + +

       

      + +

      Closely related to +annotations are scenes.  Scenes allow +one to preserve and reproduce Ôthe stateÕ of wb_view and are frequently used to +produce figures for publications.    Scenes can be shared with +collaborators or with the neuroscience community through the BALSA +Database.  Annotations are +frequently featured in scenes and allow one to highlight items of in the data +and present auxiliary information (Figure +7).  Annotations are +typically saved within a scene which eliminates the management of an external +data file (although annotations may be stored in external data files).

      + +

       

      + +

      + +

      Figure 6: Figure from A multimodal parcellation of human cerebral cortex (Glasser +et al)

      + +

       

      + +

       

      + +

       

      + +

       

      + +

      Annotation Toolbar and Example Annotations

      + +

       

      + +

      + +

      Figure 7: Window with Annotation ToolBar

      + +

       
      +

      + +

       

      + +

      Annotation Coordinate Spaces

      + +

       

      + +

      Several coordinate spaces are +available for annotations.  The +coordinate spaces control the movement of placement of annotations.

      + +

       

      + +

      Chart Data Space (Ch)

      + +

       

      + +

      An annotation in chart data +space is displayed within the char data plot and is invalid outside of the data +plot.  The coordinate range is +identical to the X- and Y-axis of the displayed chart data.  This is the range of data in the chart +changes, an annotation in chart data space will move.

      + +

       

      + +

      Stereotaxic Coordinate Space (St)

      + +

       

      + +

      An annotation in stereotaxic +space will appear in any display of a surface or a volume.  A particular annotation in stereotaxic +space may appear in more than one location such as when viewing a surface +montage.

      + +

       

      + +

      Stereotaxic space (Talairach is an example of a specific stereotaxic space) is +the three-dimensional coordinate system in which surface and volume models are +displayed.  In Workbench, the +stereotaxic space is in an LPI orientation (negative X is Left, negative Y is +Posterior, and negative Z is Inferior).  +Annotations in Stereotaxic Space will follow the model as it is panned, +rotated, or zoomed.  At times, +annotations in Stereotaxic Space may not be visible when they are behind a +model.  Annotations in Stereotaxic +Space are always drawn in a plane parallel to the screen.

      + +

       

      + +

      Surface Coordinate Space (Sf)

      + +

       

      + +

      An annotation in surface +coordinate space will appear in any view of the surface to which the annotation +is attached.  When a surface space +is used, the annotation is associated with a surface by the combination of the +surfaceÕs structure (ie: left cerebral cortex) and a +vertex index.  An optional offset +vector is used to improve visibility of the annotation (so not ÔbehindÕ the +surface).

      + +

       

      + +

      Tab Coordinate Space (T)

      + +

       

      + +

      An annotation in tab coordinate +space is displayed in a specific tab (uses tab number).  When Tile Tabs is enabled, all of the +windowÕs tabs are displayed in a grid-like pattern.  Tab Coordinate Space is a +two-dimensional space with the X- and Y-coordinates expressed in percentages +ranging from 0% to 100%.  For the +X-coordinate, 0% is on the left and 100% is on the right.  For the Y-coordinate, 0% is at the +bottom and 100% is at the top. 

      + +

      If Tile Tabs is enabled, the +coordinate range spans only cell allocation to that tab in the graphics +region.  When the tabÕs aspect is +locked, the locations of 0% and 100% may be offset from the graphics regions +edges so that annotations remain at a consistent location over an underlying +brain model.

      + +

       

      + +

      Window Coordinate Space (W)

      + +

       

      + +

      The window coordinate functions +nearly identically to the tab coordinate space except that the coordinates +always span the entire graphics region.  +Tile Tabs has no effect on window coordinates. 

      + +

       

      + +

      Aspect Locking for Tab and +Window Coordinate Spaces

      + +

       

      + +

      The graphics region in +wb_view defaults to a variable aspect ratio.  With a variable aspect ratio, the user +may independently adjust the width and height of the window.  Since the tab and window coordinate +spaces use a percentage coordinate system, annotations in these spaces will +move away from the center as the width or height is increased.  Conversely, the annotation will move +towards the center as the width or height is decreased.  For example, in Figure 9, a red oval and a fuchsia square annotation are displayed over a left +hemisphere.  In one stretches the +width of the window with an unlocked aspect ratio, the two annotations move +away from the center but remain at the same percentage width coordinate, Figure 10.  In contrast, if one locks +the aspect ratio and expands the width of the window, padding is added to the +sides so that the percentage coordinates remain limited to the original aspect +ratio, Figure 11.  The result of aspect +locking is that the annotations remain over the same anatomical features as the +windowÕs size changes.

      + +

       

      + +

       

      + +

      + +

      Figure 8: Surface with Annotations

      + +

         

      + +

      + +

      Figure 9: Window Width Increased with Unlocked +Aspect Ratio

      + +

      + +

      Figure 10: Window Width Increased After Locking +Aspect Ratio

      + +

       

      + +

      Types of Annotations

      + +

       

      + +

      Box

      + +

       

      + +

      A box annotation is +positioned with one coordinate located at the center of the box.  The boxÕs width and height are +ÔnormalizedÕ relative to the size of width and height of the tab or window +containing the box.  Thus, a size of +100% will fill the tab/window horizontally.  Boxes possess both Fill and Line colors +and one or both may be used.

      + +

       

      + +

      Color Bars

      + +

       

      + +

      Color bars are a special case +of annotations that pictorially describe the mapping of numeric data into a +color palette.  Color bars are not +created like other annotation types but are enabled for display by pressing the +color bar button for a row in the Overlay ToolBoxÕs +Layers tab.  Positioning of color +bars is performed automatically unless the user chooses to manually position +them.  Color bars are limited to Tab +and Window spaces.  Editing of color +bars is described at the end of this document.

      + +

       

      + +

      Image

      + +

       

      + +

      An image annotation is +positioned with one coordinate located at the center of the image.  The height of image is specified as a +percentage of the region containing the image.  As the region changes in height, a +corresponding change will occur in the height of the image.  The width of the image will scale with +the height of the image using the aspect ratio of the image when it was +associated with the orientation.

      + +

       

      + +

      Line

      + +

       

      + +

      A line annotation contains +two coordinates with one located at each end point.  Lines are colored using the Line Color +only.  Arrows may be added to each +of the end points.

      + +

       

      + +

      Oval

      + +

       

      + +

      With the exception of the its +shape, an ovalÕs functionality is identical to a box.

      + +

       

      + +

      Text

      + +

       

      + +

      Text annotations contain one +coordinate and the text alignment properties control the offset of the text +relative to the coordinate.  Text characters +are drawn in the Font Color.  The +Line color, if enabled, draws a box around the text.  The File color, if enabled, draws a +background behind the text.  The +height of text is specified as a percentage of the regionÕs height containing +the text.  As the region changes in +height, a corresponding change will occur in the size of the text.

      + +

       

      + +

      Annotation Properties

      + +

       

      + +

       

      + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
      +

      Property

      +
      +

      Box

      +
      +

      Color Bar

      +
      +

      Image

      +
      +

      Line

      +
      +

      Oval

      +
      +

      Text

      +
      +

      Arrow Tips

      +
      +

       

      +
      +

       

      +
      +

       

      +
      +

      Y

      +
      +

       

      +
      +

       

      +
      +

      Line Width

      +
      +

      Y

      +
      +

       

      +
      +

       

      +
      +

      Y

      +
      +

      Y

      +
      +

      Y

      +
      +

      Line Color

      +
      +

      Y

      +
      +

       

      +
      +

       

      +
      +

      Y

      +
      +

      Y

      +
      +

      Y

      +
      +

      Fill Color

      +
      +

      Y

      +
      +

      Y

      +
      +

       

      +
      +

       

      +
      +

      Y

      +
      +

      Y

      +
      +

      Text Color

      +
      +

       

      +
      +

      Y

      +
      +

       

      +
      +

       

      +
      +

       

      +
      +

      Y

      +
      +

      Text Characters

      +
      +

       

      +
      +

      Y

      +
      +

       

      +
      +

       

      +
      +

       

      +
      +

      Y

      +
      +

      Font Name

      +
      +

       

      +
      +

      Y

      +
      +

       

      +
      +

       

      +
      +

       

      +
      +

      Y

      +
      +

      Font Size

      +
      +

       

      +
      +

      Y

      +
      +

       

      +
      +

       

      +
      +

       

      +
      +

      Y

      +
      +

      Font Style

      +
      +

       

      +
      +

       

      +
      +

       

      +
      +

       

      +
      +

       

      +
      +

      Y

      +
      +

      Horizontal Alignment

      +
      +

       

      +
      +

       

      +
      +

       

      +
      +

       

      +
      +

       

      +
      +

      Y

      +
      +

      Vertical Alignment

      +
      +

       

      +
      +

       

      +
      +

       

      +
      +

       

      +
      +

       

      +
      +

      Y

      +
      +

      Orientation

      +
      +

       

      +
      +

       

      +
      +

       

      +
      +

       

      +
      +

       

      +
      +

      Y

      +
      +

      Width

      +
      +

      Y

      +
      +

      Y

      +
      +

      Y

      +
      +

       

      +
      +

      Y

      +
      +

       

      +
      +

      Height

      +
      +

      Y

      +
      +

      Y

      +
      +

      Y

      +
      +

       

      +
      +

      Y

      +
      +

       

      +
      +

      Rotation

      +
      +

      Y

      +
      +

      Y

      +
      +

      Y

      +
      +

      Y

      +
      +

      Y

      +
      +

      Y

      +
      + +

       

      + +

       

      + +

      Annotation Line Width, Line Color, and Fill Color

      + +

       

      + +

      The Line Width Property controls +the line width of Line annotations and the outline width of Box, Oval, and Text +annotations.  Line widths are +expressed as a percentage of the height of the region containing the annotation.  When multiple annotations are selected +with different line widths, the smallest selected line width is displayed +followed by a plus symbol (+).

      + +

       

      + +

      The Line Color Property +controls the outline color of a Box, Oval or Text.  To change the color, one presses the +small arrow to display a menu containing the available colors.  The two special selections are Custom +and None.  Choosing Custom displays +a dialog that allow the user to create a color by adjusting red, green, and +blue color components.  None inhibits +the drawing of the associated line.

      + +

       

      + +

      The Fill Color Property controls +the background color for a Box, Oval, or Text annotation and its functionality +is the same as the Line Color property.

      + +

       

      + +

      + +

      Figure 11: Line Width and Line/Fill Color Controls

      + +

       

      + +

      Annotation Line Arrow Tips

      + +

       

      + +

      + +

      Figure 12: Line Arrow Tip Controls

      + +

       

      + +

      The Annotation Line Arrow +Tips section controls the addition of arrows to the end points of a line +annotation.  The top arrow button +adds an arrow to the lineÕs start and the bottom arrow button adds an arrow to +the lineÕs end.

      + +

      Annotation Text Characters

      + +

       

      + +

      The Text section allows the +user to edit the text of the selected Text Annotation (when one and only one +annotation is selected).  Multiple +lines of text are separated by a Ò\nÓ character sequence.  The combo box in the second row allows +the user to connect a text annotation in Surface Space to the surface vertex +with a line or an arrow.  When +connecting a text annotation to the surface, the offset in the surface +coordinate often needs adjustment.  +In addition, one may double-click the text annotation to edit the text +in a multi-line editor.

      + +

       

      + +

      + +

      Figure 13: Text Characters and Offset (Surface Space +only) Controls

      + +

       

      + +

      Annotation Text Font Property

      + +

       

      + +

      Font Properties associated +with Text Annotations include the Font Name, the Font Size, the Text Color and +options for Bold, Italic, and Underline.  +When multiple annotations are selected, the first matching font and smallest +font size is selected.  The Bold, +Italic, and Underline buttons are selected only if all selected text +annotations have the attribute enabled.

      + +

       

      + +

      + +

      Figure 14: Text Font Attributes

      + +

       

      + +

      Annotation Text Alignment Property

      + +

       

      + +

      Text Alignment Properties provide +options for both horizontal (top row) and vertical alignment (bottom row) of +text.  Horizontal options allow left-aligned +text, center-aligned text, or right-aligned text.  Vertial +options allow for top-aligned text, middle-aligned text, or bottom-aligned +text.

      + +

       

      + +

      When there are multiple text +annotations selected with different alignments, none of the alignment buttons appear +in selected state.

      + +

       

      + +

      + +

      Figure 15: Text Alignment Controls

      + +

       

      + +

      Annotation Text Orientation

      + +

       

      + +

      The text orientation property +includes two selections, Horizontal and Stacked.   Text characters drawn in +Horizontal Orientation are drawn in a left-to-right sequence.  Text characters drawn in Stacked +Orientation are drawn in a top-to-bottom sequence.  If there are multiple text annotations +selected with differing orientations, neither of the orientation buttons is +shown as selected.

      + +

       

      + +

      + +

      Figure 16: Text Orientation Controls

      + +

       

      + +

       

      + +

      Annotation Coordinate Property

      + +

       

      + +

      With the exception of Surface +Space, the coordinates for annotations are specified with XYZ-coordinates.  If the annotation is in Surface Space, +selections are provided for the Structure, Vertex Index, Offset Mode (C or N), +and Offset Distance.  The Centroid +(C) Offset Mode uses a vector from the centroid of the surface through the +vertex.  The Normal (N) Mode offsets +using the vertexÕs normal vector.  Line +annotations contain coordinates for each endpoint and all other annotations use +a one coordinate.

      + +

       

      + +

      + +

      Figure 17: Coordinate Controls (Chart, Stereotaxic, +Tab, Window)

      + +

      Figure 18: Annotation Coordinate (Chart, Stereotaxic, +Tab, or Window)

      + +

       

      + +

      + +

      Figure 19: Surface Coordinate Controls

      + +

       

      + +

      Located on the left side of +the coordinate controls are alphanumeric characters that display an abbreviated +name for the coordinate space of the selected annotation(s).  If all of the selected annotations are +in the same coordinate space, the letter will be Ch +(Chart Data), Sf (Surface), St (Stereotaxic) T (Tab, followed by tab numbers), +and W (Window).  If multiple annotations +are selected and they are in different coordinate spaces, a plus symbol (+) is +displayed.  The coordinate controls +are enabled only when a single annotation is selected.

      + +

      Annotation Width and Height

      + +

       

      + +

      The Annotation Width and Height +Properties control the width and height of Box, Image, and Oval +Annotations.   Both the width +and height are normalized values that range from 0% to 100% where 100% is the +full width/height of the tab or window.  +For image annotations, adjusting the width (or height) will also change +the height (or width) of the image so that the imageÕs aspect ratio is +preserved to prevent distortion of the image.

      + +

       

      + +

      + +

      Figure 20: Width and Height Controls

      + +

       

      + +

      Annotation Rotation Property

      + +

       

      + +

      The Annotation Rotation property +controls the rotation annotations.  +The rotation value is in degrees and a positive value rotates clockwise.

      + +

       

      + +

      + +

       

      + +

       

      + +

       

      + +

      Annotation Files

      + +

       

      + +

      There two mechanisms for +saving annotations.

      + +

      Disk Annotation File

      + +

       

      + +

      Disk Annotation Files can be +opened, saved, added to a Spec File just like other Workbench data files.

      + +

       

      + +

      Scene Annotation File

      + +

       

      + +

      The Scene Annotation File is +maintained Òin memoryÓ and when the user creates a Scene, these annotations +become part of the scene.  Thus, +these annotations can only be displayed when the scene is loaded.  A special entry is provided on the +Manage/Save Files Window to remove all active Scene Annotations.

      + +

       

      + +

      Selecting Annotations

      + +

       

      + +

      To select a single annotation +while in Annotations mode, simply move the mouse over the annotation (the cursor +will become a Òfour arrowsÓ symbol) and click the mouse.

      + +

       

      + +

      To select multiple +annotations, hold down the Shift Key while clicking the mouse over an +annotation.  When the mouse is +clicked with the Shift Key down, any other selected annotations remain selected. 

      + +


      +If an annotation that is a member of a group is selected, all annotations in +the group become selected. 

      + +

       

      + +

      To deselect all annotations, +click the mouse in a region where there is no annotation.  If there are multiple annotations +selected and one wants to deselect an annotation, click the mouse over the +annotation while holding down the Shift Key.

      + +

       

      + +

      When an annotation is in the +selected state, sizing handles are displayed around the annotation.  For a Line Annotation, sizing handle +symbols are drawn at the end points with a slightly larger square symbol at the +first coordinate (head of the arrow).  +For a Box or Oval Annotation, a box is drawn in the foreground color with +sizing handles distributed about the box.  +When the mouse is moved over a sizing handle, the mouse cursor will +change to a symbol that indicates how moving the size handle with the mouse +will change the shape of the annotation.

      + +

       

      + +

      Editing Annotation Position, Size, and Rotation

      + +

       

      + +

      To move an annotation, first +select the annotation and move the mouse over the annotation so that the cursor +is the four arrows symbol.  Hold +down the left mouse button and then drag the mouse to move the annotation to +its new location.

      + +

       

      + +

      To resize a line annotation, +first select the line annotation.  +Next, move the mouse over a sizing handle at either end of the line so +that the cursor becomes a Òtwo arrowÓ symbol.  Now, hold down the left mouse button and +drag the line end point to its new location.

      + +

       

      + +

      To resize a box or oval +annotation, first select the annotation.  +Next, move the mouse over one of the sizing symbols so that the cursor +becomes a Òtwo arrowÓ symbol.  Now, +hold down the left mouse button and drag to change the width and/or height of +the shape.  Dragging the circular +symbol at the corners changes both the width and height of the shape.  Dragging the square symbol on a side +changes width or height but not both.

      + +

       

      + +

      + +

      Figure 22: Box in Red that is Selected for Editing

      + +

       

      + +

      To rotate a box, image, oval, +or text annotation, first select the annotation.  Next, move the mouse over the small rotation +handle at the top of the shape (small black circle attached to box around TEXT) +at which time the cursor will become a rotation symbol.  Now, to rotate the annotation, hold down +the left mouse button and drag the mouse away from the annotation to the +desired orientation. The rotation handle will always point towards the mouse +pointer so it is important that the mouse is moved away from the +annotation.  If the mouse is over +the annotation, rotation will be erratic and difficult.

      + +

       

      + +

       

      + +

       

      + +

       

      + +

      Annotations may also be +moved, resized, and rotated using the annotation toolbarÕs controls while a +shape is selected.

      + +

       

      + +

      Editing (Redo and Undo)

      + +

       

      + +

       

      + +

      Figure 23: Editing Redo and Undo Controls

      + +

       

      + +

      When editing annotations, the +user may mistakenly modify or delete an annotation.  For these instances, pressing the Undo +button will ÒundoÓ the last change to an annotation.  Redo and Undo can also be used with +grouping operations (described later in this document).

      + +

      Inserting Annotations

      + +

       

      + +

      + +

      Figure 24: Insert New Annotation Controls

      + +

       

      + +

       

      + +

      + +

      Figure 25: Annotation File Selection Controls

      + +

       

      + +

       

      + +

      When creating annotations, +new annotations are placed in either ÒScene AnnotationsÓ or an Annotation File +stored to disk.  Scene Annotations +are not saved to an annotation file but instead are added to a scene when a a new scene is created.  Note that the user will need to save the +scene file.  Disk annotation files +are saved and opened like other data files.

      + +

       

      + +

      To create a new annotation, one +must first choose the file that will contain the newly created annotation (the +default is Scene Annotations).  Once +the file is selected, it is not necessary to select a file unless one wants to +change the file.  Second, the user +must choose the Space from the top row of buttons (the selected space is +highlighted).  Third, the user must click +one of the type buttons in the bottom row to choose the type of annotation that +is created.  Lastly, the user moves +the mouse into the graphics region at which time the mouse pointer becomes a +small ÔplusÕ symbol.  The user must +either click the mouse in the graphics region to insert a default annotation +for the type or drag the mouse to create a rectangular region that bounds the +new annotation.  If the location of +the new annotation is incompatible with the selected annotation space, a dialog +pops up that allows the user to choose a different space or to cancel creation +of the annotation. When entering a text annotation, a dialog will always pop-up +for entry of the text.

      + +

       

      + +

      BEWARE that when drawing +annotations in surface space, the annotation may move a short distance from +where it was drawn.  This is caused +by the surface offset attribute of the surface coordinate.  While the annotation is attached to the +surface vertex closest to the mouse click, the annotation is offset by a +vector.  It is this offset vector +that causes the annotation to move.  +Without the offset vector, surface annotations may be partially or even +fully obscured by the surface.

      + +

      Deleting Annotations

      + +

       

      + +

      + +

      Figure 26: Delete Annotation Control

      + +

       

      + +

      Individual annotations are +deleted while in Annotation Mode by clicking the Trash Can Icon in the +Annotation Toolbar.  One may remove +an entire file of Annotations on the Manage/Save Files window.

      + +

       

      + +

      Format

      + +

       

      + +

      + +

      Figure 27: Format Menu Items

      + +

       

      + +

      The arrange menu is used to +arrange and format multiple annotations.  +At least two annotations must be selected for an alignment +operation.  At least three +annotations must be selected for a distribute operation.  Alignment and Distribution operations +may only be performed on annotations in Tab or Window space.

      + +

       

      + +

      á      Align Left Ð The selected annotations are moved so +that the left edge of each annotation is at the same X-coordinate as the +left-most annotation.

      + +

      á      Align Center Ð The selected annotations are moved so +that center X-coordinate of each annotation is at the average X-coordinate of +the annotations.

      + +

      á      Align Right Ð The selected annotations are moved so +that the right edge of each annotation is at the same X-coordinate as the +right-most annotation.

      + +

      á      Align Top - The selected annotations are moved so that +the top edge of each annotation is at the same Y-coordinate as the top-most annotation.

      + +

      á      Align Middle Ð The selected annotations are moved so +that the center Y-coordinate of each annotation is at the average Y-coordinate +of the annotations.

      + +

      á      Align Bottom - The selected annotations are moved so +that the bottom edge of each annotation is at the same Y-coordinate as the +bottom-most annotation.

      + +

      á      Distribute Horizontally Ð The left-most and right-most +annotations do not move.  The other +annotations are moved so there is equal horizontal spacing between adjacent +annotations.

      + +

      á      Distribute Vertically - The top-most and bottom-most +annotations do not move.  The other +annotations are moved so there is equal vertical spacing between adjacent +annotations.

      + +

      á      Group - See the description in Annotation Grouping.

      + +

      á      Regroup - See the description in Annotation Grouping.

      + +

      á      Ungroup - See the description in Annotation Grouping.

      + +

       

      + +

      Context Sensitive (Pop-up) Menu

      + +

       

      + +

      A pop-up menu is available when +the Mode (mouse) is Annotate.  This +menu is displayed when the user places the mouse over an annotation and +right-clicks the mouse (Control key held down with mouse click on Mac).  Items in the menu are enabled only in +certain conditions.

      + +

       

      + +

      Items in the pop-up menu are:

      + +

      á      Cut Ð Removes the annotation under the mouse and +places it onto the annotation clipboard.

      + +

      á      Copy Ð Copies the annotation under the mouse and +places it onto the annotation clipboard.

      + +

      á      Delete Ð Deletes the annotation under the mouse.

      + +

      á      Duplicate to Tab Ð When this item is enabled, it +displays a menu listing the tabs to which the annotation may be pasted.

      + +

      á      Paste Ð Inserts a copy of the annotation on the +clipboard at the mouse location.  If +the mouse location is incompatible with the annotationÕs space, a dialog is +pops up to change the space or cancel the paste operation.

      + +

      á      Paste and Change Space Ð Pops up a dialog to choose +the space and then pastes a copy of the annotation from the clipboard in the +selected space.

      + +

      á      Select All Ð Selects all annotations displayed in the +window.

      + +

      á      Edit Text Ð Pops up a dialog for changing the text in +a text annotation.

      + +

      á      Turn Off Stereotaxic/Surface Annotation Display in +Other Tabs Ð The  +display of the selected annotations is turned off in all tabs +except the tab in which this menu items was selected.

      + +

      á      Turn On Stereotaxic/Surface +Annotation Display in All Tabs Ð The display of the selected annotations is turned +on in all tabs.

      + +

      á      Turn On Stereotaxic/Surface +Annotation Display in All Groups Ð The display of the selected annotations is +turned on in all Display Groups (A, B, C, D).

      + +

      á      Turn On Stereotaxic/Surface +Annotation Display in Only in Group Ð Allows selection of a Display Group and +the selected annotations are set to only display in the selected Display Group.

      + +

      á      Group Ð See the description in Annotation Grouping.

      + +

      á      Ungroup Ð See the description in Annotation Grouping.

      + +

      á      Regroup Ð See the description in Annotation Grouping.

      + +

       

      + +

      Edit Menu

      + +

       

      + +

      The WindowÕs Edit Menu is +available for some operations.

      + +

       

      + +

      á      Undo Ð Rolls back the last annotation modification (additional +text is added to the Undo item providing information about the modification).

      + +

      á      Redo Ð Rolls back the last ÒundoÓ (additional text is +added to the Redo item providing information about the modification).

      + +

      á      Cut Ð See Cut under Context Sensitive (Pop-up) Menu.

      + +

      á      Copy Ð See Copy under Context Sensitive (Pop-up) Menu.

      + +

      á      Paste Ð See Paste under Context Sensitive (Pop-up) +Menu.

      + +

      á      Paste Special Ð See Paste and Change Space under +Context Sensitive (Pop-up) Menu.

      + +

      á      Delete Ð See Delete under Context Sensitive (Pop-up) +Menu.

      + +

      á      Select All Ð Selects all annotations displayed in the +window.

      + +
      +
      + +

       

      + +

      Display Control of Annotations

      + +

       

      + +

      + +

      Figure 28: Features ToolBox +Annotation Controls

      + +

       

      + +

      Controlling the display of +annotation is performed using the Annot section in +the Features Toolbox.

      + +

       

      + +

      Options

      + +

       

      + +

      Display Annotations Ð Enables +and disables the display of all annotation types in all open windows.

      + +

       

      + +

      Display Text Annotations Ð +Enables and disables the display of text annotations in all open windows.  Note that the Display Annotation +checkbox affects all annotations including text annotations.

      + +

       

      + +

      Show Window Annotations in +Single Tab View Ð When checked, annotations in Window space are always +displayed.  When unchecked, +annotations in Window space are only displayed when Tile Tabs is enabled.

      + +

       

      + +

      Group Ð Group +allows the user to easily switch between several different display selections +of annotations. 

      + +

       

      + +

      The display selections for +annotations in window space function different than other spaces.  Because the ÔGroupÕ selection is a +property of each browser window ÒtabÓ, it is not available when drawing Window +annotations.  Therefore, there is +only one display selection for annotations in window space.

      + +

       

      + +

      Selection of Annotations in Features Toolbox

      + +

       

      + +

      To select an annotation in +the Features Toolbox, the user clicks the mouse over the name of the +annotation.  The annotation name is selected and all other annotations are +deselected.  The selected annotation is highlighted in the graphics +region.  The user may select multiple annotations in the features toolbox +by holding down the CTRL (Apple) key or the Shift key.  If the CTRL key is +held down while the mouse is clicked, the annotation under the mouse is added +to the selected annotations.  If the Shift key is held down while the +mouse is clicked, all annotation from the last selected annotation to the +annotations under the mouse are selected.

      + +

       

      + +

      If the user clicks the name +of either group type, Space or User, all annotations in the group are selected. + If the user selects the name of annotation that is in a User Group, all +annotations in the User Group are selected.

      + +

       

      + +

       

      + +

      Content of Annotations Tab in Features Toolbox

      + +

       

      + +

      In the Features Toolbox, +annotations are grouped into a hierarchy of File, Group, and Annotations. + The hierarchy is collapsed and expanded using the small triangles on the +left side of the annotation listing.  There are three checkbox states, unchecked, +partially checked, and checked.  An annotation is either unchecked (not +displayed) or checked (is displayed).  An Annotation Group (and File) is +unchecked when all annotations in the group (file) are unchecked (not +displayed); partially checked when some, but not all annotations in the group +are checked (displayed), and the group (file) is checked when all annotations +in the group are checked (displayed).

      + +

       

      + +

      For each annotation in the +features toolbox, an icon (small image) is displayed between the checkbox and +the annotationÕs name.  This icon may consist of several colors.  If +the annotation's ÔFill ColorÕ is enabled, the background of the icon is the +fill color.  If the annotationÕs line color is enabled, the edges of the +icon are drawn in the line color.  If the annotation is a text annotation, +a small square consisting of the text characterÕs color is in the center of +icon.

      + +

       

      + +

      Annotation Grouping

      + +

       

      + +

      Annotations are organized +into a hierarchy consisting of File->Group->Annotation.  There are +two types of groups: Space Groups and User Groups.  By default, an +annotation is assigned to a Space Group.  The user may move annotations +from a Space Group to a User Group by performing a ÔGroupÕ operation. + Annotations may also be moved from a User Group to a Space Group by +performing an ÔUngroupÕ operation. 

      + +

       

      + +

      Both types of groups may only +contain annotations that are in the same coordinate space.  Stereotaxic +annotations are assigned to one group.  Surface annotations are also in +one group.  There is a group for each tab and a group for each window.

      + +

       

      + +

      Mouse Selection of Annotations

      + +

       

      + +

      To select an annotation with +the mouse, the user simply clicks the mouse over an annotation.  The +annotation under the mouse is selected and any other annotations are +deselected.  To select multiple annotations the user clicks the annotation +with the mouse while the Shift key is held down.  To deselect all +annotations, the user clicks the mouse in an empty region (not over an +annotation).

      + +

       

      + +

      If the user selects an +annotation with the mouse and the annotation is in a User Group, all +annotations in the User Group are selected.

      + +

       

      + +

       

      + +

      Creating a User Group

      + +

       

      + +

      Creation of a user group is +allowed when two or more annotations are selected and all of the selected +annotations are in the same Space Group.  If these conditions are met, the +user may create a User Group by selected Group from the Annotation ToolbarÕs +Format->Arrange Menu.  The user may also right click the mouse and +select Group from the pop-up menu.

      + +

       

      + +

      Removing a User Group

      + +

       

      + +

      To remove a User Group, the +annotations in the User Group must be selected (recall that clicking any +annotation in a User Group selects all annotations in the User Group). + The annotations are returned to a Space Group by selecting +Ungroup from the Annotation ToolbarÕs Format->Arrange Menu.  The +user may also right click the mouse and select Ungroup from the pop-up menu.

      + +

       

      + +

       

      + +

      Recreating a User Group

      + +

       

      + +

      To recreate a User Group, the +user must select at least one annotation that is currently in a Space Group but +was previously in a User Group.  If these conditions are met, the user may +recreate the User Group by selected Regroup from the Annotation ToolbarÕs +Format->Arrange Menu.  The user may also right click the mouse and +select Regroup from the pop-up menu.  All annotations that were previously +in the User Group are moved back to the User Group.

      + +

       

      + +

       

      + +

      Color Bar Editing

      + +

       

      + +

      Each overlay contains one +color bar that is available when the selected overlay is mapped using a color +palette (typically for floating point data).  To show a color bar, press the rainbow +button under Settings in Overlay ToolBox->Layers.  To Edit the color bar, press the wrench +icon.

      + +

       

      + +

       

      + +

      + +

      Figure 29: Overlay ToolBox

      + +

       

      + +

       

      + +

      + +

      Figure 30: Overlay and Map Settings Color Bar +Controls

      + +

       

      + +

      Location and Positioning

      + +

       

      + +

      This section controls the +coordinate space and location within the space (tab or window) + for the color bar.

      + +

       

      + +

      Show Color Bar in Sets the allowable space for the color bar.  Tab +restricts the color bar to the region in which the tabÕs data is drawn.  Window +allows drawing of the color bar in an area of the window.

      + +


      +
      +

      + +

       

      + +

      Positioning  When Automatic +is selected, the color bar is positioned in the bottom left corner of the +tab/window. If there is more than one color bar, they are vertically stacked.  +Manual  allows the user to +manually position the color bar by selecting the color bar (click it in +Annotations Editing Mode) and dragging it with the mouse or using the +coordinate controls in the annotationÕs editing toolbar.  It is not necessary to switch the +Positioning Mode to Manual to move the color bar.  Simply select the color bar and drag it +with the mouse and the Positioning will immediately change to Manual.

      + +

       

      + +

      Data Numerics

      + +

       

      + +

      Data Mode +Controls format of numeric above color bar.  When Data is selected, the numeric text above the color bar reflects +numerical values within the data.  +With Percentile +the values above the color bar are percentages within the range of data.  When Sign Only is selected, a plus and/or minus indicate the polarity of +the data.

      + +

       

      + +

      Numeric Format +Controls the formatting of numeric text above the color bar.  With Auto, the choice of decimal or scientific notation and digits right +of the decimal point is automatically chosen to best represent the data values.  Decimal +format ensures the data is in a decimal format.  Scientific +displays the data using scientific notation.

      + +

       

      + +

      Dec/Sci Decimals Enabled when Numeric Format is Decimal or Scientific +and controls the number of digits right of the decimal point.

      + +

       

      + +

      Subdivisions +By default, the numeric text above the color bar consists of a negative value, +zero, and a positive value.  +Subdivisions is used to add additional values between zero and +negative/positive.

      + +

       

      + +

      Show Tick Marks +If checked, a small tick mark is displayed below each numeric text value to +indicate its exact position in the color bar.

      + +

       

      + +

       

      + +

      References:

      + +

       

      + +

      Glasser MF, Coalson TS, +Robinson EC, et al. A multi-modal parcellation of +human cerebral cortex. Nature. +2016;536(7615):171-178. doi:10.1038/nature18933.

      + +

       

      + +
      + + + + diff --git a/src/Resources/Help/HelpFiles/Best_Practices_Guides/Annotation_and_Scenes_Best_Practices.html b/src/Resources/Help/HelpFiles/Best_Practices_Guides/Annotation_and_Scenes_Best_Practices.html new file mode 100644 index 0000000000000000000000000000000000000000..351600f8d0b7f29b8f1418df07691f71a8a2a6da --- /dev/null +++ b/src/Resources/Help/HelpFiles/Best_Practices_Guides/Annotation_and_Scenes_Best_Practices.html @@ -0,0 +1,39 @@ + + + + + + Annotation and Scenes Best Practices + + + +

      Annotations and Scenes Best Practices

      +

      Summary

      +

      1. Verify data files are in a common directory tree and file names are reasonable.
      2. If a multi-tab scene will be created, enter Tile Tabs (View Menu->Enter Tile Tabs).
      3. Adjust size of window.
      4. Setup view of the models (pan/rotate/zoom).
      5. Lock Aspect Ratio (click Lock Aspect button in right side of Toolbar).
      6. Enter Annotations Mode (click Annotate button in Toolbar).
      7. Add Annotations.
      8. Display the Scene Dialog (click Clapboard icon in Toolbar or select Window Menu->Scenes) and add a Scene (click Add button on Scene Dialog).
      9. Save the Scene File. Click Save As button the first time to set the path and name of the scene file. Once path and name of scene file has been set, use the Save button.
      10. Repeat last few steps to edit annotations and create additional scenes.
      11. Be sure to save the Scene File (click Save button or Save As if Save is disabled at top of Scene Dialog).

      +

      Introduction

      +

      Describing annotations and scenes is best done using an analogy with Microsoft PowerPoint. PowerPoint allows one to create a presentation consisting of slides saved into a PowerPoint file. These slides are annotated with text and various shapes. In wb_view, a scene is analogous to a PowerPoint slide and a Scene File contains multiple scenes just like a PowerPoint file contains multiple slides. In wb_view, one annotates a scene similar to the way one annotates a slide in PowerPoint. A Scene saves ‘the state’ of wb_view so that when the scene is loaded at a later time, by either the creator of the scene or another user, the user-interface and the orientation of the brain models replicates ‘the state’ at the time the scene was created.

      +

      When creating scenes and annotations, following a set of “best practices†ensures that one efficiently creates scenes that are properly displayed at a later time and when shared with other users.

      +

      Best Practices Details

      +

      1. Verify data files are in a common directory tree and file names are reasonable. Scenes contain the directory path and filename for each data file that is loaded at the time the scene is created. Once a scene has been created, moving or renaming a data file referenced by the scene will cause an error when the scene is displayed. In addition, if data files are scattered about many directories, this may cause problems when zipping a scene file and its data files or uploading a scene file to the BALSA database.

      +

      2. If a multi-tab scene will be created, enter Tile Tabs (View Menu->Enter Tile Tabs). Tile Tabs displays all of the window's tabs in a grid pattern. The number of row and columns in the Tile Tabs view is edited on the Tile Tabs Configuration Dialog (View Menu->Tile Tabs Configuration->Create and Edit). Advanced parameters include “stretch factors†that allow one to increase the size of a row or column relative to other rows and columns. One may also save the configuration for future use. If a scene is created, the configuration is added to the scene so that the configuration is available when the scene is displayed.

      +

      3. Adjust size of window. It is important to adust the size of the window prior to entering Annotations Mode (further down in this list).

      +

      4. Setup view of the models. Pan (drag mouse with SHIFT key down), Rotate (drag mouse), Zoom (drag mouse with Control Key. Use the Command Key on Mac.

      +

      5. Lock Aspect Ratio (click Lock Aspect button in right side of Toolbar). As a general rule, once the aspect ratio is locked, it should remain locked. The aspect ratio is the ratio of height divided by width. Annotations in tab and window spaces are positioned using percentage coordinates that range from 0% to 100% with 0% at the left (x) and bottom (y) and 100% at the right (x) and top (y). If the window is resized with the aspect ratio unlocked and there is a change in the aspect ratio, annotations in tab/window space may no longer appear in the correct location (such as over an anatomical feature). Locking the aspect ratio adds padding to the left and right or bottom and top of the graphics region so that the model’s region maintains the same aspect ratio (ratio of height to width). This ensures that annotations remain in the correct location relative to the viewed brain model.

      +

      6. Enter Annotations Mode (click Annotate button in Toolbar).

      +

      7. To add annotations, click any of the Space and/or Type icons in the Insert New section of the Annotation Toolbar and then click in graphics region to create the annotation. A new annotation is automatically selected so that it can be moved/resized with the mouse or its attributes changed using controls in the toolbar.

      +

      8. Display the Scene Dialog (click Clapboard icon in Toolbar or select Window Menu->Scenes) and add a Scene (click Add button on Scene Dialog). In the Create New Scene Dialog, edit the name and description for the new scene and then press the OK button. Note that by default, new annotations are added to the current scene and stored in the scene file. If one wants to place annotations into a disk file, click the small arrow to the right of the Scene button in the Insert New section of the Toolbar. When the arrow is clicked, a menu is displayed for creating and/or selecting a disk annotation file.

      +

      9. Save the Scene File. Click Save As button the first time to set the path and name of the scene file. Once path and name of scene file has been set, use the Save button.To set the name of the New Scene File, click the Save As button. In the file selection dialog, choose the directory, and enter a descriptive name for the Scene File. It is best for the Scene File to be in the same directory or the parent directory of the directory containing your data files. If the data files are scattered about many directories, it may cause problems if the Scene File and its data files are zipped for distribution to others or when uploading the BALSA Database

      +

      10. Repeat last few steps to edit annotations and create additional scenes.

      +

      11. If the Save button at the top of the Scene Dialog is enabled, the Scene File is in a modified state. Click the Save button to save the Scene File and avoid losing your work.

      +

      Understanding the Tab and Window Coordinate Spaces

      +

      In PowerPoint, each slide maintains a fixed aspect ratio (the ratio of height to width remains constant) with a spatial coordinate system (typically inches). As a result, each annotation in PowerPoint is places its annotions at a spatial coordinate (X and Y in inches). In contrast, wb_view defaults to a variable aspect ratio so that the window is resizable to best fit the brain model (surface, volume, chart). This variable aspect ratio is problematic for annotations in Tab or Window Space both of which use a percentage coordinate system (0% at left/bottom and 100% at right/top).

      +

      If one creates a Tab Space annotation with the variable aspect enabled and changes the size of the window, the Tab Space annotation may no longer be in the correct location. To correct this problem, wb_view supports “locking of the aspect ratioâ€. When the aspect ratio is locked, the window (and each tab within the window) are fixed to the aspect ratio at the time aspect locking is enabled. If the size of the window changes while the aspect is locked, the contents of window and each tab will expand or contract in size but maintain a fixed aspect by adding space to sides of the model when needed.

      +

      Scenes

      +

      Scenes are closely related to annotations. Scenes allow one to preserve 'the state’ of wb_view. Thus one if one saves a scene, exits wb_view, later restarts wb_view, and loads the scene, wb_view appears identical to when the scene was originally saved. Scenes are frequently used for figures in publications. Scenes are also uploaded to the BALSA database for sharing with other users.

      +

      While annotations can be saved in disk files, annotations are most commonly saved to a scene. When creating annotations for a scene, it is a best practice to save the annotations to the scene instead of creating a disk annotation file.

      + + + + diff --git a/src/Resources/Help/HelpFiles/ChartingOverview/ChartingOverview.html b/src/Resources/Help/HelpFiles/ChartingOverview/ChartingOverview.html new file mode 100644 index 0000000000000000000000000000000000000000..3eb820f268ada2f005bb440304539124de069e5f --- /dev/null +++ b/src/Resources/Help/HelpFiles/ChartingOverview/ChartingOverview.html @@ -0,0 +1,89 @@ + + + + + Charting Overview + + +
      +

      Charting Overview

      +
      + With the release of 1.3.0, charting has been completely revised. The + previous version of charting is still available by selecting the + Chart Old in Toolbar->View.  Any scenes created with the + older version of charting should display correctly in Chart + Old.  In addition, wb_view will attempt to convert old versions + of charting to the new charting functionality.
      +
      + Charting has expanded to include the display of histograms in + addition to the pre-existing support for matrices and line-series + data.  An important feature of Charting is the simultaneous + display of multiple charts for the selected chart type. In the + Toolbar, the Chart Layers tab provides the selection of charts, as + layers, similar to Layers used in the Overlays for surfaces and + volumes.
      +
      +

      Histogram Charts

      +
      + Histogram charts are available for any data file that contain one or + more maps of scalar data and this includes many CIFTI file types, + GIFTI metric files, and volume files.  A histogram shows the + distribution of data within a single map with an option to average + the data from all maps.  This map contain the same data that is + mapped to brainordinates in surface and volume views.  To view + a histogram chart, change Toolbar->View mode to Chart and select + Histogram in Toolbar->Chart Type.  In Toolbox->Chart + Layers, enable an overlay using an On checkbox and then choose a + file and map for display as a histogram.  Multiple histograms + can be displayed using multiple layers.  When displaying more + than one histogram, one may want to change the histogram chart from + Bars to Envelope by clicking the Layer's wrench button, selecting + the Palette tab, and selecting the appropriate check boxes in the + Histogram section.  There are a variety of additional options + for controlling the display of histograms.
      +
      +
      +
      +
      +
      +

      Line-Series Charts

      +
      + Line-Series charts are available for most data files that contain + multiple maps of scalar data.  A line-series chart shows one + data element from each map for a selected brainordinate (surface + vertex or volume voxel).  To display line-series charts from a + data-file, one must enable the layer by checking the On checkbox and + enable loading of line charts from the data-file by checking the + Load checkbox.  Note that the Load checkbox is a property of + the file so if the same file is in more than one layer, the Load + checkbox will maintain the same checked status in these + layers.  Once loading is enabled for a file, one must identify + brainordinates by clicking the mouse on a surface vertex or a volume + voxel.  Doing so will load the data for the selected + brainordinate and display the data as a line-chart.  A history + of line-charts loaded from a data file is available on in the Lines + section of the Overlay and Map Settings dialog (click the Wrench + button in the row containing the layer).  One sets the number + of chart lines displayed along with editing properties that include + the color and thickness of the chart lines.
      +
      +
      +
      +
      +
      +

      Matrix Charts

      +
      + Matrix charts are available for a limited number of files that + contain parcellated connectivity.  By default the full matrix + is displayed.  If the matrix is square (number of rows in the + data file matches the number of columns) a button in the Layer + controls the display of the matrix as the full matrix or the lower + or upper quadrant.  The palette mapping for coloring the matrix + is identical to that used for display of the file's data in surface + and volume overlays.
      +
      +
      +
      +
      + + diff --git a/src/Resources/Help/HelpFiles/ChartingOverview/HistogramChart.png b/src/Resources/Help/HelpFiles/ChartingOverview/HistogramChart.png new file mode 100644 index 0000000000000000000000000000000000000000..6e2d3242531d980d7fe10103e469035f7cf86be7 Binary files /dev/null and b/src/Resources/Help/HelpFiles/ChartingOverview/HistogramChart.png differ diff --git a/src/Resources/Help/HelpFiles/ChartingOverview/HistogramControl.png b/src/Resources/Help/HelpFiles/ChartingOverview/HistogramControl.png new file mode 100644 index 0000000000000000000000000000000000000000..1e83b716568f4683fa9170706d581a76ee111d04 Binary files /dev/null and b/src/Resources/Help/HelpFiles/ChartingOverview/HistogramControl.png differ diff --git a/src/Resources/Help/HelpFiles/ChartingOverview/LineSeriesChart.png b/src/Resources/Help/HelpFiles/ChartingOverview/LineSeriesChart.png new file mode 100644 index 0000000000000000000000000000000000000000..d2c7d6f99ea61a8d6f58c080e56e77264bed62e6 Binary files /dev/null and b/src/Resources/Help/HelpFiles/ChartingOverview/LineSeriesChart.png differ diff --git a/src/Resources/Help/HelpFiles/ChartingOverview/LineSeriesHistory.png b/src/Resources/Help/HelpFiles/ChartingOverview/LineSeriesHistory.png new file mode 100644 index 0000000000000000000000000000000000000000..e8cedc8a6f2ee3afea50f02be6e5aed695a32f12 Binary files /dev/null and b/src/Resources/Help/HelpFiles/ChartingOverview/LineSeriesHistory.png differ diff --git a/src/Resources/Help/HelpFiles/ChartingOverview/MatrixChart.png b/src/Resources/Help/HelpFiles/ChartingOverview/MatrixChart.png new file mode 100644 index 0000000000000000000000000000000000000000..d962f990e5001ed0d2b7245410024398c5c19138 Binary files /dev/null and b/src/Resources/Help/HelpFiles/ChartingOverview/MatrixChart.png differ diff --git a/src/Resources/Help/HelpFiles/Features_Toolbox/Annotations/Annotations.fld/filelist.xml b/src/Resources/Help/HelpFiles/Features_Toolbox/Annotations/Annotations.fld/filelist.xml new file mode 100644 index 0000000000000000000000000000000000000000..ff7b3a76eb379125fecb76266493a506c293b3a1 --- /dev/null +++ b/src/Resources/Help/HelpFiles/Features_Toolbox/Annotations/Annotations.fld/filelist.xml @@ -0,0 +1,6 @@ + + + + + + \ No newline at end of file diff --git a/src/Resources/Help/HelpFiles/Features_Toolbox/Annotations/Annotations.fld/image001.png b/src/Resources/Help/HelpFiles/Features_Toolbox/Annotations/Annotations.fld/image001.png new file mode 100644 index 0000000000000000000000000000000000000000..8583d395218776b5d0f3592d0bbf2747f64b2d27 Binary files /dev/null and b/src/Resources/Help/HelpFiles/Features_Toolbox/Annotations/Annotations.fld/image001.png differ diff --git a/src/Resources/Help/HelpFiles/Features_Toolbox/Annotations/Annotations.fld/themedata.thmx b/src/Resources/Help/HelpFiles/Features_Toolbox/Annotations/Annotations.fld/themedata.thmx new file mode 100644 index 0000000000000000000000000000000000000000..87e8520bab1913e2793103510f54475816746406 Binary files /dev/null and b/src/Resources/Help/HelpFiles/Features_Toolbox/Annotations/Annotations.fld/themedata.thmx differ diff --git a/src/Resources/Help/HelpFiles/Features_Toolbox/Annotations/Annotations.html b/src/Resources/Help/HelpFiles/Features_Toolbox/Annotations/Annotations.html new file mode 100644 index 0000000000000000000000000000000000000000..35769aaf7d43defad3381d22270a5c38073cde7e Binary files /dev/null and b/src/Resources/Help/HelpFiles/Features_Toolbox/Annotations/Annotations.html differ diff --git a/src/Resources/Help/HelpFiles/Features_Toolbox/Annotations/annotations.png b/src/Resources/Help/HelpFiles/Features_Toolbox/Annotations/annotations.png new file mode 100644 index 0000000000000000000000000000000000000000..8583d395218776b5d0f3592d0bbf2747f64b2d27 Binary files /dev/null and b/src/Resources/Help/HelpFiles/Features_Toolbox/Annotations/annotations.png differ diff --git a/src/Resources/Help/HelpFiles/Features_Toolbox/Borders/Borders.html b/src/Resources/Help/HelpFiles/Features_Toolbox/Borders/Borders.html new file mode 100644 index 0000000000000000000000000000000000000000..42da41c5f3af9545192c8c14f9f2a32014bf2354 --- /dev/null +++ b/src/Resources/Help/HelpFiles/Features_Toolbox/Borders/Borders.html @@ -0,0 +1,205 @@ + + + + + Borders + + +

      Borders
      +
      The Borders Tab in the Features Toolbox + contains options for display and selection of loaded and newly + created borders on brain surfaces. 

      +
        +
      • Display + + Borders toggles border display on and off. When + Border Mode + is turned on, Display Borders is turned on by default.
      • +
      +
        +
      • Group: identifies the Border group for the Active + Tab. Tabs assigned to the same Group will display the same + borders with the same attributes.
      • +
      +
        +
      • Borders + + Attributes contains options for viewing borders.
      • +
      +
        +
          +
        • Contralateral check box toggles on display of borders + that are currently being displayed on one hemisphere to be + displayed on the contralateral hemisphere (on top of that + hemisphere's own displayed borders, if any).
        • +
        +
      +
        +
          +
        • Draw As sets the shape of the border + components (Lines, Spheres, Squares, Spheres and Lines).
        • +
        +
      +
        +
          +
        • Coloring sets the Class, Name, or Standard Color as + the border color source.
        • +
        +
      +
        +
          +
        • Standard Color Selects the color when Coloring is set to Standard + Color.
        • +
        +
      +
        +
          +
        • Line Diameter controls the border thickness + if line is selected as the border component.
        • +
        +
      +
        +
          +
        • Symbol Diameter controls the size of the + sphere or square symbol border components.
        • +
        +
      +
        +
          +
        • Unstretched Lines is used to correctly display a + border from a 3D surface on a flat surface when the border + crosses a surface cut from flattening. The distance + indicates the limit for how much a border can be stretched + across a cut surface before it is NOT displayed. + Specifically, if the value of [(length of border on flat + surface) / (length of border of 3d volume-interaction + surface)] exceeds the Unstretched + Lines value then the border is NOT displayed.
        • +
        • Above Offset is + used to move the borders above the surface with a positive + value
        • +
        +
      +


      +
      Border Attributes Image

      +
        +
      • Borders + + Selection controls group and individual display + selection for borders. If a higher level + group is selected on or off, all borders below that level will + be similarly selected on/off. The All On/Off buttons allow for + quick toggling of all borders on or off.
      • +
      +
      +
      + + diff --git a/src/Resources/Help/HelpFiles/Features_Toolbox/Borders/Borders_Attributes.png b/src/Resources/Help/HelpFiles/Features_Toolbox/Borders/Borders_Attributes.png new file mode 100644 index 0000000000000000000000000000000000000000..de953ab991b755cf949b30984390bc7c31ec7b0d Binary files /dev/null and b/src/Resources/Help/HelpFiles/Features_Toolbox/Borders/Borders_Attributes.png differ diff --git a/src/Resources/GuiResources/HelpFiles/Features_Toolbox/Borders/Borders_Selection.png b/src/Resources/Help/HelpFiles/Features_Toolbox/Borders/Borders_Selection.png similarity index 100% rename from src/Resources/GuiResources/HelpFiles/Features_Toolbox/Borders/Borders_Selection.png rename to src/Resources/Help/HelpFiles/Features_Toolbox/Borders/Borders_Selection.png diff --git a/src/Resources/GuiResources/HelpFiles/Features_Toolbox/Borders/Borders_Attributes.png b/src/Resources/Help/HelpFiles/Features_Toolbox/Borders_Attributes.png similarity index 100% rename from src/Resources/GuiResources/HelpFiles/Features_Toolbox/Borders/Borders_Attributes.png rename to src/Resources/Help/HelpFiles/Features_Toolbox/Borders_Attributes.png diff --git a/src/Resources/GuiResources/HelpFiles/Features_Toolbox/Features_TB_icon.png b/src/Resources/Help/HelpFiles/Features_Toolbox/Features_TB_icon.png similarity index 100% rename from src/Resources/GuiResources/HelpFiles/Features_Toolbox/Features_TB_icon.png rename to src/Resources/Help/HelpFiles/Features_Toolbox/Features_TB_icon.png diff --git a/src/Resources/Help/HelpFiles/Features_Toolbox/Features_Toolbox.html b/src/Resources/Help/HelpFiles/Features_Toolbox/Features_Toolbox.html new file mode 100644 index 0000000000000000000000000000000000000000..ef7cdce2051795523341c43635acd8c68fcf4fa1 --- /dev/null +++ b/src/Resources/Help/HelpFiles/Features_Toolbox/Features_Toolbox.html @@ -0,0 +1,58 @@ + + + + + + Features Toolbox + + +

      Features Toolbox

      +
      + The Features Toolbox contains controls for displaying features + including Annotations, Borders, Fibers, Foci, + Images, and Labels onto the data being displayed + on structures.
      +  
      + The Features Toolbox is not displayed by default, but is turned on + with the button (or with View Menu > + Features Toolbox) and attached to the right side of the Viewing + Area. It can also be detached from the Workbench Window by + left click + drag on the top of the Features Toolbox. To reattach, + double click on the top of the Features Toolbox.
      +
      + There are 6 tabs in the Features Toolbox, each controlling a + different kind of feature's display:
      +
      +     Annotations + controls attributes and selection of annotations.
      +
      +     Borders + controls attributes and selection of borders on surfaces.
      +
      +     Fibers controls + attributes, selection, and samples of fibers on surfaces and in + volumes.
      +
      +     Foci controls + attributes and selection of foci on surfaces and in volumes.
      +
      +     Images controls + selection of images in the background.
      +
      +     Labels controls + attributes and selection of labels on surfaces and in volumes.
      +
      +  
      +
      + Tabs are inactive (grayed-out) if no files are loaded containing a + particular Feature.
      +
      +
      +
      + + diff --git a/src/Resources/GuiResources/HelpFiles/Features_Toolbox/Fibers/Fibers.html b/src/Resources/Help/HelpFiles/Features_Toolbox/Fibers/Fibers.html similarity index 100% rename from src/Resources/GuiResources/HelpFiles/Features_Toolbox/Fibers/Fibers.html rename to src/Resources/Help/HelpFiles/Features_Toolbox/Fibers/Fibers.html diff --git a/src/Resources/GuiResources/HelpFiles/Features_Toolbox/Foci/Foci.html b/src/Resources/Help/HelpFiles/Features_Toolbox/Foci/Foci.html similarity index 100% rename from src/Resources/GuiResources/HelpFiles/Features_Toolbox/Foci/Foci.html rename to src/Resources/Help/HelpFiles/Features_Toolbox/Foci/Foci.html diff --git a/src/Resources/Help/HelpFiles/Features_Toolbox/Foci/Foci_Attributes.png b/src/Resources/Help/HelpFiles/Features_Toolbox/Foci/Foci_Attributes.png new file mode 100644 index 0000000000000000000000000000000000000000..213ca4cae56b95f50b6e42996c237060bda8459e Binary files /dev/null and b/src/Resources/Help/HelpFiles/Features_Toolbox/Foci/Foci_Attributes.png differ diff --git a/src/Resources/GuiResources/HelpFiles/Features_Toolbox/Foci/Foci_Selection.png b/src/Resources/Help/HelpFiles/Features_Toolbox/Foci/Foci_Selection.png similarity index 100% rename from src/Resources/GuiResources/HelpFiles/Features_Toolbox/Foci/Foci_Selection.png rename to src/Resources/Help/HelpFiles/Features_Toolbox/Foci/Foci_Selection.png diff --git a/src/Resources/Help/HelpFiles/Features_Toolbox/Images/Images.html b/src/Resources/Help/HelpFiles/Features_Toolbox/Images/Images.html new file mode 100644 index 0000000000000000000000000000000000000000..e6e1a1a55cddc00072d4a6b4b4d918a17fa602e7 --- /dev/null +++ b/src/Resources/Help/HelpFiles/Features_Toolbox/Images/Images.html @@ -0,0 +1,53 @@ + + + + + Borders + + + + + Images
      + The Images Tab in the Features Toolbox + contains options for display and selection of images in the + background. +
        +
      • Group: identifies the Image + group for the Active Tab. Tabs assigned to the same Group will + display the same images with the same attributes.
      • +
      + +
        +
      • Display Image toggles image + display on and off.
      • +
      + Images Attributes  
      +
        +
      • Depth: Chooses where to place image in relation to + models.  Back places image behind models; Front places + image in front of models; and Middle place images in the middle + of the models.
      • +
      • Minimum Threshold : Is used to filter out pixels in the + image that have a grayscale value less than this value.
        +
      • +
      • Maximum Threshold : Is used to filter out pixels in the + image that have a grayscale value greater than this value.
        +
      • +
      • Opacity : Blend image with models in front of the + image.
        +
      • +
      + Attributes for image
      +
      +
      + Images Selection
        Chooses image displayed in the + background.
      +
      +

      +
      + + diff --git a/src/Resources/Help/HelpFiles/Features_Toolbox/Images/Images_Attributes.png b/src/Resources/Help/HelpFiles/Features_Toolbox/Images/Images_Attributes.png new file mode 100644 index 0000000000000000000000000000000000000000..6fa5e3c15119156b0e39776972848c1fbaf544a4 Binary files /dev/null and b/src/Resources/Help/HelpFiles/Features_Toolbox/Images/Images_Attributes.png differ diff --git a/src/Resources/Help/HelpFiles/Features_Toolbox/Images/Images_Selection.png b/src/Resources/Help/HelpFiles/Features_Toolbox/Images/Images_Selection.png new file mode 100644 index 0000000000000000000000000000000000000000..bf6646acdba8c4f78e044770d28fc98414321ab5 Binary files /dev/null and b/src/Resources/Help/HelpFiles/Features_Toolbox/Images/Images_Selection.png differ diff --git a/src/Resources/GuiResources/HelpFiles/Features_Toolbox/Labels/Labels.html b/src/Resources/Help/HelpFiles/Features_Toolbox/Labels/Labels.html similarity index 100% rename from src/Resources/GuiResources/HelpFiles/Features_Toolbox/Labels/Labels.html rename to src/Resources/Help/HelpFiles/Features_Toolbox/Labels/Labels.html diff --git a/src/Resources/GuiResources/HelpFiles/Features_Toolbox/Labels/Labels_Attributes.png b/src/Resources/Help/HelpFiles/Features_Toolbox/Labels/Labels_Attributes.png similarity index 100% rename from src/Resources/GuiResources/HelpFiles/Features_Toolbox/Labels/Labels_Attributes.png rename to src/Resources/Help/HelpFiles/Features_Toolbox/Labels/Labels_Attributes.png diff --git a/src/Resources/GuiResources/HelpFiles/Features_Toolbox/Labels/Labels_Selection.png b/src/Resources/Help/HelpFiles/Features_Toolbox/Labels/Labels_Selection.png similarity index 100% rename from src/Resources/GuiResources/HelpFiles/Features_Toolbox/Labels/Labels_Selection.png rename to src/Resources/Help/HelpFiles/Features_Toolbox/Labels/Labels_Selection.png diff --git a/src/Resources/GuiResources/HelpFiles/Glossary/Brainordinate/Brainordinate.html b/src/Resources/Help/HelpFiles/Glossary/Brainordinate/Brainordinate.html similarity index 100% rename from src/Resources/GuiResources/HelpFiles/Glossary/Brainordinate/Brainordinate.html rename to src/Resources/Help/HelpFiles/Glossary/Brainordinate/Brainordinate.html diff --git a/src/Resources/GuiResources/HelpFiles/Glossary/CIFTI/CIFTI.html b/src/Resources/Help/HelpFiles/Glossary/CIFTI/CIFTI.html similarity index 100% rename from src/Resources/GuiResources/HelpFiles/Glossary/CIFTI/CIFTI.html rename to src/Resources/Help/HelpFiles/Glossary/CIFTI/CIFTI.html diff --git a/src/Resources/GuiResources/HelpFiles/Glossary/ConnectomeDB/ConnectomeDB.html b/src/Resources/Help/HelpFiles/Glossary/ConnectomeDB/ConnectomeDB.html similarity index 100% rename from src/Resources/GuiResources/HelpFiles/Glossary/ConnectomeDB/ConnectomeDB.html rename to src/Resources/Help/HelpFiles/Glossary/ConnectomeDB/ConnectomeDB.html diff --git a/src/Resources/GuiResources/HelpFiles/Glossary/ConnectomeDB/ConnectomeDB_login.png b/src/Resources/Help/HelpFiles/Glossary/ConnectomeDB/ConnectomeDB_login.png similarity index 100% rename from src/Resources/GuiResources/HelpFiles/Glossary/ConnectomeDB/ConnectomeDB_login.png rename to src/Resources/Help/HelpFiles/Glossary/ConnectomeDB/ConnectomeDB_login.png diff --git a/src/Resources/GuiResources/HelpFiles/Glossary/ConnectomeDB/Thumbs.db b/src/Resources/Help/HelpFiles/Glossary/ConnectomeDB/Thumbs.db similarity index 100% rename from src/Resources/GuiResources/HelpFiles/Glossary/ConnectomeDB/Thumbs.db rename to src/Resources/Help/HelpFiles/Glossary/ConnectomeDB/Thumbs.db diff --git a/src/Resources/GuiResources/HelpFiles/Glossary/GIFTI/GIFTI.html b/src/Resources/Help/HelpFiles/Glossary/GIFTI/GIFTI.html similarity index 100% rename from src/Resources/GuiResources/HelpFiles/Glossary/GIFTI/GIFTI.html rename to src/Resources/Help/HelpFiles/Glossary/GIFTI/GIFTI.html diff --git a/src/Resources/GuiResources/HelpFiles/Glossary/Grayordinate/Grayordinate.html b/src/Resources/Help/HelpFiles/Glossary/Grayordinate/Grayordinate.html similarity index 100% rename from src/Resources/GuiResources/HelpFiles/Glossary/Grayordinate/Grayordinate.html rename to src/Resources/Help/HelpFiles/Glossary/Grayordinate/Grayordinate.html diff --git a/src/Resources/GuiResources/HelpFiles/Glossary/NIFTI/NIFTI.html b/src/Resources/Help/HelpFiles/Glossary/NIFTI/NIFTI.html similarity index 100% rename from src/Resources/GuiResources/HelpFiles/Glossary/NIFTI/NIFTI.html rename to src/Resources/Help/HelpFiles/Glossary/NIFTI/NIFTI.html diff --git a/src/Resources/GuiResources/HelpFiles/Glossary/Scene/Scene.html b/src/Resources/Help/HelpFiles/Glossary/Scene/Scene.html similarity index 100% rename from src/Resources/GuiResources/HelpFiles/Glossary/Scene/Scene.html rename to src/Resources/Help/HelpFiles/Glossary/Scene/Scene.html diff --git a/src/Resources/GuiResources/HelpFiles/Glossary/Specification_File/Spec_File.png b/src/Resources/Help/HelpFiles/Glossary/Specification_File/Spec_File.png similarity index 100% rename from src/Resources/GuiResources/HelpFiles/Glossary/Specification_File/Spec_File.png rename to src/Resources/Help/HelpFiles/Glossary/Specification_File/Spec_File.png diff --git a/src/Resources/GuiResources/HelpFiles/Glossary/Specification_File/Specification_File.html b/src/Resources/Help/HelpFiles/Glossary/Specification_File/Specification_File.html similarity index 100% rename from src/Resources/GuiResources/HelpFiles/Glossary/Specification_File/Specification_File.html rename to src/Resources/Help/HelpFiles/Glossary/Specification_File/Specification_File.html diff --git a/src/Resources/GuiResources/HelpFiles/Glossary/Specification_File/Thumbs.db b/src/Resources/Help/HelpFiles/Glossary/Specification_File/Thumbs.db similarity index 100% rename from src/Resources/GuiResources/HelpFiles/Glossary/Specification_File/Thumbs.db rename to src/Resources/Help/HelpFiles/Glossary/Specification_File/Thumbs.db diff --git a/src/Resources/GuiResources/HelpFiles/Glossary/Whiteordinate/Whiteordinate.html b/src/Resources/Help/HelpFiles/Glossary/Whiteordinate/Whiteordinate.html similarity index 100% rename from src/Resources/GuiResources/HelpFiles/Glossary/Whiteordinate/Whiteordinate.html rename to src/Resources/Help/HelpFiles/Glossary/Whiteordinate/Whiteordinate.html diff --git a/src/Resources/GuiResources/HelpFiles/Information_Window/Information_Window.html b/src/Resources/Help/HelpFiles/Information_Window/Information_Window.html similarity index 100% rename from src/Resources/GuiResources/HelpFiles/Information_Window/Information_Window.html rename to src/Resources/Help/HelpFiles/Information_Window/Information_Window.html diff --git a/src/Resources/GuiResources/HelpFiles/Information_Window/Information_Window.png b/src/Resources/Help/HelpFiles/Information_Window/Information_Window.png similarity index 100% rename from src/Resources/GuiResources/HelpFiles/Information_Window/Information_Window.png rename to src/Resources/Help/HelpFiles/Information_Window/Information_Window.png diff --git a/src/Resources/Help/HelpFiles/Information_Window/Properties/Information_Properties.png b/src/Resources/Help/HelpFiles/Information_Window/Properties/Information_Properties.png new file mode 100644 index 0000000000000000000000000000000000000000..4702aaab19c9fc4ef55417c32d6c6199efd22155 Binary files /dev/null and b/src/Resources/Help/HelpFiles/Information_Window/Properties/Information_Properties.png differ diff --git a/src/Resources/GuiResources/HelpFiles/Information_Window/Properties/Properties.html b/src/Resources/Help/HelpFiles/Information_Window/Properties/Properties.html similarity index 100% rename from src/Resources/GuiResources/HelpFiles/Information_Window/Properties/Properties.html rename to src/Resources/Help/HelpFiles/Information_Window/Properties/Properties.html diff --git a/src/Resources/Help/HelpFiles/Menus/Data_Menu/Data_Menu.html b/src/Resources/Help/HelpFiles/Menus/Data_Menu/Data_Menu.html new file mode 100644 index 0000000000000000000000000000000000000000..5d661f53ecd66dff865026f6c20f9d249f2f0e72 --- /dev/null +++ b/src/Resources/Help/HelpFiles/Menus/Data_Menu/Data_Menu.html @@ -0,0 +1,23 @@ + + + + + Data Menu + + + Data Menu
      +
        +
      • Project Foci allows the user to + project foci a fixed distance above or below the volume + interaction surface. Any offset of the foci from the surface + is NOT preserved.
      • +
      • Split Multi-Structure Border Files + Older border files may contain borders for more than one + surface structure. This menu item allows the user split the + old border file into single structure border files.
      • +
      +
      +
      + + + diff --git a/src/Resources/GuiResources/HelpFiles/Menus/Data_Menu/Project_Foci_Dialog.png b/src/Resources/Help/HelpFiles/Menus/Data_Menu/Project_Foci_Dialog.png similarity index 100% rename from src/Resources/GuiResources/HelpFiles/Menus/Data_Menu/Project_Foci_Dialog.png rename to src/Resources/Help/HelpFiles/Menus/Data_Menu/Project_Foci_Dialog.png diff --git a/src/Resources/GuiResources/HelpFiles/Menus/Data_Menu/Thumbs.db b/src/Resources/Help/HelpFiles/Menus/Data_Menu/Thumbs.db similarity index 100% rename from src/Resources/GuiResources/HelpFiles/Menus/Data_Menu/Thumbs.db rename to src/Resources/Help/HelpFiles/Menus/Data_Menu/Thumbs.db diff --git a/src/Resources/GuiResources/HelpFiles/Menus/Develop_Menu/Develop_Menu.html b/src/Resources/Help/HelpFiles/Menus/Develop_Menu/Develop_Menu.html similarity index 63% rename from src/Resources/GuiResources/HelpFiles/Menus/Develop_Menu/Develop_Menu.html rename to src/Resources/Help/HelpFiles/Menus/Develop_Menu/Develop_Menu.html index 1be5d6c8e8cf6f5bd35586b08a0ee36ad6d729d0..46a59351aa0ab01b622ead69efd6898fadfdad96 100644 --- a/src/Resources/GuiResources/HelpFiles/Menus/Develop_Menu/Develop_Menu.html +++ b/src/Resources/Help/HelpFiles/Menus/Develop_Menu/Develop_Menu.html @@ -1,20 +1,19 @@ - - - - - Develop Menu - - - Develop Menu
      - The Develop Menu will only appear in the Menu - bar when Show Develop Menu in Menu Bar is selected On in - the Preferences Misc tab.  Currently the Develop Menu - only contains Time Graphics Update, which is used - by Workbench developers to gauge graphics performance.
      - - - + + + + + Develop Menu + + + Develop Menu
      + The Develop Menu will only appear in the Menu + bar when Show Develop Menu in Menu Bar is selected On in + the Preferences Misc tab.  Currently the Develop Menu + only contains Time Graphics Update, which is used + by Workbench developers to gauge graphics performance.  It + may contain other menu items for use during software development.
      + + + diff --git a/src/Resources/Help/HelpFiles/Menus/Edit_Menu/Edit_Menu.fld/filelist.xml b/src/Resources/Help/HelpFiles/Menus/Edit_Menu/Edit_Menu.fld/filelist.xml new file mode 100644 index 0000000000000000000000000000000000000000..f58efde46e362009ae769a556634018fcd948cb8 --- /dev/null +++ b/src/Resources/Help/HelpFiles/Menus/Edit_Menu/Edit_Menu.fld/filelist.xml @@ -0,0 +1,5 @@ + + + + + \ No newline at end of file diff --git a/src/Resources/Help/HelpFiles/Menus/Edit_Menu/Edit_Menu.fld/themedata.thmx b/src/Resources/Help/HelpFiles/Menus/Edit_Menu/Edit_Menu.fld/themedata.thmx new file mode 100644 index 0000000000000000000000000000000000000000..87e8520bab1913e2793103510f54475816746406 Binary files /dev/null and b/src/Resources/Help/HelpFiles/Menus/Edit_Menu/Edit_Menu.fld/themedata.thmx differ diff --git a/src/Resources/Help/HelpFiles/Menus/Edit_Menu/Edit_Menu.html b/src/Resources/Help/HelpFiles/Menus/Edit_Menu/Edit_Menu.html new file mode 100644 index 0000000000000000000000000000000000000000..1f8ff6370f5e22862aae10da983d88492eb4a8db --- /dev/null +++ b/src/Resources/Help/HelpFiles/Menus/Edit_Menu/Edit_Menu.html @@ -0,0 +1,119 @@ + + + + + Edit Menu + + +

      Edit Menu
      + Items on the Edit Menu and their text will vary depending upon + the state of wb_view such as when an annotation is selected in + Annotations Mode.

      +
        +
      • Undo (text may vary) Undo + a previous operation.
      • +
      • Redo (text may vary) Redo + an operation that was undone.
      • +
      • Cut Remove and place the selected item onto the + clipboard.
      • +
      • Copy Place a copy of the selected item onto the + clipboard.
      • +
      • Paste (text may vary) Paste + the item from the clipboard at the mouse cursor location.
      • +
      • Paste + + Special (text may vary) A special mode to paste the + item from the clipboard at the mouse cursor location.
      • +
      • Delete Delete the + selected item
      • +
      • Select + + All Used to select + all time, typically used for Annotations
      • +
      • Start Dictation + Added by operating system on MacOS; serves no function in + wb_view
        +
      • +
      • Emoji & + Symbols  Added by operating system on MacOS; serves + no function in wb_view
        +
      • +
      +   + + + + +
      +  
      + + diff --git a/src/Resources/Help/HelpFiles/Menus/Edit_Menu/Edit_Menu.png b/src/Resources/Help/HelpFiles/Menus/Edit_Menu/Edit_Menu.png new file mode 100644 index 0000000000000000000000000000000000000000..f4916662202529b82bc3df329d6d229b8e6bf396 Binary files /dev/null and b/src/Resources/Help/HelpFiles/Menus/Edit_Menu/Edit_Menu.png differ diff --git a/src/Resources/GuiResources/HelpFiles/Menus/File_Menu/._~$imation_Control.docx b/src/Resources/Help/HelpFiles/Menus/File_Menu/._~$imation_Control.docx similarity index 100% rename from src/Resources/GuiResources/HelpFiles/Menus/File_Menu/._~$imation_Control.docx rename to src/Resources/Help/HelpFiles/Menus/File_Menu/._~$imation_Control.docx diff --git a/src/Resources/GuiResources/HelpFiles/Menus/File_Menu/._~$le_Menu.docx b/src/Resources/Help/HelpFiles/Menus/File_Menu/._~$le_Menu.docx similarity index 100% rename from src/Resources/GuiResources/HelpFiles/Menus/File_Menu/._~$le_Menu.docx rename to src/Resources/Help/HelpFiles/Menus/File_Menu/._~$le_Menu.docx diff --git a/src/Resources/GuiResources/HelpFiles/Menus/File_Menu/._~$pture_Image.docx b/src/Resources/Help/HelpFiles/Menus/File_Menu/._~$pture_Image.docx similarity index 100% rename from src/Resources/GuiResources/HelpFiles/Menus/File_Menu/._~$pture_Image.docx rename to src/Resources/Help/HelpFiles/Menus/File_Menu/._~$pture_Image.docx diff --git a/src/Resources/GuiResources/HelpFiles/Menus/File_Menu/Animation_Control/Animation_Control.html b/src/Resources/Help/HelpFiles/Menus/File_Menu/Animation_Control/Animation_Control.html similarity index 100% rename from src/Resources/GuiResources/HelpFiles/Menus/File_Menu/Animation_Control/Animation_Control.html rename to src/Resources/Help/HelpFiles/Menus/File_Menu/Animation_Control/Animation_Control.html diff --git a/src/Resources/GuiResources/HelpFiles/Menus/File_Menu/Animation_Control/Animation_Control_dialog.png b/src/Resources/Help/HelpFiles/Menus/File_Menu/Animation_Control/Animation_Control_dialog.png similarity index 100% rename from src/Resources/GuiResources/HelpFiles/Menus/File_Menu/Animation_Control/Animation_Control_dialog.png rename to src/Resources/Help/HelpFiles/Menus/File_Menu/Animation_Control/Animation_Control_dialog.png diff --git a/src/Resources/GuiResources/HelpFiles/Menus/File_Menu/Animation_Control/Thumbs.db b/src/Resources/Help/HelpFiles/Menus/File_Menu/Animation_Control/Thumbs.db similarity index 100% rename from src/Resources/GuiResources/HelpFiles/Menus/File_Menu/Animation_Control/Thumbs.db rename to src/Resources/Help/HelpFiles/Menus/File_Menu/Animation_Control/Thumbs.db diff --git a/src/Resources/GuiResources/HelpFiles/Menus/File_Menu/Capture_Image/Capture_Image.html b/src/Resources/Help/HelpFiles/Menus/File_Menu/Capture_Image/Capture_Image.html similarity index 100% rename from src/Resources/GuiResources/HelpFiles/Menus/File_Menu/Capture_Image/Capture_Image.html rename to src/Resources/Help/HelpFiles/Menus/File_Menu/Capture_Image/Capture_Image.html diff --git a/src/Resources/GuiResources/HelpFiles/Menus/File_Menu/Capture_Image/Capture_Image_dialog.png b/src/Resources/Help/HelpFiles/Menus/File_Menu/Capture_Image/Capture_Image_dialog.png similarity index 100% rename from src/Resources/GuiResources/HelpFiles/Menus/File_Menu/Capture_Image/Capture_Image_dialog.png rename to src/Resources/Help/HelpFiles/Menus/File_Menu/Capture_Image/Capture_Image_dialog.png diff --git a/src/Resources/GuiResources/HelpFiles/Menus/File_Menu/Capture_Image/File_menu.png b/src/Resources/Help/HelpFiles/Menus/File_Menu/Capture_Image/File_menu.png similarity index 100% rename from src/Resources/GuiResources/HelpFiles/Menus/File_Menu/Capture_Image/File_menu.png rename to src/Resources/Help/HelpFiles/Menus/File_Menu/Capture_Image/File_menu.png diff --git a/src/Resources/GuiResources/HelpFiles/Menus/File_Menu/Capture_Image/Thumbs.db b/src/Resources/Help/HelpFiles/Menus/File_Menu/Capture_Image/Thumbs.db similarity index 100% rename from src/Resources/GuiResources/HelpFiles/Menus/File_Menu/Capture_Image/Thumbs.db rename to src/Resources/Help/HelpFiles/Menus/File_Menu/Capture_Image/Thumbs.db diff --git a/src/Resources/GuiResources/HelpFiles/Menus/File_Menu/File_Menu.html b/src/Resources/Help/HelpFiles/Menus/File_Menu/File_Menu.html similarity index 100% rename from src/Resources/GuiResources/HelpFiles/Menus/File_Menu/File_Menu.html rename to src/Resources/Help/HelpFiles/Menus/File_Menu/File_Menu.html diff --git a/src/Resources/GuiResources/HelpFiles/Menus/File_Menu/File_menu.png b/src/Resources/Help/HelpFiles/Menus/File_Menu/File_menu.png similarity index 100% rename from src/Resources/GuiResources/HelpFiles/Menus/File_Menu/File_menu.png rename to src/Resources/Help/HelpFiles/Menus/File_Menu/File_menu.png diff --git a/src/Resources/Help/HelpFiles/Menus/File_Menu/Preferences/Preferences.html b/src/Resources/Help/HelpFiles/Menus/File_Menu/Preferences/Preferences.html new file mode 100644 index 0000000000000000000000000000000000000000..72922e22f5a9f0705d4316cd8a8a18dc07784f7a --- /dev/null +++ b/src/Resources/Help/HelpFiles/Menus/File_Menu/Preferences/Preferences.html @@ -0,0 +1,148 @@ + + + + + Preferences + + + Preferences
      + Preferences, accessible under the wb_view menu + (Mac) or File menu + (PC or Linux), allows one to tailor the Workbench viewing + environment to one's preference. Changes to Preferences are saved + between Workbench sessions. Options include:
      + +
      Colors   + + + + + + +  
      +
        +
      • Buttons to set the Foreground + (chart axes, colorbar labels, etc.) and Background + colors for All types of views, or Chart, Surface, or Volume + views separately.
        +
      • +
      +
      +  
      +
      +

      + ID     +
        +
      • Show Surface ID Symbols to + enable display of identification symbols on surfaces.
      • +
      • Show Volume ID Symbols to enable + display of identification symbols on volume slices.
      • +
        +  
        + Misc     + +
        +
        +  
        +
        +
        +

        + New + + + Tabs  
        +
        +
          +
        • Yoked to + Group A to set On/Off whether newly created tabs + are by default Yoked + to the Group A yoking group.
          +
        • +
        • Volume + Axes Crosshairs to set On/Off display of axes lines in + all Volume views.
          +
        • +
        • Volume Axes Labels to set + On/Off display of superior (S)/inferior (I) and left + (L)/right (R) axes in all Volume views. 
          +
        • +
        • Volume ID Moves Slice Selection + to set in newly created Tabs, On/Off movement of the volume slice/crosshairs + displayed to the same plane as the brainordinate selected in + that tab (or a yoked surface/volume tab).
          +
        • +
        • Volume Montage Slice Coord to + set On/Off display of x, y, or z coordinate slice position + in all Volume slice plane views.
          +
        • +
        • Volume Montage Precision to + set the decimal point precision of x, y, or z coordinate + slice position values in all Volume slice plane views.
          +
        • +
        +
      +
      +   +
        +
        + OpenGL  
         
        +
          +
        • Setting for the Image Capture + Method (OpenGL computer code) used by wb_view to + capture an image of the Viewing Window. Changing this + setting might resolve problems with image capturing on your + system.
          +
        • +
        +
        +  
        +
        +
        +

        +
      + + diff --git a/src/Resources/Help/HelpFiles/Menus/File_Menu/Preferences/Preferences_Colors.png b/src/Resources/Help/HelpFiles/Menus/File_Menu/Preferences/Preferences_Colors.png new file mode 100644 index 0000000000000000000000000000000000000000..dcf197829f39a903875c512b197acbf166542f81 Binary files /dev/null and b/src/Resources/Help/HelpFiles/Menus/File_Menu/Preferences/Preferences_Colors.png differ diff --git a/src/Resources/Help/HelpFiles/Menus/File_Menu/Preferences/Preferences_ID.png b/src/Resources/Help/HelpFiles/Menus/File_Menu/Preferences/Preferences_ID.png new file mode 100644 index 0000000000000000000000000000000000000000..6283140e6268fb5e28ba4cb78d4a69bdf90002c1 Binary files /dev/null and b/src/Resources/Help/HelpFiles/Menus/File_Menu/Preferences/Preferences_ID.png differ diff --git a/src/Resources/Help/HelpFiles/Menus/File_Menu/Preferences/Preferences_Misc.png b/src/Resources/Help/HelpFiles/Menus/File_Menu/Preferences/Preferences_Misc.png new file mode 100644 index 0000000000000000000000000000000000000000..65a21bb136476a5cdd681e704543710d48e628e2 Binary files /dev/null and b/src/Resources/Help/HelpFiles/Menus/File_Menu/Preferences/Preferences_Misc.png differ diff --git a/src/Resources/Help/HelpFiles/Menus/File_Menu/Preferences/Preferences_NewTabs.png b/src/Resources/Help/HelpFiles/Menus/File_Menu/Preferences/Preferences_NewTabs.png new file mode 100644 index 0000000000000000000000000000000000000000..38c922c992ef17ab3c1eef316305da2a7161188c Binary files /dev/null and b/src/Resources/Help/HelpFiles/Menus/File_Menu/Preferences/Preferences_NewTabs.png differ diff --git a/src/Resources/Help/HelpFiles/Menus/File_Menu/Preferences/Preferences_OpenGL.png b/src/Resources/Help/HelpFiles/Menus/File_Menu/Preferences/Preferences_OpenGL.png new file mode 100644 index 0000000000000000000000000000000000000000..b132e0d1af2f5cb0cd66bb4cbede462c43963f7b Binary files /dev/null and b/src/Resources/Help/HelpFiles/Menus/File_Menu/Preferences/Preferences_OpenGL.png differ diff --git a/src/Resources/GuiResources/HelpFiles/Menus/File_Menu/Preferences/Preferences_Vol.png b/src/Resources/Help/HelpFiles/Menus/File_Menu/Preferences/Preferences_Vol.png similarity index 100% rename from src/Resources/GuiResources/HelpFiles/Menus/File_Menu/Preferences/Preferences_Vol.png rename to src/Resources/Help/HelpFiles/Menus/File_Menu/Preferences/Preferences_Vol.png diff --git a/src/Resources/GuiResources/HelpFiles/Menus/File_Menu/Preferences/Thumbs.db b/src/Resources/Help/HelpFiles/Menus/File_Menu/Preferences/Thumbs.db similarity index 100% rename from src/Resources/GuiResources/HelpFiles/Menus/File_Menu/Preferences/Thumbs.db rename to src/Resources/Help/HelpFiles/Menus/File_Menu/Preferences/Thumbs.db diff --git a/src/Resources/GuiResources/HelpFiles/Menus/File_Menu/Save-Manage_Files/Gear_symbol.png b/src/Resources/Help/HelpFiles/Menus/File_Menu/Save-Manage_Files/Gear_symbol.png similarity index 100% rename from src/Resources/GuiResources/HelpFiles/Menus/File_Menu/Save-Manage_Files/Gear_symbol.png rename to src/Resources/Help/HelpFiles/Menus/File_Menu/Save-Manage_Files/Gear_symbol.png diff --git a/src/Resources/GuiResources/HelpFiles/Menus/File_Menu/Save-Manage_Files/Reload_symbol.png b/src/Resources/Help/HelpFiles/Menus/File_Menu/Save-Manage_Files/Reload_symbol.png similarity index 100% rename from src/Resources/GuiResources/HelpFiles/Menus/File_Menu/Save-Manage_Files/Reload_symbol.png rename to src/Resources/Help/HelpFiles/Menus/File_Menu/Save-Manage_Files/Reload_symbol.png diff --git a/src/Resources/GuiResources/HelpFiles/Menus/File_Menu/Save-Manage_Files/Remove_symbol.png b/src/Resources/Help/HelpFiles/Menus/File_Menu/Save-Manage_Files/Remove_symbol.png similarity index 100% rename from src/Resources/GuiResources/HelpFiles/Menus/File_Menu/Save-Manage_Files/Remove_symbol.png rename to src/Resources/Help/HelpFiles/Menus/File_Menu/Save-Manage_Files/Remove_symbol.png diff --git a/src/Resources/GuiResources/HelpFiles/Menus/File_Menu/Save-Manage_Files/Save-Manage_Files.html b/src/Resources/Help/HelpFiles/Menus/File_Menu/Save-Manage_Files/Save-Manage_Files.html similarity index 100% rename from src/Resources/GuiResources/HelpFiles/Menus/File_Menu/Save-Manage_Files/Save-Manage_Files.html rename to src/Resources/Help/HelpFiles/Menus/File_Menu/Save-Manage_Files/Save-Manage_Files.html diff --git a/src/Resources/GuiResources/HelpFiles/Menus/File_Menu/Save-Manage_Files/Save-Manage_Files_dialog.png b/src/Resources/Help/HelpFiles/Menus/File_Menu/Save-Manage_Files/Save-Manage_Files_dialog.png similarity index 100% rename from src/Resources/GuiResources/HelpFiles/Menus/File_Menu/Save-Manage_Files/Save-Manage_Files_dialog.png rename to src/Resources/Help/HelpFiles/Menus/File_Menu/Save-Manage_Files/Save-Manage_Files_dialog.png diff --git a/src/Resources/GuiResources/HelpFiles/Menus/File_Menu/Save-Manage_Files/Thumbs.db b/src/Resources/Help/HelpFiles/Menus/File_Menu/Save-Manage_Files/Thumbs.db similarity index 100% rename from src/Resources/GuiResources/HelpFiles/Menus/File_Menu/Save-Manage_Files/Thumbs.db rename to src/Resources/Help/HelpFiles/Menus/File_Menu/Save-Manage_Files/Thumbs.db diff --git a/src/Resources/GuiResources/HelpFiles/Menus/File_Menu/Save-Manage_Files/Upload_symbol.png b/src/Resources/Help/HelpFiles/Menus/File_Menu/Save-Manage_Files/Upload_symbol.png similarity index 100% rename from src/Resources/GuiResources/HelpFiles/Menus/File_Menu/Save-Manage_Files/Upload_symbol.png rename to src/Resources/Help/HelpFiles/Menus/File_Menu/Save-Manage_Files/Upload_symbol.png diff --git a/src/Resources/GuiResources/HelpFiles/Menus/File_Menu/Thumbs.db b/src/Resources/Help/HelpFiles/Menus/File_Menu/Thumbs.db similarity index 100% rename from src/Resources/GuiResources/HelpFiles/Menus/File_Menu/Thumbs.db rename to src/Resources/Help/HelpFiles/Menus/File_Menu/Thumbs.db diff --git a/src/Resources/GuiResources/HelpFiles/Menus/Help_Menu/Bug_Report_box.png b/src/Resources/Help/HelpFiles/Menus/Help_Menu/Bug_Report_box.png similarity index 100% rename from src/Resources/GuiResources/HelpFiles/Menus/Help_Menu/Bug_Report_box.png rename to src/Resources/Help/HelpFiles/Menus/Help_Menu/Bug_Report_box.png diff --git a/src/Resources/GuiResources/HelpFiles/Menus/Help_Menu/Help_Menu.html b/src/Resources/Help/HelpFiles/Menus/Help_Menu/Help_Menu.html similarity index 100% rename from src/Resources/GuiResources/HelpFiles/Menus/Help_Menu/Help_Menu.html rename to src/Resources/Help/HelpFiles/Menus/Help_Menu/Help_Menu.html diff --git a/src/Resources/GuiResources/HelpFiles/Menus/Help_Menu/Help_Menu.png b/src/Resources/Help/HelpFiles/Menus/Help_Menu/Help_Menu.png similarity index 100% rename from src/Resources/GuiResources/HelpFiles/Menus/Help_Menu/Help_Menu.png rename to src/Resources/Help/HelpFiles/Menus/Help_Menu/Help_Menu.png diff --git a/src/Resources/GuiResources/HelpFiles/Menus/Help_Menu/Help_Search.png b/src/Resources/Help/HelpFiles/Menus/Help_Menu/Help_Search.png similarity index 100% rename from src/Resources/GuiResources/HelpFiles/Menus/Help_Menu/Help_Search.png rename to src/Resources/Help/HelpFiles/Menus/Help_Menu/Help_Search.png diff --git a/src/Resources/GuiResources/HelpFiles/Menus/Help_Menu/Help_box.png b/src/Resources/Help/HelpFiles/Menus/Help_Menu/Help_box.png similarity index 100% rename from src/Resources/GuiResources/HelpFiles/Menus/Help_Menu/Help_box.png rename to src/Resources/Help/HelpFiles/Menus/Help_Menu/Help_box.png diff --git a/src/Resources/GuiResources/HelpFiles/Menus/Help_Menu/Help_button.png b/src/Resources/Help/HelpFiles/Menus/Help_Menu/Help_button.png similarity index 100% rename from src/Resources/GuiResources/HelpFiles/Menus/Help_Menu/Help_button.png rename to src/Resources/Help/HelpFiles/Menus/Help_Menu/Help_button.png diff --git a/src/Resources/GuiResources/HelpFiles/Menus/Help_Menu/Workbench_Help.png b/src/Resources/Help/HelpFiles/Menus/Help_Menu/Workbench_Help.png similarity index 100% rename from src/Resources/GuiResources/HelpFiles/Menus/Help_Menu/Workbench_Help.png rename to src/Resources/Help/HelpFiles/Menus/Help_Menu/Workbench_Help.png diff --git a/src/Resources/Help/HelpFiles/Menus/Menus.html b/src/Resources/Help/HelpFiles/Menus/Menus.html new file mode 100644 index 0000000000000000000000000000000000000000..3a3171881ed2b65eee491bb4c8939733e9f0c8f9 --- /dev/null +++ b/src/Resources/Help/HelpFiles/Menus/Menus.html @@ -0,0 +1,29 @@ + + + + + Menus + + +

      Menus

      +
      + +
      +
      + + diff --git a/src/Resources/GuiResources/HelpFiles/Menus/Surface_Menu/Surface_Menu.html b/src/Resources/Help/HelpFiles/Menus/Surface_Menu/Surface_Menu.html similarity index 100% rename from src/Resources/GuiResources/HelpFiles/Menus/Surface_Menu/Surface_Menu.html rename to src/Resources/Help/HelpFiles/Menus/Surface_Menu/Surface_Menu.html diff --git a/src/Resources/GuiResources/HelpFiles/Menus/Surface_Menu/Surface_Properties.png b/src/Resources/Help/HelpFiles/Menus/Surface_Menu/Surface_Properties.png similarity index 100% rename from src/Resources/GuiResources/HelpFiles/Menus/Surface_Menu/Surface_Properties.png rename to src/Resources/Help/HelpFiles/Menus/Surface_Menu/Surface_Properties.png diff --git a/src/Resources/GuiResources/HelpFiles/Menus/Surface_Menu/Volume_Interaction_Surfaces.png b/src/Resources/Help/HelpFiles/Menus/Surface_Menu/Volume_Interaction_Surfaces.png similarity index 100% rename from src/Resources/GuiResources/HelpFiles/Menus/Surface_Menu/Volume_Interaction_Surfaces.png rename to src/Resources/Help/HelpFiles/Menus/Surface_Menu/Volume_Interaction_Surfaces.png diff --git a/src/Resources/GuiResources/HelpFiles/Menus/Thumbs.db b/src/Resources/Help/HelpFiles/Menus/Thumbs.db similarity index 100% rename from src/Resources/GuiResources/HelpFiles/Menus/Thumbs.db rename to src/Resources/Help/HelpFiles/Menus/Thumbs.db diff --git a/src/Resources/GuiResources/HelpFiles/Menus/View_Menu/Gaps_And_Margins/Gaps_And_Margins.html b/src/Resources/Help/HelpFiles/Menus/View_Menu/Gaps_And_Margins/Gaps_And_Margins.html similarity index 100% rename from src/Resources/GuiResources/HelpFiles/Menus/View_Menu/Gaps_And_Margins/Gaps_And_Margins.html rename to src/Resources/Help/HelpFiles/Menus/View_Menu/Gaps_And_Margins/Gaps_And_Margins.html diff --git a/src/Resources/GuiResources/HelpFiles/Menus/View_Menu/Gaps_And_Margins/Gaps_And_Margins.png b/src/Resources/Help/HelpFiles/Menus/View_Menu/Gaps_And_Margins/Gaps_And_Margins.png similarity index 100% rename from src/Resources/GuiResources/HelpFiles/Menus/View_Menu/Gaps_And_Margins/Gaps_And_Margins.png rename to src/Resources/Help/HelpFiles/Menus/View_Menu/Gaps_And_Margins/Gaps_And_Margins.png diff --git a/src/Resources/GuiResources/HelpFiles/Menus/View_Menu/Thumbs.db b/src/Resources/Help/HelpFiles/Menus/View_Menu/Thumbs.db similarity index 100% rename from src/Resources/GuiResources/HelpFiles/Menus/View_Menu/Thumbs.db rename to src/Resources/Help/HelpFiles/Menus/View_Menu/Thumbs.db diff --git a/src/Resources/GuiResources/HelpFiles/Menus/View_Menu/Tile_Tabs_Configuration/Thumbs.db b/src/Resources/Help/HelpFiles/Menus/View_Menu/Tile_Tabs_Configuration/Thumbs.db similarity index 100% rename from src/Resources/GuiResources/HelpFiles/Menus/View_Menu/Tile_Tabs_Configuration/Thumbs.db rename to src/Resources/Help/HelpFiles/Menus/View_Menu/Tile_Tabs_Configuration/Thumbs.db diff --git a/src/Resources/GuiResources/HelpFiles/Menus/View_Menu/Tile_Tabs_Configuration/Tile_Tabs_Configuration.html b/src/Resources/Help/HelpFiles/Menus/View_Menu/Tile_Tabs_Configuration/Tile_Tabs_Configuration.html similarity index 100% rename from src/Resources/GuiResources/HelpFiles/Menus/View_Menu/Tile_Tabs_Configuration/Tile_Tabs_Configuration.html rename to src/Resources/Help/HelpFiles/Menus/View_Menu/Tile_Tabs_Configuration/Tile_Tabs_Configuration.html diff --git a/src/Resources/GuiResources/HelpFiles/Menus/View_Menu/Tile_Tabs_Configuration/Tile_Tabs_Configuration.png b/src/Resources/Help/HelpFiles/Menus/View_Menu/Tile_Tabs_Configuration/Tile_Tabs_Configuration.png similarity index 100% rename from src/Resources/GuiResources/HelpFiles/Menus/View_Menu/Tile_Tabs_Configuration/Tile_Tabs_Configuration.png rename to src/Resources/Help/HelpFiles/Menus/View_Menu/Tile_Tabs_Configuration/Tile_Tabs_Configuration.png diff --git a/src/Resources/GuiResources/HelpFiles/Menus/View_Menu/View_Menu.html b/src/Resources/Help/HelpFiles/Menus/View_Menu/View_Menu.html similarity index 100% rename from src/Resources/GuiResources/HelpFiles/Menus/View_Menu/View_Menu.html rename to src/Resources/Help/HelpFiles/Menus/View_Menu/View_Menu.html diff --git a/src/Resources/GuiResources/HelpFiles/Menus/View_Menu/View_Menu.png b/src/Resources/Help/HelpFiles/Menus/View_Menu/View_Menu.png similarity index 100% rename from src/Resources/GuiResources/HelpFiles/Menus/View_Menu/View_Menu.png rename to src/Resources/Help/HelpFiles/Menus/View_Menu/View_Menu.png diff --git a/src/Resources/Help/HelpFiles/Menus/Window_Menu.png b/src/Resources/Help/HelpFiles/Menus/Window_Menu.png new file mode 100644 index 0000000000000000000000000000000000000000..7a56fd247495492a3639deba10a37a7ed25b5264 Binary files /dev/null and b/src/Resources/Help/HelpFiles/Menus/Window_Menu.png differ diff --git a/src/Resources/GuiResources/HelpFiles/Menus/Window_Menu/IdentifyBrainordinateDialog.png b/src/Resources/Help/HelpFiles/Menus/Window_Menu/IdentifyBrainordinateDialog.png similarity index 100% rename from src/Resources/GuiResources/HelpFiles/Menus/Window_Menu/IdentifyBrainordinateDialog.png rename to src/Resources/Help/HelpFiles/Menus/Window_Menu/IdentifyBrainordinateDialog.png diff --git a/src/Resources/GuiResources/HelpFiles/Menus/Window_Menu/Rename_tab.png b/src/Resources/Help/HelpFiles/Menus/Window_Menu/Rename_tab.png similarity index 100% rename from src/Resources/GuiResources/HelpFiles/Menus/Window_Menu/Rename_tab.png rename to src/Resources/Help/HelpFiles/Menus/Window_Menu/Rename_tab.png diff --git a/src/Resources/GuiResources/HelpFiles/Menus/Window_Menu/Thumbs.db b/src/Resources/Help/HelpFiles/Menus/Window_Menu/Thumbs.db similarity index 100% rename from src/Resources/GuiResources/HelpFiles/Menus/Window_Menu/Thumbs.db rename to src/Resources/Help/HelpFiles/Menus/Window_Menu/Thumbs.db diff --git a/src/Resources/GuiResources/HelpFiles/Menus/Window_Menu/Window_Menu.html b/src/Resources/Help/HelpFiles/Menus/Window_Menu/Window_Menu.html similarity index 65% rename from src/Resources/GuiResources/HelpFiles/Menus/Window_Menu/Window_Menu.html rename to src/Resources/Help/HelpFiles/Menus/Window_Menu/Window_Menu.html index 2f9be05cc3a301fd2881bf16355c308b546d6af1..9f90ab5899ee6c865a762946c46699a5455b8e6c 100644 --- a/src/Resources/GuiResources/HelpFiles/Menus/Window_Menu/Window_Menu.html +++ b/src/Resources/Help/HelpFiles/Menus/Window_Menu/Window_Menu.html @@ -1,12 +1,12 @@ - - - - - Window Menu - - - Window Menu
      - The Window Menu contains options for viewing + + + + Window Menu + + + Window Menu
      + The Window Menu contains options for viewing
      Viewing @@ -14,72 +14,66 @@ - - Tabs and and Workbench - - Windows. -
        -
      • Next Tab switches to the Viewing - Tab to the right of the currently displayed tab.  The - shortcut for Next Tab is Command/Ctrl + right - arrow.
      • -
      - -
        -
      • Lock Window Aspect locks the aspect ratio - for the entire graphics region in the window.
      • -
      -
        -
      • Lock Tab Aspect locks the aspect ratio - for the graphics region in the selected tab.
      • -
      -
        -
      • Lock Aspect Ratio for All Tabs locks the aspect ratio - for the graphics region in each tab in the window.
      • -
      -
        -
      • Unlock Aspect Ratio for All Tabs unlocks the aspect ratio - for the graphics region for each tab in the window.
      • -
      - - -
        -
      • Next Tab switches to the Viewing Tab to the right of the - currently displayed tab.  The shortcut for Previous Tab is - Command/Ctrl + right arrow.
      • -
      -
        -
      • Previous Tab switches to the Viewing Tab to the left of the - currently displayed tab.  The shortcut for Previous Tab is - Command/Ctrl + left arrow.
      • -
      -
        -
      • Rename Selected Tab... allows - the user to rename the Viewing Tab with any name or descriptor - they choose.
        -

        -
      • -
      -
      -
        -
      • Move All Tabs in Current Window to - New Windows separates all of the Viewing Tabs out into - individual Workbench Windows.
      • -
      -
        -
      • Move All Tabs From All Windows Into - Selected Window takes all individual Workbench Windows - and turns them into Viewing Tabs within a single Window.
      • -
      -
        + + + Windows.
        +
        +
        +
          +
        • Lock Window Aspect Ratios + locks the aspect ratio for the entire graphics region in the + window.
        • +
        +
          +
        +
          +
        • Lock All Tab Aspect Ratios + locks the aspect ratio for all tabs in the window.  If + Tile Tabs is enabled, the width and height of the region + allocated to the tab is used to set the tab's aspect + ratio.  When viewing a single tab, the width and height + of the window is used to set the tab's aspect ratio.
        • +
        +
          +
        • Next Tab switches to the Viewing Tab to the right of the + currently displayed tab.  The shortcut for Previous Tab is + Command/Ctrl + right arrow.
        • +
        +
          +
        • Previous Tab switches to the Viewing Tab to the left of the + currently displayed tab.  The shortcut for Previous Tab is + Command/Ctrl + left arrow.
        • +
        +
          +
        • Rename Selected Tab... allows + the user to rename the Viewing Tab with any name or descriptor + they choose.
          +

          +
        • +
        +
        +
          +
        • Move All Tabs in Current Window to + New Windows separates all of the Viewing Tabs out into + individual Workbench Windows.
        • +
        +
          +
        • Move All Tabs From All Windows Into + Selected Window takes all individual Workbench Windows + and turns them into Viewing Tabs within a single Window.
        • +
        +
        • Move Selected Tab to Window > moves @@ -88,52 +82,44 @@ - - the active Viewing Tab into a new Window (if there are no - other Windows open) or into another existing Window.
        • -
        - -
          -
        • Information - - - - - Window opens the Information - Window.  When the user clicks on a surface or volume in - the viewing area, this is where the information for that - location is displayed.
        • -
        -
          -
        • Scenes... - opens the Scenes Window.  When there - is a check mark in front of this option (as shown below), it - means the Scene box is open.  Click this option again and - it will close the Scenes Window.
        • -
        -
          -
        • Bring All To Front pulls all of - the Workbench elements (Windows, Toolboxes, etc.) that are - opened, out in front of all other displays (Terminal windows, - browsers, applications, etc.).
        • -
        -
          -
        • Tile Windows organizes all open - Workbench Windows into columns and rows.
          -
        • -
        -  
        -  
        - - + + + the active Viewing Tab into a new Window (if there are no + other Windows open) or into another existing Window. +
      +
        +
      • Identify + opens the Information Window.  Allows the user to + select Brainordinates via several different methods.
      • +
      +
        +
      • Information + opens the Information Window.  When the user clicks + on a surface or volume in the viewing area, this is where the + information for that location is displayed.
      • +
      +
        +
      • Scenes... + opens the Scenes Window.  When there + is a check mark in front of this option (as shown below), it + means the Scene box is open.  Click this option again and + it will close the Scenes Window.
      • +
      +
        +
      • Bring All To Front pulls all of + the Workbench elements (Windows, Toolboxes, etc.) that are + opened, out in front of all other displays (Terminal windows, + browsers, applications, etc.).
      • +
      +
        +
      • Tile Windows organizes all open + Workbench Windows into columns and rows.
        +
      • +
      +  
      +  
      + + diff --git a/src/Resources/Help/HelpFiles/Menus/Window_Menu/Window_Menu.png b/src/Resources/Help/HelpFiles/Menus/Window_Menu/Window_Menu.png new file mode 100644 index 0000000000000000000000000000000000000000..7a56fd247495492a3639deba10a37a7ed25b5264 Binary files /dev/null and b/src/Resources/Help/HelpFiles/Menus/Window_Menu/Window_Menu.png differ diff --git a/src/Resources/GuiResources/HelpFiles/Menus/wb_view_Menu/._~$_view_Menu.docx b/src/Resources/Help/HelpFiles/Menus/wb_view_Menu/._~$_view_Menu.docx similarity index 100% rename from src/Resources/GuiResources/HelpFiles/Menus/wb_view_Menu/._~$_view_Menu.docx rename to src/Resources/Help/HelpFiles/Menus/wb_view_Menu/._~$_view_Menu.docx diff --git a/src/Resources/GuiResources/HelpFiles/Menus/wb_view_Menu/Thumbs.db b/src/Resources/Help/HelpFiles/Menus/wb_view_Menu/Thumbs.db similarity index 100% rename from src/Resources/GuiResources/HelpFiles/Menus/wb_view_Menu/Thumbs.db rename to src/Resources/Help/HelpFiles/Menus/wb_view_Menu/Thumbs.db diff --git a/src/Resources/GuiResources/HelpFiles/Menus/wb_view_Menu/wb_view_Menu.html b/src/Resources/Help/HelpFiles/Menus/wb_view_Menu/wb_view_Menu.html similarity index 100% rename from src/Resources/GuiResources/HelpFiles/Menus/wb_view_Menu/wb_view_Menu.html rename to src/Resources/Help/HelpFiles/Menus/wb_view_Menu/wb_view_Menu.html diff --git a/src/Resources/GuiResources/HelpFiles/Menus/wb_view_Menu/wb_view_Menu.png b/src/Resources/Help/HelpFiles/Menus/wb_view_Menu/wb_view_Menu.png similarity index 100% rename from src/Resources/GuiResources/HelpFiles/Menus/wb_view_Menu/wb_view_Menu.png rename to src/Resources/Help/HelpFiles/Menus/wb_view_Menu/wb_view_Menu.png diff --git a/src/Resources/GuiResources/HelpFiles/Mouse_Controls/Mouse_Controls.html b/src/Resources/Help/HelpFiles/Mouse_Controls/Mouse_Controls.html similarity index 100% rename from src/Resources/GuiResources/HelpFiles/Mouse_Controls/Mouse_Controls.html rename to src/Resources/Help/HelpFiles/Mouse_Controls/Mouse_Controls.html diff --git a/src/Resources/GuiResources/HelpFiles/Mouse_Controls/RightClick_Surface.png b/src/Resources/Help/HelpFiles/Mouse_Controls/RightClick_Surface.png similarity index 100% rename from src/Resources/GuiResources/HelpFiles/Mouse_Controls/RightClick_Surface.png rename to src/Resources/Help/HelpFiles/Mouse_Controls/RightClick_Surface.png diff --git a/src/Resources/GuiResources/HelpFiles/Mouse_Controls/RightClick_Volume.png b/src/Resources/Help/HelpFiles/Mouse_Controls/RightClick_Volume.png similarity index 100% rename from src/Resources/GuiResources/HelpFiles/Mouse_Controls/RightClick_Volume.png rename to src/Resources/Help/HelpFiles/Mouse_Controls/RightClick_Volume.png diff --git a/src/Resources/GuiResources/HelpFiles/Mouse_Controls/Thumbs.db b/src/Resources/Help/HelpFiles/Mouse_Controls/Thumbs.db similarity index 100% rename from src/Resources/GuiResources/HelpFiles/Mouse_Controls/Thumbs.db rename to src/Resources/Help/HelpFiles/Mouse_Controls/Thumbs.db diff --git a/src/Resources/GuiResources/HelpFiles/Open_Spec_File_Dialog/Open_Spec.png b/src/Resources/Help/HelpFiles/Open_Spec_File_Dialog/Open_Spec.png similarity index 100% rename from src/Resources/GuiResources/HelpFiles/Open_Spec_File_Dialog/Open_Spec.png rename to src/Resources/Help/HelpFiles/Open_Spec_File_Dialog/Open_Spec.png diff --git a/src/Resources/GuiResources/HelpFiles/Open_Spec_File_Dialog/Open_Spec_File_Dialog.html b/src/Resources/Help/HelpFiles/Open_Spec_File_Dialog/Open_Spec_File_Dialog.html similarity index 100% rename from src/Resources/GuiResources/HelpFiles/Open_Spec_File_Dialog/Open_Spec_File_Dialog.html rename to src/Resources/Help/HelpFiles/Open_Spec_File_Dialog/Open_Spec_File_Dialog.html diff --git a/src/Resources/GuiResources/HelpFiles/Open_Spec_File_Dialog/Thumbs.db b/src/Resources/Help/HelpFiles/Open_Spec_File_Dialog/Thumbs.db similarity index 100% rename from src/Resources/GuiResources/HelpFiles/Open_Spec_File_Dialog/Thumbs.db rename to src/Resources/Help/HelpFiles/Open_Spec_File_Dialog/Thumbs.db diff --git a/src/Resources/GuiResources/HelpFiles/Open_Spec_File_Dialog/gear-button.png b/src/Resources/Help/HelpFiles/Open_Spec_File_Dialog/gear-button.png similarity index 100% rename from src/Resources/GuiResources/HelpFiles/Open_Spec_File_Dialog/gear-button.png rename to src/Resources/Help/HelpFiles/Open_Spec_File_Dialog/gear-button.png diff --git a/src/Resources/Help/HelpFiles/Overlay_Toolbox/Chart_Layers/Chart Layers.html b/src/Resources/Help/HelpFiles/Overlay_Toolbox/Chart_Layers/Chart Layers.html new file mode 100644 index 0000000000000000000000000000000000000000..e777ae7883cdd5a5580a663fac131a2401ba92bc --- /dev/null +++ b/src/Resources/Help/HelpFiles/Overlay_Toolbox/Chart_Layers/Chart Layers.html @@ -0,0 +1,58 @@ + + + + + + Chart Layers + + +

      Chart Layers

      +
      + The Chart Layers tab contain controls for the display of + charts.  Some controls are enabled only for a specific chart + type (Histogram, Line-Series, or Matrix).
      +
        +
      • On : Enables the layer for display as a chart.
        +
      • +
      • Load : Enables loading of line-series data from the file + selected in the overlay.  This is a file property so if the + file is in more than one layer, the check boxes will have the + same status.
        +
      • +
      • Settings Wrench :  Launches the Overlay and Map Settings + Dialog.
        +
      • +
      • Settings Color Bar: Displays a color bar in the graphics + window showing distribution of
        +
      • +
      • Settings Construction : Displays a menu for adding, moving, + and removing layers.
        +
      • +
      • Settings Matrix : Selects view of matrix to the full matrix, + full with no diagonal, or an lower/upper triangular view.
        +
      • +
      • Settings Vertical Axis : Selects location of the vertical axis + to the left or right.
        +
      • +
      • File : Selects file for the layer.
        +
      • +
      • Yoke : Sets map yoking between layers to a map yoking group + denoted by a Roman numeral. Navigation between maps of layers in + the same map yoking group will be yoked (move together). If + files with differing numbers of maps are set to the same map + yoking group, a warning will appear before yoking is activated. +
        +
      • +
      • Index : Selects index of row or map.
        +
      • +
      • All : For histogram, allows one to display a histogram + containing data from all maps instead of the selected map.
        +
      • +
      • Name : Allows selection of the map by Name.
        +
      • +
      +
      +
      +
      + + diff --git a/src/Resources/Help/HelpFiles/Overlay_Toolbox/Chart_Layers/LineChartingLayers.png b/src/Resources/Help/HelpFiles/Overlay_Toolbox/Chart_Layers/LineChartingLayers.png new file mode 100644 index 0000000000000000000000000000000000000000..1b18b5f299a3159db9915ef97c9cd9f25117b64f Binary files /dev/null and b/src/Resources/Help/HelpFiles/Overlay_Toolbox/Chart_Layers/LineChartingLayers.png differ diff --git a/src/Resources/GuiResources/HelpFiles/Overlay_Toolbox/Charting/Charting.html b/src/Resources/Help/HelpFiles/Overlay_Toolbox/Charting/Charting.html similarity index 90% rename from src/Resources/GuiResources/HelpFiles/Overlay_Toolbox/Charting/Charting.html rename to src/Resources/Help/HelpFiles/Overlay_Toolbox/Charting/Charting.html index 0b7d0b4ebc8c45e71ba77cbef4f5fab5383ff17b..e56f8ce883b96880d67b9b7d65c35d98a28da942 100644 --- a/src/Resources/GuiResources/HelpFiles/Overlay_Toolbox/Charting/Charting.html +++ b/src/Resources/Help/HelpFiles/Overlay_Toolbox/Charting/Charting.html @@ -1,15 +1,20 @@ - - - - - Charting - - - Charting
      - The Charting tab in the Overlay Toolbox - contains controls for display of data graphs and matrices. The + + + + Charting + + + Charting
      +
      + This is the old version of charting and users are recommended to + use the new version of charting (Chart Layers) for any new charting + activities.
      +
      + The Charting tab in the
      Overlay + Toolbox contains controls for display of data graphs and + matrices. The Active @@ -19,79 +24,81 @@ - - Tab must be in must be in Chart - - View in order for this tab to be active (not - grayed-out). For Data/Time Series chart type, Select checkboxes - indicate whether file(s) will be charted when a brainordinate is - identified (in another Viewing Tab).  -
        -
      • Loading shows loaded Data - Series, Matrix, OR Time Series files to select for charting, - depending on which Chart Type is selected in the Toolbar.
      • -
      -
        -
          -
        • For Data/Time Series - chart type, Select checkboxes indicate whether - file(s) will be charted when a brainordinate - is identified (in another Viewing Tab).
          -
        • -
        • For Matrix chart type, - select a loaded file from the dropdown (only one matrix file - can be shown at a time). As in the Layers tab, wrench and - colorbar settings buttons allow one to set the palette for - the data displayed in the matrix and display a colorbar of - the palette spectrum, respectively.
          -
        • -
          -
        -
        -
          -
        -
      • History shows a list and line - color for each identified vertex that has been charted. - History is only active for - Data/Time Series chart types.
      • -
      -
        -
          -
        • The chart displays - a data - value (y-axis) for each Map Index/unit of time (x-axis). - The color of the selected brainordinate will match the - color of (one of) the chart(s) graphed for that - brainordinate.
          -
        • - -
        • Each charted - graph can be turned on/off of the display with the - checkboxes. Most recent charts are listed at the top of - the history.
          -
        • - -
        • Show Average displays an - averaged chart for all of the data displayed on - the chart (charts graphed from different files will be - averaged together). If displayed charts have differing - numbers of data points, the average will have the same - number of datapoints as the last graphed chart (listed - at top).
          -
        • -
        • Show last selects the total - number of charts to be displayed both in the Viewing - Area and in the History. This setting is not retroactive - and one may want to set this for a high value if - multiple files are being charted at once. If a - brainordinate "falls out" of the listed history/display, - it will be displayed as the brainordinate ID symbol - color set in in order for this tab to be active (not + grayed-out). For Data/Time Series chart type, Select checkboxes + indicate whether file(s) will be charted when a brainordinate is + identified (in another Viewing Tab).  +
            +
          • Loading shows loaded Data + Series, Matrix, OR Time Series files to select for charting, + depending on which Chart Type is selected in the Toolbar.
          • +
          +
            +
              +
            • For Data/Time Series + chart type, Select checkboxes indicate whether + file(s) will be charted when a brainordinate + is identified (in another Viewing Tab).
              +
            • +
            • For Matrix chart type, + select a loaded file from the dropdown (only one matrix file + can be shown at a time). As in the Layers tab, wrench and + colorbar settings buttons allow one to set the palette for + the data displayed in the matrix and display a colorbar of + the palette spectrum, respectively.
              +
            • +
              +
            +
            +
              +
            +
          • History shows a list and line + color for each identified vertex that has been charted. + History is only active for + Data/Time Series chart types.
          • +
          +
            +
              +
            • The chart displays + a data + value (y-axis) for each Map Index/unit of time (x-axis). + The color of the selected brainordinate will match the + color of (one of) the chart(s) graphed for that + brainordinate.
              +
            • + +
            • Each charted + graph can be turned on/off of the display with the + checkboxes. Most recent charts are listed at the top of + the history.
              +
            • + +
            • Show Average displays an + averaged chart for all of the data displayed on + the chart (charts graphed from different files will be + averaged together). If displayed charts have differing + numbers of data points, the average will have the same + number of datapoints as the last graphed chart (listed + at top).
              +
            • +
            • Show last selects the total + number of charts to be displayed both in the Viewing + Area and in the History. This setting is not retroactive + and one may want to set this for a high value if + multiple files are being charted at once. If a + brainordinate "falls out" of the listed history/display, + it will be displayed as the brainordinate ID symbol + color set in Information @@ -99,16 +106,17 @@ - - Window > Properties.
              -
            • -
              -
              -
            -
            -
            -
          - - + + + Window > Properties.

          +
        • +
          + +
        +
        +
        +
      + + diff --git a/src/Resources/GuiResources/HelpFiles/Overlay_Toolbox/Charting/Charting_History.png b/src/Resources/Help/HelpFiles/Overlay_Toolbox/Charting/Charting_History.png similarity index 100% rename from src/Resources/GuiResources/HelpFiles/Overlay_Toolbox/Charting/Charting_History.png rename to src/Resources/Help/HelpFiles/Overlay_Toolbox/Charting/Charting_History.png diff --git a/src/Resources/GuiResources/HelpFiles/Overlay_Toolbox/Charting/Charting_Loading.png b/src/Resources/Help/HelpFiles/Overlay_Toolbox/Charting/Charting_Loading.png similarity index 100% rename from src/Resources/GuiResources/HelpFiles/Overlay_Toolbox/Charting/Charting_Loading.png rename to src/Resources/Help/HelpFiles/Overlay_Toolbox/Charting/Charting_Loading.png diff --git a/src/Resources/GuiResources/HelpFiles/Overlay_Toolbox/Charting/Thumbs.db b/src/Resources/Help/HelpFiles/Overlay_Toolbox/Charting/Thumbs.db similarity index 100% rename from src/Resources/GuiResources/HelpFiles/Overlay_Toolbox/Charting/Thumbs.db rename to src/Resources/Help/HelpFiles/Overlay_Toolbox/Charting/Thumbs.db diff --git a/src/Resources/GuiResources/HelpFiles/Overlay_Toolbox/Connectivity/Connectivity.html b/src/Resources/Help/HelpFiles/Overlay_Toolbox/Connectivity/Connectivity.html similarity index 75% rename from src/Resources/GuiResources/HelpFiles/Overlay_Toolbox/Connectivity/Connectivity.html rename to src/Resources/Help/HelpFiles/Overlay_Toolbox/Connectivity/Connectivity.html index 73f326d6e126d2dbf1019e4e0ff76301ebbad828..41ff6e180f035f5c087c84b2c0668d836bd884da 100644 --- a/src/Resources/GuiResources/HelpFiles/Overlay_Toolbox/Connectivity/Connectivity.html +++ b/src/Resources/Help/HelpFiles/Overlay_Toolbox/Connectivity/Connectivity.html @@ -1,69 +1,82 @@ - - - - - Connectivity - - - Connectivity
      - The Connectivity tab in the Overlay Toolbox - contains a listing of loaded Connectivity and Fiber Orientation - files and links.
      -
      - URL links (loaded using Open Location... in File Menu) - to remotely stored files are often used to access large (>30 - GB) connectivity matrix files, loading one row of the matrix at a - time as needed, rather than having to locally store and load the - whole 90,000 x 90,000 matrix into memory.
      -
        -
      • On - checkboxes toggle on/off data loading from the listed - connectivity file for identified brainordinates (selected - by left clicking on surfaces or volumes). If checked, every - time a brainordinate is selected, data will be - loaded into memory from that file for that brainordinate, - regardless of whether it is displayed in any viewing tab. - For best performance, toggle off connectivity loading for - files not currently in use.
      • - -
      - -
        -
      • Copy - copies the data from the currently loaded matrix row - (for the currently selected brainordinate) and creates a - new *.dscalar file containing this map data. The new - file(s) created this way will appear and can be saved - (and renamed, if desired) in the + + + + Connectivity + + + Connectivity
        + The Connectivity tab in the
        Overlay Toolbox + contains a listing of loaded Connectivity (Dense, Dense Dynamic, + Parcel,  and Fiber Orientation) files and links.
        +
        + URL links (loaded using Open Location... in File Menu) + to remotely stored files are often used to access large (>30 + GB) connectivity matrix files, loading one row of the matrix at a + time as needed, rather than having to locally store and load the + whole 90,000 x 90,000 matrix into memory.
        +
          +
        • Load + checkboxes toggle on/off data loading from the listed + connectivity file for identified brainordinates (selected + by left clicking on surfaces or volumes). If checked, every + + time a brainordinate is selected, data will be + loaded into memory from that file for that brainordinate, + regardless of whether it is displayed in any viewing tab. + For best performance, toggle off connectivity loading for + files not currently in use.
        • + +
        + +
          +
        • Layer + Data-series files support "dynamic connectivity".  + When a brainordinate is identified, the corresponding + row to the brainordinate is correlated with all other + rows in the file to produce a connectivity map displayed + on the brainordinates.  This dynamic connectivity + produces the same data as found in a dense connectivity + file without needing to create the dense connectivity + file.  Note that dense connectivity files may be + very large (eg 9 GB).
          +
        • +
        • Copy + copies the data from the currently loaded matrix row + (for the currently selected brainordinate) and creates a + new *.dscalar file containing this map data. The new + file(s) created this way will appear and can be saved + (and renamed, if desired) in the Save/Manage - - Files Dialog.
        • - -
        - -
          -
        • Connectivity File - lists links or paths to loaded dense or - parcellated connectivity matrix files. For dMRI-based - tractography connectivity (trajectory or *.traj*) - files, an associated Fiber Orientation File - must also be loaded for display and is listed to the - right.
          -

          -
        • -
        -
        -
          -
        -
        - - + + + Files Dialog.
      • + +
      + +
        +
      • Connectivity File + lists links or paths to loaded dense or + parcellated connectivity matrix files. For dMRI-based + tractography connectivity (trajectory or *.traj*) + files, an associated Fiber Orientation File + must also be loaded for display and is listed to the + right.
        +

        +
      • +
      +
      +
        +
      +
      + + diff --git a/src/Resources/GuiResources/HelpFiles/Overlay_Toolbox/Connectivity/Connectivity.png b/src/Resources/Help/HelpFiles/Overlay_Toolbox/Connectivity/Connectivity.png similarity index 100% rename from src/Resources/GuiResources/HelpFiles/Overlay_Toolbox/Connectivity/Connectivity.png rename to src/Resources/Help/HelpFiles/Overlay_Toolbox/Connectivity/Connectivity.png diff --git a/src/Resources/GuiResources/HelpFiles/Overlay_Toolbox/Layers/Add_Layer.png b/src/Resources/Help/HelpFiles/Overlay_Toolbox/Layers/Add_Layer.png similarity index 100% rename from src/Resources/GuiResources/HelpFiles/Overlay_Toolbox/Layers/Add_Layer.png rename to src/Resources/Help/HelpFiles/Overlay_Toolbox/Layers/Add_Layer.png diff --git a/src/Resources/GuiResources/HelpFiles/Overlay_Toolbox/Layers/Colorbar.png b/src/Resources/Help/HelpFiles/Overlay_Toolbox/Layers/Colorbar.png similarity index 100% rename from src/Resources/GuiResources/HelpFiles/Overlay_Toolbox/Layers/Colorbar.png rename to src/Resources/Help/HelpFiles/Overlay_Toolbox/Layers/Colorbar.png diff --git a/src/Resources/GuiResources/HelpFiles/Overlay_Toolbox/Layers/Layers.html b/src/Resources/Help/HelpFiles/Overlay_Toolbox/Layers/Layers.html similarity index 90% rename from src/Resources/GuiResources/HelpFiles/Overlay_Toolbox/Layers/Layers.html rename to src/Resources/Help/HelpFiles/Overlay_Toolbox/Layers/Layers.html index 282f2441e218b9361f6552cee61687fd605ca373..fd05fc28c68706453756c4a58639dce002755400 100644 --- a/src/Resources/GuiResources/HelpFiles/Overlay_Toolbox/Layers/Layers.html +++ b/src/Resources/Help/HelpFiles/Overlay_Toolbox/Layers/Layers.html @@ -1,84 +1,87 @@ - - - - - Layers - - - Layers
      - The Layers tab in the Overlay Toolbox - contains controls for display of data maps on surfaces or in volumes. - -
        -
      • By default, 3 layers are loaded into the - Layers tab. The top layer of displayed data is listed as the - first row, the second layer below that, etc., like a layer - cake.
      • -
      -
        -
      • Checkboxes under On at the left - indicate whether the data listed for that layer is being - displayed. When a top layer is unchecked, the layers - underneath that layer are visible.
      • -
      -
        -
      • The File pulldown sets the file - to display on that layer. Only loaded files that can be - displayed in the type of view set for the + + + + Layers + + + Layers
        + The Layers tab in the
        Overlay Toolbox + contains controls for display of data maps on surfaces or in volumes. + +
          +
        • By default, 3 layers are loaded into the + Layers tab. The top layer of displayed data is listed as the + first row, the second layer below that, etc., like a layer + cake.
        • +
        +
          +
        • Checkboxes under On at the left + indicate whether the data listed for that layer is being + displayed. When a top layer is unchecked, the layers + underneath that layer are visible.
        • +
        +
          +
        • The File pulldown sets the file + to display on that layer. Only loaded files that can be + displayed in the type of view set for the Active - - Tab will be options. Files can - contain one or more brain Maps that may be selected by - number or name in the pulldowns to the right.
        • -
        -
          -
        • Clicking the - button opens the Tab will be options. Files can + contain one or more brain Maps that may be selected by + number or name in the pulldowns to the right.
        • +
        +
          +
        • Clicking the + button opens the Overlay - - and Map Settings box that contains several - tabs for setting display options for the different types of - Layers data: Labels, Layer (volume layer settings), Metadata, - Palette, Parcels, Trajectory.
        • -
        -
          -
        • Clicking the - button toggles on the colorbar for the displayed layer's - palette. The values at the top of the colorbar change with - each map to match the range of data displayed.
        • -
        -
          -
        • Clicking the - button shows options for moving, adding and removing layers - relative to the layer in which this button was selected. -
        • -
        -
          -
        • Opacity sets the opacity of the - layer. 1.0 is 100% opaque. If the opacity is set to a low - value, layers under the selected layer will show through. -
        • -
        -
          -
        • Yoke sets map yoking between - layers to a map yoking group denoted by a Roman numeral. - Navigation between maps of layers in the same map yoking group - will be yoked (move together). If files with differing numbers - of maps are set to the same map yoking group, a warning will - appear before yoking is activated. 
          -
        • -
        -
        -
        -
        - - + + + and Map Settings box that contains several + tabs for setting display options for the different types of + Layers data: Labels, Layer (volume layer settings), Metadata, + Palette, Parcels, Trajectory.
      • +
      +
        +
      • Clicking the + button toggles on the colorbar for the displayed layer's + palette. The values at the top of the colorbar change with + each map to match the range of data displayed.
      • +
      +
        +
      • Clicking the + button shows options for moving, adding and removing layers + relative to the layer in which this button was selected. +
      • +
      +
        +
      • Opacity sets the opacity of the + layer. 1.0 is 100% opaque. If the opacity is set to a low + value, layers under the selected layer will show through. +
      • +
      +
        +
      • Yoke sets map yoking between + layers to a map yoking group denoted by a Roman numeral. + Navigation between maps of layers in the same map yoking group + will be yoked (move together). If files with differing numbers + of maps are set to the same map yoking group, a warning will + appear before yoking is activated. 
        +
      • +
      +
      +
      +
      + + diff --git a/src/Resources/GuiResources/HelpFiles/Overlay_Toolbox/Layers/Overlay_Toolbox_horiz.png b/src/Resources/Help/HelpFiles/Overlay_Toolbox/Layers/Overlay_Toolbox_horiz.png similarity index 100% rename from src/Resources/GuiResources/HelpFiles/Overlay_Toolbox/Layers/Overlay_Toolbox_horiz.png rename to src/Resources/Help/HelpFiles/Overlay_Toolbox/Layers/Overlay_Toolbox_horiz.png diff --git a/src/Resources/Help/HelpFiles/Overlay_Toolbox/Layers/Overlay_and_Map_Settings/Overlay_Toolbox_horiz.png b/src/Resources/Help/HelpFiles/Overlay_Toolbox/Layers/Overlay_and_Map_Settings/Overlay_Toolbox_horiz.png new file mode 100644 index 0000000000000000000000000000000000000000..652041fa71d1f5b7cb9d443d87ded7bb15c6fbf2 Binary files /dev/null and b/src/Resources/Help/HelpFiles/Overlay_Toolbox/Layers/Overlay_and_Map_Settings/Overlay_Toolbox_horiz.png differ diff --git a/src/Resources/GuiResources/HelpFiles/Overlay_Toolbox/Layers/Overlay_and_Map_Settings/Overlay_and_Map_Settings.html b/src/Resources/Help/HelpFiles/Overlay_Toolbox/Layers/Overlay_and_Map_Settings/Overlay_and_Map_Settings.html similarity index 100% rename from src/Resources/GuiResources/HelpFiles/Overlay_Toolbox/Layers/Overlay_and_Map_Settings/Overlay_and_Map_Settings.html rename to src/Resources/Help/HelpFiles/Overlay_Toolbox/Layers/Overlay_and_Map_Settings/Overlay_and_Map_Settings.html diff --git a/src/Resources/GuiResources/HelpFiles/Overlay_Toolbox/Layers/Overlay_and_Map_Settings/Palette_Settings.png b/src/Resources/Help/HelpFiles/Overlay_Toolbox/Layers/Overlay_and_Map_Settings/Palette_Settings.png similarity index 100% rename from src/Resources/GuiResources/HelpFiles/Overlay_Toolbox/Layers/Overlay_and_Map_Settings/Palette_Settings.png rename to src/Resources/Help/HelpFiles/Overlay_Toolbox/Layers/Overlay_and_Map_Settings/Palette_Settings.png diff --git a/src/Resources/GuiResources/HelpFiles/Overlay_Toolbox/Layers/Overlay_and_Map_Settings/Settings_icon.png b/src/Resources/Help/HelpFiles/Overlay_Toolbox/Layers/Overlay_and_Map_Settings/Settings_icon.png similarity index 100% rename from src/Resources/GuiResources/HelpFiles/Overlay_Toolbox/Layers/Overlay_and_Map_Settings/Settings_icon.png rename to src/Resources/Help/HelpFiles/Overlay_Toolbox/Layers/Overlay_and_Map_Settings/Settings_icon.png diff --git a/src/Resources/GuiResources/HelpFiles/Overlay_Toolbox/Layers/Settings_icon.png b/src/Resources/Help/HelpFiles/Overlay_Toolbox/Layers/Settings_icon.png similarity index 100% rename from src/Resources/GuiResources/HelpFiles/Overlay_Toolbox/Layers/Settings_icon.png rename to src/Resources/Help/HelpFiles/Overlay_Toolbox/Layers/Settings_icon.png diff --git a/src/Resources/GuiResources/HelpFiles/Overlay_Toolbox/Overlay_Toolbox.html b/src/Resources/Help/HelpFiles/Overlay_Toolbox/Overlay_Toolbox.html similarity index 82% rename from src/Resources/GuiResources/HelpFiles/Overlay_Toolbox/Overlay_Toolbox.html rename to src/Resources/Help/HelpFiles/Overlay_Toolbox/Overlay_Toolbox.html index 60de90ea3a37d5e8f149ffa438338a53ff55b9a9..a07d3dd5d52e180942ece45cb0b5bd33ce60ea72 100644 --- a/src/Resources/GuiResources/HelpFiles/Overlay_Toolbox/Overlay_Toolbox.html +++ b/src/Resources/Help/HelpFiles/Overlay_Toolbox/Overlay_Toolbox.html @@ -1,70 +1,80 @@ - - - - - Overlay Toolbox - - - Overlay Toolbox
      - The Overlay Toolbox contains controls for - setting the data to be displayed on structures and in charts.
      -  
      - By default, the Overlay Toolbox is attached to the bottom of the Workbench - Window, under the  + + + + Overlay Toolbox + + + Overlay Toolbox
      + The Overlay Toolbox contains controls for + setting the data to be displayed on structures and in charts.
      +  
      + By default, the Overlay Toolbox is attached to the bottom of the
      Workbench + Window, under the Viewing - - Area, in a horizontal configuration (top image). - It can also be attached to the left side - of the Viewing Area, in a vertical configuration (bottom image, - set in View - menu >  Overlay Toolbox). Either - configuration can be detached from the Workbench Window by left - click + drag on the top of the - Overlay Toolbox. To reattach, double click on the top of the Overlay - Toolbox.
      -
      - There are 4 tabs in the Overlay Toolbox, each controlling a - different type of data viewing:
      -
        -
      • Layers - controls display of data maps on surfaces or in volumes.
      • -
      -
        -
      • Charting - controls which files are charted and, for data series/time - series, which data for identified vertices is being displayed - on a chart. 
      • -
      -
        -
      • Connectivity - controls which Connectivity and Fiber orientation files, data - is being loaded from for display.
      • -
      -
        -
      • Vol/Surf - Outline controls display of surface contours - on volume slices/planes.
        -
      • -
      - Tabs and files available for viewing change - depending on whether you are in Chart view, - Montage - view, Volume view, - All view, + + + + Area, in a horizontal configuration (top image). + It can also be attached to the left side + of the Viewing Area, in a vertical configuration (bottom image, + set in View + menu >  Overlay Toolbox). Either + configuration can be detached from the Workbench Window by left + click + drag on the top of the + Overlay Toolbox. To reattach, double click on the top of the Overlay + Toolbox.
      +
      + There are 4 tabs in the Overlay Toolbox, each controlling a + different type of data viewing:
      +
        +
      • Layers + controls display of data maps on surfaces or in volumes.
      • +
      • Chart Layers + controls new version of charting ("Chart" in + Toolbar->View).
        +
      • +
      +
        +
      • Charting + controls older version of charting ("Chart Old" in + Toolbar-> View) Set files that are charted and, for data + series/time series, which data for identified vertices is + being displayed on a chart. 
      • +
      +
        +
      • Connectivity + controls which Connectivity and Fiber orientation files, data + is being loaded from for display.
      • +
      +
        +
      • Vol/Surf + Outline controls display of surface contours + on volume slices/planes.
        +
      • +
      + Tabs and files available for viewing change + depending on whether you are in Chart view, + Montage + view, Volume view, + All view, or Surface - - view
      -
      -
        -
      -
      -
      -
      -
      - - + + + + view. 
      +
      +
        +
      +
      +
      +
      +
      + + diff --git a/src/Resources/Help/HelpFiles/Overlay_Toolbox/Overlay_Toolbox_horiz.png b/src/Resources/Help/HelpFiles/Overlay_Toolbox/Overlay_Toolbox_horiz.png new file mode 100644 index 0000000000000000000000000000000000000000..4038fa4c934e2923eb76fedd958d04955af48f37 Binary files /dev/null and b/src/Resources/Help/HelpFiles/Overlay_Toolbox/Overlay_Toolbox_horiz.png differ diff --git a/src/Resources/Help/HelpFiles/Overlay_Toolbox/Overlay_Toolbox_vert.png b/src/Resources/Help/HelpFiles/Overlay_Toolbox/Overlay_Toolbox_vert.png new file mode 100644 index 0000000000000000000000000000000000000000..da351da1a574b66dadf8a4cae688ed47eb28dbbf Binary files /dev/null and b/src/Resources/Help/HelpFiles/Overlay_Toolbox/Overlay_Toolbox_vert.png differ diff --git a/src/Resources/GuiResources/HelpFiles/Overlay_Toolbox/Thumbs.db b/src/Resources/Help/HelpFiles/Overlay_Toolbox/Thumbs.db similarity index 100% rename from src/Resources/GuiResources/HelpFiles/Overlay_Toolbox/Thumbs.db rename to src/Resources/Help/HelpFiles/Overlay_Toolbox/Thumbs.db diff --git a/src/Resources/GuiResources/HelpFiles/Overlay_Toolbox/Vol-Surf_Outline/Color-Vol-Surf_Outline.png b/src/Resources/Help/HelpFiles/Overlay_Toolbox/Vol-Surf_Outline/Color-Vol-Surf_Outline.png similarity index 100% rename from src/Resources/GuiResources/HelpFiles/Overlay_Toolbox/Vol-Surf_Outline/Color-Vol-Surf_Outline.png rename to src/Resources/Help/HelpFiles/Overlay_Toolbox/Vol-Surf_Outline/Color-Vol-Surf_Outline.png diff --git a/src/Resources/GuiResources/HelpFiles/Overlay_Toolbox/Vol-Surf_Outline/Tab-Vol-Surf_Outline.png b/src/Resources/Help/HelpFiles/Overlay_Toolbox/Vol-Surf_Outline/Tab-Vol-Surf_Outline.png similarity index 100% rename from src/Resources/GuiResources/HelpFiles/Overlay_Toolbox/Vol-Surf_Outline/Tab-Vol-Surf_Outline.png rename to src/Resources/Help/HelpFiles/Overlay_Toolbox/Vol-Surf_Outline/Tab-Vol-Surf_Outline.png diff --git a/src/Resources/GuiResources/HelpFiles/Overlay_Toolbox/Vol-Surf_Outline/Vol-Surf_Outline.html b/src/Resources/Help/HelpFiles/Overlay_Toolbox/Vol-Surf_Outline/Vol-Surf_Outline.html similarity index 87% rename from src/Resources/GuiResources/HelpFiles/Overlay_Toolbox/Vol-Surf_Outline/Vol-Surf_Outline.html rename to src/Resources/Help/HelpFiles/Overlay_Toolbox/Vol-Surf_Outline/Vol-Surf_Outline.html index b10ce5e83d7f8eaed43212ad9821e5c31cb9b071..c287c73996ab37c765d624f0b5fbdb8c6ae087c7 100644 --- a/src/Resources/GuiResources/HelpFiles/Overlay_Toolbox/Vol-Surf_Outline/Vol-Surf_Outline.html +++ b/src/Resources/Help/HelpFiles/Overlay_Toolbox/Vol-Surf_Outline/Vol-Surf_Outline.html @@ -1,61 +1,64 @@ - - - - - Vol/Surf Outline - - - Vol/Surf Outline
      -
      The Volume Surface - Outline tab (Vol/Surf Outline for short) in the Overlay Toolbox - allows one to set parameters for viewing brain surface - outlines on volume slices in the Volume or All (Whole Brain) + + + + Vol/Surf Outline + + + Vol/Surf Outline
      +
      The Volume Surface + Outline tab (Vol/Surf Outline for short) in the
      Overlay Toolbox + allows one to set parameters for viewing brain surface + outlines on volume slices in the Volume or All (Whole Brain) Viewing - - Tabs (inactive in all other Views). 
      -
      - This is useful for comparing Freesurfer, or otherwise - reconstructed, surfaces with volume slices, as a check of the - quality of the surface.  It is also useful for viewing - Connectivity data (dense connectivity and BOLD dense data series - files) on both the cortical ribbon and in subcortical voxels.
      -
        -
      • On checkbox toggles on/off a brain surface outlined - on a volume slice view.
      • -
      -
        -
      -
        -
      • Color Source sets the coloring of - the outline as provided by either (A) the displayed overlays - from another Tab, or (B) from a list of default solid colors.
      • -
      -
        -
      • Thickness sets the width of the - lines.
      • -
      -
        -
      • File selects which surface will be - used for the outline display.
        -
      • -
      -
      -
      -
        -
      - A
      -
      -
      - B
      -
      -
      - - + + + Tabs (inactive in all other Views). 
      +
      + This is useful for comparing Freesurfer, or otherwise + reconstructed, surfaces with volume slices, as a check of the + quality of the surface.  It is also useful for viewing + Connectivity data (dense connectivity and BOLD dense data series + files) on both the cortical ribbon and in subcortical voxels.
      +
        +
      • On checkbox toggles on/off a brain surface outlined + on a volume slice view.
      • +
      +
        +
      +
        +
      • Color Source sets the coloring of + the outline as provided by either (A) the displayed overlays + from another Tab, or (B) from a list of default solid colors.
      • +
      +
        +
      • Thickness sets the width of the + lines as a percentage of the height of the tab in which the + volume is drawn.
        +
      • +
      +
        +
      • File selects which surface will be + used for the outline display.
        +
      • +
      +
      +
      +
        +
      + A
      +
      +
      + B
      +
      +
      + + diff --git a/src/Resources/GuiResources/HelpFiles/Overlay_Toolbox/Vol-Surf_Outline/Vol-Surf_Outline.png b/src/Resources/Help/HelpFiles/Overlay_Toolbox/Vol-Surf_Outline/Vol-Surf_Outline.png similarity index 100% rename from src/Resources/GuiResources/HelpFiles/Overlay_Toolbox/Vol-Surf_Outline/Vol-Surf_Outline.png rename to src/Resources/Help/HelpFiles/Overlay_Toolbox/Vol-Surf_Outline/Vol-Surf_Outline.png diff --git a/src/Resources/Help/HelpFiles/Scenes_Window/Create_New_Scene_File/Create_New_Scene.png b/src/Resources/Help/HelpFiles/Scenes_Window/Create_New_Scene_File/Create_New_Scene.png new file mode 100644 index 0000000000000000000000000000000000000000..735c47c7579e94d0186290331fb2af5920dffd82 Binary files /dev/null and b/src/Resources/Help/HelpFiles/Scenes_Window/Create_New_Scene_File/Create_New_Scene.png differ diff --git a/src/Resources/GuiResources/HelpFiles/Scenes_Window/Create_New_Scene_File/Create_New_Scene_File.html b/src/Resources/Help/HelpFiles/Scenes_Window/Create_New_Scene_File/Create_New_Scene_File.html similarity index 100% rename from src/Resources/GuiResources/HelpFiles/Scenes_Window/Create_New_Scene_File/Create_New_Scene_File.html rename to src/Resources/Help/HelpFiles/Scenes_Window/Create_New_Scene_File/Create_New_Scene_File.html diff --git a/src/Resources/GuiResources/HelpFiles/Scenes_Window/Create_New_Scene_File/Scenes button.png b/src/Resources/Help/HelpFiles/Scenes_Window/Create_New_Scene_File/Scenes button.png similarity index 100% rename from src/Resources/GuiResources/HelpFiles/Scenes_Window/Create_New_Scene_File/Scenes button.png rename to src/Resources/Help/HelpFiles/Scenes_Window/Create_New_Scene_File/Scenes button.png diff --git a/src/Resources/GuiResources/HelpFiles/Scenes_Window/Create_New_Scene_File/Thumbs.db b/src/Resources/Help/HelpFiles/Scenes_Window/Create_New_Scene_File/Thumbs.db similarity index 100% rename from src/Resources/GuiResources/HelpFiles/Scenes_Window/Create_New_Scene_File/Thumbs.db rename to src/Resources/Help/HelpFiles/Scenes_Window/Create_New_Scene_File/Thumbs.db diff --git a/src/Resources/Help/HelpFiles/Scenes_Window/Scenes_Window.html b/src/Resources/Help/HelpFiles/Scenes_Window/Scenes_Window.html new file mode 100644 index 0000000000000000000000000000000000000000..e06ebf8da9551548580adcc7cac65073bbc01dba --- /dev/null +++ b/src/Resources/Help/HelpFiles/Scenes_Window/Scenes_Window.html @@ -0,0 +1,140 @@ + + + + + Scenes Window + + + Scenes Window
      + The Scenes Window is the interface to + save the current Workbench session (loaded files and settings) as + a Scene + (in a .scene file).  Scenes allow one to save a snapshot of a + Workbench session that contains a record of all of the files + loaded and view settings, making it easy to get to a specific + display.  Using scenes, one can "pick up where they left off" + when reopening Workbench.
      +
      + File Section
      +
        +
      • Scene File selection is used to + choose among loaded Scene files. Use File>Open + File... to load an existing scene file and have it + show up in this selector.
      • +
      • New  Creates + a new Scene File.  A new Scene File is created with + no scenes within it. Add any number of scenes with the Add + button.
      • +
      • Save  Saves the selected + scene file using its current name.  This button is + disabled if the selected scene file has never been saved and + so the Save As button should be used to save the file.
        +
      • +
      • Zip  Creates a ZIP file + containing the scene file and all data files it references.
        +
      • +
      • List Files  Lists all data + files from all scenes within the scene file
        +
      • +
      • Open  Displays a file selection + dialog for selecting and opening a scene file.
        +
      • +
      • Save As   Displays a + file selection dialog for saving the scene file with a new + name.
        +
      • +
      • Upload   Displays a + dialog for uploading the scene file and its data files to + the BALSA database.
        +
      • +
      +


      + Show Scene Group
      +
      + +

        +
      • Show loads the active (highlighted in blue) + scene.  Double clicking on a scene's + title/description will also show the scene.
      • +
      • Preview... opens a separate window with an enlarged + display of the highlighted scene's thumbnail along with + the name and description.
      • +
      • Options  Use background and foreground colors + from scene when a scene is created, the foreground and + background colors are added to the scene and will be + used when the scene is displayed. Note that the + background and foreground colors are adjusted using + the Preferences Dialog. When a scene is loaded that + contains background and foreground colors, they will + override the user's Preferences colors until a + different scene is loaded, a spec file is loaded, or + this checkbox is unchecked.
      • +
      +

      +
      +

      Create Scene Group
      +

      +
      +
        +
      • Add... + creates a new scene within the current Scene File and adds + it to the bottom of the list of scenes.  The scene + needs to be set up before clicking this button.
      • +
      • Insert... creates a new scene that + is added to the scene list above the currently + highlighted scene.
      • +
      • Replace... is used to modify an existing + scene.  The highlighted scene will be + overwritten at the time the Replace button + is clicked, therefore make scene changes before + clicking this button.   When a scene is + replaced, the name/description of the scene can also + be edited.
        +
      • +
      + +

      Selected Scene Group +

        +
      • Move Up moves the selected + scene up one position in the list of scenes.
      • +
      • Move Down moves the selected scene + down one position in the list of scenes.
      • +
      • Delete Deletes the highlighted + scene from the scene file.
        +
      • +
      +

      +
      +

      Testing Group
      +

      +
      +
        +
      • Replace All Loads and saves each scene in the scene + file.
      • +
      • Test All  Loads each scene in the scene + file.  At the conclusion, a dialog is displayed containing + each scene's preview image next to an image captured after + loading the scene.
        +
      • +
      + +
        +
      +
      +
      +
      +
        +
      +
      + + diff --git a/src/Resources/Help/HelpFiles/Scenes_Window/Scenes_Window.png b/src/Resources/Help/HelpFiles/Scenes_Window/Scenes_Window.png new file mode 100644 index 0000000000000000000000000000000000000000..0b3848d9bc3165d25e6dd7184c0b831e96810d3a Binary files /dev/null and b/src/Resources/Help/HelpFiles/Scenes_Window/Scenes_Window.png differ diff --git a/src/Resources/GuiResources/HelpFiles/Scenes_Window/Thumbs.db b/src/Resources/Help/HelpFiles/Scenes_Window/Thumbs.db similarity index 100% rename from src/Resources/GuiResources/HelpFiles/Scenes_Window/Thumbs.db rename to src/Resources/Help/HelpFiles/Scenes_Window/Thumbs.db diff --git a/src/Resources/GuiResources/HelpFiles/Select_Brainordinate_Window/CiftiFileRow.png b/src/Resources/Help/HelpFiles/Select_Brainordinate_Window/CiftiFileRow.png similarity index 100% rename from src/Resources/GuiResources/HelpFiles/Select_Brainordinate_Window/CiftiFileRow.png rename to src/Resources/Help/HelpFiles/Select_Brainordinate_Window/CiftiFileRow.png diff --git a/src/Resources/GuiResources/HelpFiles/Select_Brainordinate_Window/Indentify_Brainordinate.png b/src/Resources/Help/HelpFiles/Select_Brainordinate_Window/Indentify_Brainordinate.png similarity index 100% rename from src/Resources/GuiResources/HelpFiles/Select_Brainordinate_Window/Indentify_Brainordinate.png rename to src/Resources/Help/HelpFiles/Select_Brainordinate_Window/Indentify_Brainordinate.png diff --git a/src/Resources/GuiResources/HelpFiles/Select_Brainordinate_Window/Select_Brainordinate_Window.html b/src/Resources/Help/HelpFiles/Select_Brainordinate_Window/Select_Brainordinate_Window.html similarity index 100% rename from src/Resources/GuiResources/HelpFiles/Select_Brainordinate_Window/Select_Brainordinate_Window.html rename to src/Resources/Help/HelpFiles/Select_Brainordinate_Window/Select_Brainordinate_Window.html diff --git a/src/Resources/GuiResources/HelpFiles/Splash_Screen/Splash_Screen.html b/src/Resources/Help/HelpFiles/Splash_Screen/Splash_Screen.html similarity index 100% rename from src/Resources/GuiResources/HelpFiles/Splash_Screen/Splash_Screen.html rename to src/Resources/Help/HelpFiles/Splash_Screen/Splash_Screen.html diff --git a/src/Resources/GuiResources/HelpFiles/Splash_Screen/Splash_screen.png b/src/Resources/Help/HelpFiles/Splash_Screen/Splash_screen.png similarity index 100% rename from src/Resources/GuiResources/HelpFiles/Splash_Screen/Splash_screen.png rename to src/Resources/Help/HelpFiles/Splash_Screen/Splash_screen.png diff --git a/src/Resources/GuiResources/HelpFiles/Splash_Screen/Thumbs.db b/src/Resources/Help/HelpFiles/Splash_Screen/Thumbs.db similarity index 100% rename from src/Resources/GuiResources/HelpFiles/Splash_Screen/Thumbs.db rename to src/Resources/Help/HelpFiles/Splash_Screen/Thumbs.db diff --git a/src/Resources/Help/HelpFiles/Toolbar/AllToolbar.png b/src/Resources/Help/HelpFiles/Toolbar/AllToolbar.png new file mode 100644 index 0000000000000000000000000000000000000000..99bce50ddb3d041f9e3ecae296592ad4c8ca219b Binary files /dev/null and b/src/Resources/Help/HelpFiles/Toolbar/AllToolbar.png differ diff --git a/src/Resources/GuiResources/HelpFiles/Toolbar/Clipping/Clipping.html b/src/Resources/Help/HelpFiles/Toolbar/Clipping/Clipping.html similarity index 100% rename from src/Resources/GuiResources/HelpFiles/Toolbar/Clipping/Clipping.html rename to src/Resources/Help/HelpFiles/Toolbar/Clipping/Clipping.html diff --git a/src/Resources/GuiResources/HelpFiles/Toolbar/Clipping/Clipping_Planes.png b/src/Resources/Help/HelpFiles/Toolbar/Clipping/Clipping_Planes.png similarity index 100% rename from src/Resources/GuiResources/HelpFiles/Toolbar/Clipping/Clipping_Planes.png rename to src/Resources/Help/HelpFiles/Toolbar/Clipping/Clipping_Planes.png diff --git a/src/Resources/GuiResources/HelpFiles/Toolbar/Clipping/Thumbs.db b/src/Resources/Help/HelpFiles/Toolbar/Clipping/Thumbs.db similarity index 100% rename from src/Resources/GuiResources/HelpFiles/Toolbar/Clipping/Thumbs.db rename to src/Resources/Help/HelpFiles/Toolbar/Clipping/Thumbs.db diff --git a/src/Resources/GuiResources/HelpFiles/Toolbar/Custom_Orientation/Custom_Orientation.html b/src/Resources/Help/HelpFiles/Toolbar/Custom_Orientation/Custom_Orientation.html similarity index 96% rename from src/Resources/GuiResources/HelpFiles/Toolbar/Custom_Orientation/Custom_Orientation.html rename to src/Resources/Help/HelpFiles/Toolbar/Custom_Orientation/Custom_Orientation.html index 5215cb80c67b8acff526bcb58cd5dd9cf0af7525..4324e146f99a87d7d151655af40b8e70da6e78f0 100644 --- a/src/Resources/GuiResources/HelpFiles/Toolbar/Custom_Orientation/Custom_Orientation.html +++ b/src/Resources/Help/HelpFiles/Toolbar/Custom_Orientation/Custom_Orientation.html @@ -1,72 +1,72 @@ - - - - - Custom Orientation - - - Custom Orientation
      -
      The Custom button - in the Orientation/Slice Plane sections of the Toolbar allows the user - to set, save, edit, or access custom orientations for Montage, - Volume, All, and Surface Views.  - -
        -
      • Click Custom and Create and - Edit... to open the Custom Orientation settings box:
        -
      • -
      -
        -
      -  
      - -
        -
      • Workbench Window sets the Workbench - Window to which the orientation transform - will be applied. Note: All tabs will have the transform - applied.
      • -
      -
        -
      • Pan (X, Y) sets the - amount of X, Y, Z offset from the center.
      • -
      -
        -
      • Rotate (X, Y, Z) sets the - rotation about the x-, y-, and z-axes.
      • -
      -
        -
      • Oblique Rotate (X, Y, Z) - sets the rotation about the oblique x-, y-, and z-axes in Volume - or All - views-- only active when Oblique is selected in Slice - Indices/Coords.
      • -
      -
        -
      • Zoom sets zoom factor relative - to the default of 1.
      • -
      -
        -
      • Copy copies the settings from - the Transform fields to the selected (highlighted) Custom - Orientation file listed on the right.
      • -
      -
        -
      • Load takes the settings from the - selected (highlighted) Custom Orientation file listed on the - right and applies them to the structures in the selected - Window.
      • -
      -
        -
      • New.. and Delete... - allow one to create or delete Custom Orientations from the - list. The listed saved orientations are saved locally and will - be available every time Workbench is opened on the same - system.
        -
      • -
      - - + + + + + Custom Orientation + + + Custom Orientation
      +
      The Custom button + in the Orientation/Slice Plane sections of the Toolbar allows the user + to set, save, edit, or access custom orientations for Montage, + Volume, All, and Surface Views.  + +
        +
      • Click Custom and Create and + Edit... to open the Custom Orientation settings box:
        +
      • +
      +
        +
      +  
      + +
        +
      • Workbench Window sets the Workbench + Window to which the orientation transform + will be applied. Note: All tabs will have the transform + applied.
      • +
      +
        +
      • Pan (X, Y) sets the + amount of X, Y, Z offset from the center.
      • +
      +
        +
      • Rotate (X, Y, Z) sets the + rotation about the x-, y-, and z-axes.
      • +
      +
        +
      • Oblique Rotate (X, Y, Z) + sets the rotation about the oblique x-, y-, and z-axes in Volume + or All + views-- only active when Oblique is selected in Slice + Indices/Coords.
      • +
      +
        +
      • Zoom sets zoom factor relative + to the default of 1.
      • +
      +
        +
      • Copy copies the settings from + the Transform fields to the selected (highlighted) Custom + Orientation file listed on the right.
      • +
      +
        +
      • Load takes the settings from the + selected (highlighted) Custom Orientation file listed on the + right and applies them to the structures in the selected + Window.
      • +
      +
        +
      • New.. and Delete... + allow one to create or delete Custom Orientations from the + list. The listed saved orientations are saved locally and will + be available every time Workbench is opened on the same + system.
        +
      • +
      + + diff --git a/src/Resources/GuiResources/HelpFiles/Toolbar/Custom_Orientation/Custom_Orientation.png b/src/Resources/Help/HelpFiles/Toolbar/Custom_Orientation/Custom_Orientation.png similarity index 100% rename from src/Resources/GuiResources/HelpFiles/Toolbar/Custom_Orientation/Custom_Orientation.png rename to src/Resources/Help/HelpFiles/Toolbar/Custom_Orientation/Custom_Orientation.png diff --git a/src/Resources/Help/HelpFiles/Toolbar/HistogramLineChartToolbar.png b/src/Resources/Help/HelpFiles/Toolbar/HistogramLineChartToolbar.png new file mode 100644 index 0000000000000000000000000000000000000000..782b1094013cec3fa91680429a101b1251e91529 Binary files /dev/null and b/src/Resources/Help/HelpFiles/Toolbar/HistogramLineChartToolbar.png differ diff --git a/src/Resources/Help/HelpFiles/Toolbar/LineSeriesChartOldToolbar.png b/src/Resources/Help/HelpFiles/Toolbar/LineSeriesChartOldToolbar.png new file mode 100644 index 0000000000000000000000000000000000000000..640a9c22fb38812708aae86bf3595c664be5b423 Binary files /dev/null and b/src/Resources/Help/HelpFiles/Toolbar/LineSeriesChartOldToolbar.png differ diff --git a/src/Resources/Help/HelpFiles/Toolbar/MatrixChartOldToolbar.png b/src/Resources/Help/HelpFiles/Toolbar/MatrixChartOldToolbar.png new file mode 100644 index 0000000000000000000000000000000000000000..3e60ccb8ede4f0d8fc45adceacbd2e075723e1c5 Binary files /dev/null and b/src/Resources/Help/HelpFiles/Toolbar/MatrixChartOldToolbar.png differ diff --git a/src/Resources/Help/HelpFiles/Toolbar/MatrixChartToolbar.png b/src/Resources/Help/HelpFiles/Toolbar/MatrixChartToolbar.png new file mode 100644 index 0000000000000000000000000000000000000000..4bee7593c0c7e7ffd0fa4702a3935357dbce658e Binary files /dev/null and b/src/Resources/Help/HelpFiles/Toolbar/MatrixChartToolbar.png differ diff --git a/src/Resources/GuiResources/HelpFiles/Toolbar/Mode/Border_Drawing/Border_Drawing.html b/src/Resources/Help/HelpFiles/Toolbar/Mode/Border_Drawing/Border_Drawing.html similarity index 100% rename from src/Resources/GuiResources/HelpFiles/Toolbar/Mode/Border_Drawing/Border_Drawing.html rename to src/Resources/Help/HelpFiles/Toolbar/Mode/Border_Drawing/Border_Drawing.html diff --git a/src/Resources/GuiResources/HelpFiles/Toolbar/Mode/Border_Drawing/Border_Editing/Border_Editing.html b/src/Resources/Help/HelpFiles/Toolbar/Mode/Border_Drawing/Border_Editing/Border_Editing.html similarity index 100% rename from src/Resources/GuiResources/HelpFiles/Toolbar/Mode/Border_Drawing/Border_Editing/Border_Editing.html rename to src/Resources/Help/HelpFiles/Toolbar/Mode/Border_Drawing/Border_Editing/Border_Editing.html diff --git a/src/Resources/GuiResources/HelpFiles/Toolbar/Mode/Border_Drawing/Border_Editing/Border_edit.png b/src/Resources/Help/HelpFiles/Toolbar/Mode/Border_Drawing/Border_Editing/Border_edit.png similarity index 100% rename from src/Resources/GuiResources/HelpFiles/Toolbar/Mode/Border_Drawing/Border_Editing/Border_edit.png rename to src/Resources/Help/HelpFiles/Toolbar/Mode/Border_Drawing/Border_Editing/Border_edit.png diff --git a/src/Resources/GuiResources/HelpFiles/Toolbar/Mode/Border_Drawing/Border_Editing/Edit_Border_Properties.png b/src/Resources/Help/HelpFiles/Toolbar/Mode/Border_Drawing/Border_Editing/Edit_Border_Properties.png similarity index 100% rename from src/Resources/GuiResources/HelpFiles/Toolbar/Mode/Border_Drawing/Border_Editing/Edit_Border_Properties.png rename to src/Resources/Help/HelpFiles/Toolbar/Mode/Border_Drawing/Border_Editing/Edit_Border_Properties.png diff --git a/src/Resources/GuiResources/HelpFiles/Toolbar/Mode/Border_Drawing/Border_Editing/Thumbs.db b/src/Resources/Help/HelpFiles/Toolbar/Mode/Border_Drawing/Border_Editing/Thumbs.db similarity index 100% rename from src/Resources/GuiResources/HelpFiles/Toolbar/Mode/Border_Drawing/Border_Editing/Thumbs.db rename to src/Resources/Help/HelpFiles/Toolbar/Mode/Border_Drawing/Border_Editing/Thumbs.db diff --git a/src/Resources/GuiResources/HelpFiles/Toolbar/Mode/Border_Drawing/Border_drawing.png b/src/Resources/Help/HelpFiles/Toolbar/Mode/Border_Drawing/Border_drawing.png similarity index 100% rename from src/Resources/GuiResources/HelpFiles/Toolbar/Mode/Border_Drawing/Border_drawing.png rename to src/Resources/Help/HelpFiles/Toolbar/Mode/Border_Drawing/Border_drawing.png diff --git a/src/Resources/GuiResources/HelpFiles/Toolbar/Mode/Border_Drawing/Create_ROI/Border_ROI.png b/src/Resources/Help/HelpFiles/Toolbar/Mode/Border_Drawing/Create_ROI/Border_ROI.png similarity index 100% rename from src/Resources/GuiResources/HelpFiles/Toolbar/Mode/Border_Drawing/Create_ROI/Border_ROI.png rename to src/Resources/Help/HelpFiles/Toolbar/Mode/Border_Drawing/Create_ROI/Border_ROI.png diff --git a/src/Resources/GuiResources/HelpFiles/Toolbar/Mode/Border_Drawing/Create_ROI/Create_ROI.html b/src/Resources/Help/HelpFiles/Toolbar/Mode/Border_Drawing/Create_ROI/Create_ROI.html similarity index 100% rename from src/Resources/GuiResources/HelpFiles/Toolbar/Mode/Border_Drawing/Create_ROI/Create_ROI.html rename to src/Resources/Help/HelpFiles/Toolbar/Mode/Border_Drawing/Create_ROI/Create_ROI.html diff --git a/src/Resources/GuiResources/HelpFiles/Toolbar/Mode/Border_Drawing/Create_ROI/Create_Region_of_Interest.png b/src/Resources/Help/HelpFiles/Toolbar/Mode/Border_Drawing/Create_ROI/Create_Region_of_Interest.png similarity index 100% rename from src/Resources/GuiResources/HelpFiles/Toolbar/Mode/Border_Drawing/Create_ROI/Create_Region_of_Interest.png rename to src/Resources/Help/HelpFiles/Toolbar/Mode/Border_Drawing/Create_ROI/Create_Region_of_Interest.png diff --git a/src/Resources/GuiResources/HelpFiles/Toolbar/Mode/Border_Drawing/Create_ROI/Thumbs.db b/src/Resources/Help/HelpFiles/Toolbar/Mode/Border_Drawing/Create_ROI/Thumbs.db similarity index 100% rename from src/Resources/GuiResources/HelpFiles/Toolbar/Mode/Border_Drawing/Create_ROI/Thumbs.db rename to src/Resources/Help/HelpFiles/Toolbar/Mode/Border_Drawing/Create_ROI/Thumbs.db diff --git a/src/Resources/GuiResources/HelpFiles/Toolbar/Mode/Border_Drawing/Thumbs.db b/src/Resources/Help/HelpFiles/Toolbar/Mode/Border_Drawing/Thumbs.db similarity index 100% rename from src/Resources/GuiResources/HelpFiles/Toolbar/Mode/Border_Drawing/Thumbs.db rename to src/Resources/Help/HelpFiles/Toolbar/Mode/Border_Drawing/Thumbs.db diff --git a/src/Resources/GuiResources/HelpFiles/Toolbar/Mode/Border_Tools.png b/src/Resources/Help/HelpFiles/Toolbar/Mode/Border_Tools.png similarity index 100% rename from src/Resources/GuiResources/HelpFiles/Toolbar/Mode/Border_Tools.png rename to src/Resources/Help/HelpFiles/Toolbar/Mode/Border_Tools.png diff --git a/src/Resources/GuiResources/HelpFiles/Toolbar/Mode/Foci_Creation/Create_Focus.png b/src/Resources/Help/HelpFiles/Toolbar/Mode/Foci_Creation/Create_Focus.png similarity index 100% rename from src/Resources/GuiResources/HelpFiles/Toolbar/Mode/Foci_Creation/Create_Focus.png rename to src/Resources/Help/HelpFiles/Toolbar/Mode/Foci_Creation/Create_Focus.png diff --git a/src/Resources/GuiResources/HelpFiles/Toolbar/Mode/Foci_Creation/Foci_Creation.html b/src/Resources/Help/HelpFiles/Toolbar/Mode/Foci_Creation/Foci_Creation.html similarity index 100% rename from src/Resources/GuiResources/HelpFiles/Toolbar/Mode/Foci_Creation/Foci_Creation.html rename to src/Resources/Help/HelpFiles/Toolbar/Mode/Foci_Creation/Foci_Creation.html diff --git a/src/Resources/GuiResources/HelpFiles/Toolbar/Mode/Foci_Creation/Foci_Editing/Edit_Focus_box.png b/src/Resources/Help/HelpFiles/Toolbar/Mode/Foci_Creation/Foci_Editing/Edit_Focus_box.png similarity index 100% rename from src/Resources/GuiResources/HelpFiles/Toolbar/Mode/Foci_Creation/Foci_Editing/Edit_Focus_box.png rename to src/Resources/Help/HelpFiles/Toolbar/Mode/Foci_Creation/Foci_Editing/Edit_Focus_box.png diff --git a/src/Resources/GuiResources/HelpFiles/Toolbar/Mode/Foci_Creation/Foci_Editing/Foci_Editing.html b/src/Resources/Help/HelpFiles/Toolbar/Mode/Foci_Creation/Foci_Editing/Foci_Editing.html similarity index 100% rename from src/Resources/GuiResources/HelpFiles/Toolbar/Mode/Foci_Creation/Foci_Editing/Foci_Editing.html rename to src/Resources/Help/HelpFiles/Toolbar/Mode/Foci_Creation/Foci_Editing/Foci_Editing.html diff --git a/src/Resources/GuiResources/HelpFiles/Toolbar/Mode/Foci_Creation/Foci_Editing/Foci_edit.png b/src/Resources/Help/HelpFiles/Toolbar/Mode/Foci_Creation/Foci_Editing/Foci_edit.png similarity index 100% rename from src/Resources/GuiResources/HelpFiles/Toolbar/Mode/Foci_Creation/Foci_Editing/Foci_edit.png rename to src/Resources/Help/HelpFiles/Toolbar/Mode/Foci_Creation/Foci_Editing/Foci_edit.png diff --git a/src/Resources/GuiResources/HelpFiles/Toolbar/Mode/Foci_Creation/Foci_Editing/Thumbs.db b/src/Resources/Help/HelpFiles/Toolbar/Mode/Foci_Creation/Foci_Editing/Thumbs.db similarity index 100% rename from src/Resources/GuiResources/HelpFiles/Toolbar/Mode/Foci_Creation/Foci_Editing/Thumbs.db rename to src/Resources/Help/HelpFiles/Toolbar/Mode/Foci_Creation/Foci_Editing/Thumbs.db diff --git a/src/Resources/GuiResources/HelpFiles/Toolbar/Mode/Foci_Creation/Foci_Tools.png b/src/Resources/Help/HelpFiles/Toolbar/Mode/Foci_Creation/Foci_Tools.png similarity index 100% rename from src/Resources/GuiResources/HelpFiles/Toolbar/Mode/Foci_Creation/Foci_Tools.png rename to src/Resources/Help/HelpFiles/Toolbar/Mode/Foci_Creation/Foci_Tools.png diff --git a/src/Resources/GuiResources/HelpFiles/Toolbar/Mode/Foci_Creation/Thumbs.db b/src/Resources/Help/HelpFiles/Toolbar/Mode/Foci_Creation/Thumbs.db similarity index 100% rename from src/Resources/GuiResources/HelpFiles/Toolbar/Mode/Foci_Creation/Thumbs.db rename to src/Resources/Help/HelpFiles/Toolbar/Mode/Foci_Creation/Thumbs.db diff --git a/src/Resources/GuiResources/HelpFiles/Toolbar/Mode/Foci_Tools.png b/src/Resources/Help/HelpFiles/Toolbar/Mode/Foci_Tools.png similarity index 100% rename from src/Resources/GuiResources/HelpFiles/Toolbar/Mode/Foci_Tools.png rename to src/Resources/Help/HelpFiles/Toolbar/Mode/Foci_Tools.png diff --git a/src/Resources/GuiResources/HelpFiles/Toolbar/Mode/Mode.html b/src/Resources/Help/HelpFiles/Toolbar/Mode/Mode.html similarity index 100% rename from src/Resources/GuiResources/HelpFiles/Toolbar/Mode/Mode.html rename to src/Resources/Help/HelpFiles/Toolbar/Mode/Mode.html diff --git a/src/Resources/GuiResources/HelpFiles/Toolbar/Mode/Thumbs.db b/src/Resources/Help/HelpFiles/Toolbar/Mode/Thumbs.db similarity index 100% rename from src/Resources/GuiResources/HelpFiles/Toolbar/Mode/Thumbs.db rename to src/Resources/Help/HelpFiles/Toolbar/Mode/Thumbs.db diff --git a/src/Resources/GuiResources/HelpFiles/Toolbar/Mode/Volume_Tools.png b/src/Resources/Help/HelpFiles/Toolbar/Mode/Volume_Tools.png similarity index 100% rename from src/Resources/GuiResources/HelpFiles/Toolbar/Mode/Volume_Tools.png rename to src/Resources/Help/HelpFiles/Toolbar/Mode/Volume_Tools.png diff --git a/src/Resources/Help/HelpFiles/Toolbar/MontageToolbar.png b/src/Resources/Help/HelpFiles/Toolbar/MontageToolbar.png new file mode 100644 index 0000000000000000000000000000000000000000..8c3b8afafd2b0e6e7ae1c24e9360e96f7159e32f Binary files /dev/null and b/src/Resources/Help/HelpFiles/Toolbar/MontageToolbar.png differ diff --git a/src/Resources/Help/HelpFiles/Toolbar/SurfaceToolbar.png b/src/Resources/Help/HelpFiles/Toolbar/SurfaceToolbar.png new file mode 100644 index 0000000000000000000000000000000000000000..4bee7593c0c7e7ffd0fa4702a3935357dbce658e Binary files /dev/null and b/src/Resources/Help/HelpFiles/Toolbar/SurfaceToolbar.png differ diff --git a/src/Resources/GuiResources/HelpFiles/Toolbar/Tab_Functions/Tab_Functions.html b/src/Resources/Help/HelpFiles/Toolbar/Tab_Functions/Tab_Functions.html similarity index 94% rename from src/Resources/GuiResources/HelpFiles/Toolbar/Tab_Functions/Tab_Functions.html rename to src/Resources/Help/HelpFiles/Toolbar/Tab_Functions/Tab_Functions.html index 5a5fff7903e8848ca391f455376798179e7a8dc8..61b9e84ada99cea4848802061cc83bbeae89b868 100644 --- a/src/Resources/GuiResources/HelpFiles/Toolbar/Tab_Functions/Tab_Functions.html +++ b/src/Resources/Help/HelpFiles/Toolbar/Tab_Functions/Tab_Functions.html @@ -1,55 +1,55 @@ - - - - - Tab Functions - - - Tab Functions
      - The Tab section of the Toolbar contains controls - for functions that are active across Viewing - Tabs. Tab Functions are not active - in Chart View. -
      - Yoking - allows one to define groups of tabs or windows that will have - linked rotation, panning, and zooming. -
        -
      -
      -
        -
      -
      -
        -
      • Yoking Groups are defined between Surface, - Montage, - and All - Views OR between Volume - and All views (yoking is not applied between surfaces and - volumes).
      • -
      -
      -
        -
      -
      -
      -
        -
      -
      -
        -
      • Surfaces are yoked so that the - corresponding lateral or medial contralateral hemispheres can - be viewed at the same time (e.g. superior temporal gyrus on - the lateral surface can be viewed on the right and left - hemisphere simultaneously). 
      • -
      -
      -
      - -
        -
      - - + + + + + Tab Functions + + + Tab Functions
      + The Tab section of the Toolbar contains controls + for functions that are active across Viewing + Tabs. Tab Functions are not active + in Chart View. +
      + Yoking + allows one to define groups of tabs or windows that will have + linked rotation, panning, and zooming. +
        +
      +
      +
        +
      +
      +
        +
      • Yoking Groups are defined between Surface, + Montage, + and All + Views OR between Volume + and All views (yoking is not applied between surfaces and + volumes).
      • +
      +
      +
        +
      +
      +
      +
        +
      +
      +
        +
      • Surfaces are yoked so that the + corresponding lateral or medial contralateral hemispheres can + be viewed at the same time (e.g. superior temporal gyrus on + the lateral surface can be viewed on the right and left + hemisphere simultaneously). 
      • +
      +
      +
      + +
        +
      + + diff --git a/src/Resources/GuiResources/HelpFiles/Toolbar/Tab_Functions/Tab_Functions.png b/src/Resources/Help/HelpFiles/Toolbar/Tab_Functions/Tab_Functions.png similarity index 100% rename from src/Resources/GuiResources/HelpFiles/Toolbar/Tab_Functions/Tab_Functions.png rename to src/Resources/Help/HelpFiles/Toolbar/Tab_Functions/Tab_Functions.png diff --git a/src/Resources/GuiResources/HelpFiles/Toolbar/Thumbs.db b/src/Resources/Help/HelpFiles/Toolbar/Thumbs.db similarity index 100% rename from src/Resources/GuiResources/HelpFiles/Toolbar/Thumbs.db rename to src/Resources/Help/HelpFiles/Toolbar/Thumbs.db diff --git a/src/Resources/Help/HelpFiles/Toolbar/Toolbar.html b/src/Resources/Help/HelpFiles/Toolbar/Toolbar.html new file mode 100644 index 0000000000000000000000000000000000000000..7ffb9ae0fab9ab39384a0d8146bad86ac21f20ca --- /dev/null +++ b/src/Resources/Help/HelpFiles/Toolbar/Toolbar.html @@ -0,0 +1,867 @@ + + + + + Toolbar + + + Toolbar
      + Toolbar is the section at the top of the Workbench + Window that contains viewing settings for the + structure you are displaying in the Active + Tab and access to + Workbench functional modes (border drawing, region of interest + definition, etc.).
      +
      +

      + + + +
        + +
          +
        • The Toolbar can be hidden or shown + using the button at the + top right of the Workbench Window or by clicking on + Toolbar in the View Menu.
          +
        • +
        • Tooltips + (available by hovering mouse over buttons/elements) are + an easy way to see information on the buttons and + functions on the Toolbar (or throughout wb_view).
          +
        • +
        +
        +
      +
      +

      Montage Toolbar

      +
        +
      • Orientation contains buttons to + set orthogonal and User-defined views of the brain surface + displayed.
      • +
      +
        +
          +
        • Orthogonal view buttons are labeled: LM + (lateral-medial), DV (dorsal-ventral), AP + (anterior-posterior). 
          +
        • +
        • Using the Mouse + Controls, in Montage view, rotation is + mirrored between hemisphere lateral/medial sides and panning + moves hemisphere sides together or away from each other.
          +
        • +
        • The Reset button resets the + orientation/zoom to the default.
          +
        • +
        • Custom + + + + + + + + + + Orientation allows one to set and save (or + not) a specific transform (pan, rotate, oblique rotate, + zoom) for a surface or volume.
        • +
        +
      +
        +
      • Montage Selection contains + pull-downs and checkboxes to set the surface montage to view.
      • +
      +
        +
          +
        • Top Left pull-down sets the montage + structure: Cerebral Cortex, Cerebellar Cortex, or Flat. A + structure is only viewable if the appropriate surface is + loaded.
          +
        • +
        • Top Right pull-down sets the page + orientation of the surfaces displayed. Pull-downs in the + middle set the surface file for each hemisphere from the + list of loaded files. Up to 2 left and 2 right surfaces can + be displayed at once. The checkboxes indicate if the set + surfaces are being displayed.
          +
        • +
        • Left, Lateral, Medial, + and Right checkboxes indicate if these structures + are being displayed.
        • +
        +
      + +
        +
      • Clipping + contains settings for cutting down Surface, Volume, or + Features data to be viewed.
      • +
      + Tab + contains cross-tab functions for yoking the display of two or more + Viewing Tabs and for locking aspect ratio (used with + annotations).  Shading slightly alters the coloring assigned + to brainordinates to provide a better quality image.  Shading + only affects surfaces.
      +
      +
      +

      +
      +
      +

      Volume Toolbar

      +
        +
      • Slice + + + + + + + + + + + + Plane contains buttons to set which volume slice planes + are displayed: P (parasagittal), C (coronal), + A (axial), All (all 3 planes). 
      • +
      +
      +
      +
      +
        +
      • The Reset button resets the + orientation/zoom to the default.
      • +
      +
      +
      +
        +
      • Custom + + + + + + + + + + + Orientation allows one to set and save (or + not) a specific transform (pan, rotate, oblique rotate, + zoom) for a surface or volume.
      • +
      • The crosshair button (large '+' symbol) + enables the drawing of crosshairs, representing the selected + slices.
      • +
      • The crosshair labels button enables + drawing labels indicating the orientation of the viewed + slice.
        +
      • +
      +
      +
        +
      • Montage contains settings for + viewing multiple volume slices from a single slice plane at + once. Rows/Cols set the number of Rows or + Columns of slices to display. Step sets the index + spacing between slices in the volume slice montage. 
      • +
      +

      +
        + +
      • Slice Indices/Coords + contains + settings for the slice index and stereotaxic (Talairach) + coordinate to be viewed for each slice plane.  +
      • +
        +
      + +
      +
        +
      • The vertical Origin button + rests the slice indices to the default (centered at the + AC).
      • +
      +
      +
      +
      +
        + +
      • The  button (default on) + activates movement of the crosshairs/volume slice to the same + plane as the brainordinate selected in any of the Viewing + Tabs in the same yoking group as the Volume tab.
      • +
      +
      +
      +
        +
      • The pull-down at the bottom toggles + between Orthogonal and Oblique volume + viewing.
      • +
      • The button at the left enables the + slices to move to the brainordinate of an identification + operation (mouse click on a volume slice or a surface.
      • +
      • The "M" button at the right selects + masking during oblique viewing.  In oblique viewing, + cubic interpolation is used which may cause edge effects and + masking will remove these edge effects.
        +
      • +
      +
      +
      + +
        +
      • Clipping + contains settings for cutting down Surface, Volume, or + Features data to be viewed.
      • +
      +
        +
      • Tab + contains cross-tab functions for yoking the display of two or + more Viewing Tabs and for locking aspect ratio (used with + annotations). Shading + slightly alters the coloring assigned to brainordinates to + provide a better quality image.  Shading only affects + surfaces.
      • +
      +
      +
      +
      +
      +

      All Toolbar

      +
      +

      +
        +
      • Orientation contains buttons to + set orthogonal and User-defined views of the brain + surfaces/volume displayed.
      • +
      +
      +
        +
      • Orthogonal view buttons are labeled: L + (left), R (right), D (dorsal), V + (ventral), A (anterior), and P + (posterior). 
      • +
      +
      +
      +
      +
        +
      • The Reset button resets the + orientation/zoom to the default.
      • +
      +
      +
      + +
      +
        +
      • Surface Viewing contains + pull-downs and check-boxes to set the surfaces to view: L + (left hemisphere), R (right hemisphere), C + (cerebellum).
      • +
      +
      +
        +
      • The top pull-down menu changes the type + of surface: Anatomical, Inflated or Very Inflated.
      • +
      +
      +
      +
        +
      • The check-boxes to the left of the L, R + and C turn on/off the display of that structure.
      • +
      +
      +
      +
        +
      • To change the structure's surface file, + click on the L, R or C button and a list of loaded files + will appear for selection.
      • +
      +
      +
      +
        +
      • The scroll boxes + to the right, control the spacing between left and right + hemispheres (relative to anatomical spacing = 0) and the + spacing of the cerebellum away from the cortex.
      • + +
      +
      + +
        +
      • Slice Indices/Coords + contains toggles to turn off/on volume planes (P = + parasagittal, C = coronal, A = axial) and + settings for the slice index and stereotaxic (Talairach) + coordinate to be viewed. 
      • +
      +
      +
        +
      • The vertical Origin button + rests the slice indices to the default (centered at the + AC).
      • +
      +
      +
      +
      +
        + +
      • The  button (default on) + activates movement of the crosshairs/volume slice to the same + plane as the brainordinate selected in any of the Viewing + Tabs in the same yoking group as the All tab.
      • +
      +
      +
      +
        +
      • The pull-down at the bottom toggles + between Orthogonal and Oblique volume + viewing.
      • +
      +
      + +
        +
      • Clipping + contains settings for cutting down Surface, Volume, or + Features data to be viewed.
      • +
      +
        +
      • Tab contains + cross-tab functions for yoking the display of two or more + Viewing Tabs + and for locking aspect ratio (used with annotations).  Shading slightly alters the coloring + assigned to brainordinates to provide a better quality + image.  Shading only affects surfaces.
      • +
      +
      +
      +
      +
      +

      Histogram and Line Series Chart Toolbar

      + Chart Type selects the type of chart + displayed in the tab.  Both Histogram and Line-Series charts + use the same toolbar.
      +
      + Chart Axes contains controls for setting axes' attributes.
      +
      +
      +
        +
      • Edit Axis Selection - Selects the + axis (Left, Bottom, Right) for editing
        +
      • +
      • Label from File In selection - A + label is displayed in the axis (eg: counts, data, etc.) and + this label is a property of a file.  Since there may be + more than one layer, use this control to choose the layer and + the label displayed is from the file in the selected layer.
        +
      • +
      • Edit Label... Pops up a dialog for + editing the text in the axis' label.
        +
      • +
      • Axis Checkbox - Turns display of + the entire axis on of off.
        +
      • +
      • Label Checkbox - Turns display of + the label on or off.
        +
      • +
      • Nums Checkbox - Turns display of + the numeric values in the axis on or off.
        +
      • +
      • Rotate Checkbox - When checked, + the label is rotated so that the text is oriented bottom to + top (rotated 90 degrees).
        +
      • +
      • Ticks Checkbox - Turns display of + the axis tick marks on or off.
        +
      • +
      • Label Size - Controls the height + of the label's text characters as a percentage of the tab's + graphics region height.
        +
      • +
      • Scale Size - Controls the width (left + or right axis) or height (bottom axis) axis as a percentage + of the tab's graphics region height.
      • +
      • Pad Size - Controls the height of + the label's text characters as a percentage of the tab's + graphics region height.
      • +
      • Lines Size- Controls the height of the label's text + characters as a percentage of the tab's graphics region + height.
        +
      • +
      • Range Mode - Chooses the range of + the axis.  Auto extends the range of the axis to beyond + the data's minimum and maximum values so that, in most cases, + whole numbers are displayed.  Data sets the axis range to + the exact range of the data.  User allows the user to set + the minimum and maximum of the data displayed.
        +
      • +
      • Max (Range) Value - The minimum + value in the axis.
        +
      • +
      • Min (Range) Value - The maximum + value in the axis.
        +
      • +
      • Format - Controls format of the + numerical values in the axis.  Decimal displays the + numerical values using a decimal format (eg: -1.8, 2, + etc).  Scientific displays the numerical values using + scientific notation (eg: 1.32e-2; -2.5e+3; etc.)   Auto + chooses a format to best display the values from either + Decimal or Scientific.
        +
      • +
      • Decimals - When the Format is + Decimal or Scientific, this controls the number of digits + right of the decimal point.
        +
      • +
      • Subdivisions - Subdivisions is the + number of numeric values between the minimum and maximum + values.  Auto sets the number number of subdivisions + appropriate for the data.  User allows the user to set + the number of subdivisions using the numerical value below + Auto/User.
        +
      • +
      + Chart Title contains controls for the + display of a title above the chart.
      +
      +
        +
      • Show Title Checkbox - Turns + display of the title on or off.
        +
      • +
      • Size Value - Sets the size of the + label's text as a percentage of the tab's graphics region + height.
        +
      • +
      • Pad Value - Sets the size of the + space between the edge of the tab's graphics region and the + top of the text.
        +
      • +
      • Edit Title button - Displays a + dialog for editing the title's text.
        +
      • +
      +
      +
      Mode + contains buttons for switching between Viewing + + + + + + + + + + + + + + + + Area mouse controls for performing actions + including Border + drawing and Foci + creation or typical View mode.
      +
      + Tab
      contains + cross-tab functions for yoking the display of two or more Viewing + Tabs
      and for + locking aspect ratio (used with annotation
      +
      +
      +
      +
      +

      Matrix Chart Toolbar

      +
      + Chart Type selects + the type of chart displayed in the tab.
      +
      + Chart Orientation
      contains buttons to set + orthogonal and User-defined views of the brain surfaces/volume + displayed.
      +
      +
        +
      • Reset button resets the view of + the matrix to remove any panning and zooming (shift-drag mouse + to pan matrix, command-drag mouse to zoom).
      • +
      • Custom button allows one to create + or use an existing custom orientation.
      • +
      + Chart Attributes
      +
      +
        +
      • Cell Width - Scales the width of + the cells to be larger or smaller.
      • +
      • Cell Height - Scales the height of + the cells to be larger or smaller.
      • +
      • Highlight Selection - If a + row is selected in the matrix, row is highlighted with an + outline.  The color of the outline is set on the + Preferences' Colors tab.
      • +
      • Show Grid Outline - + Displays a grid with lines at the horizontal and vertical cell + boundaries.  The color of the grid lines is set on the + Preferences' Colors tab.
      • +
      + Chart Title + contains controls for the display of a title above the chart.
      +
      +     Show Title Checkbox - Turns display + of the title on or off.
      +     Size Value - Sets the size of the + label's text as a percentage of the tab's graphics region + height.
      +     Pad Value - Sets the size of the + space between the edge of the tab's graphics region and the top + of the text.
      +     Edit Title button - Displays a dialog + for editing the title's text.
      +
      +
      +

      +
      +
      +
      +

      Surface Toolbar

      +
      +

      +
        +
      • Orientation contains buttons to + set orthogonal and User-defined views of the brain surface + displayed: L (lateral), M (medial), D (dorsal), + V (ventral), A + (anterior), and P (posterior). 
      • +
      +
        +
          +
        • The Reset button resets the + orientation/zoom to the default.
          +
        • +
        • Custom + + + + + + + + Orientation allows one to set and save (or + not) a specific transform (pan, rotate, oblique rotate, + zoom) for a surface or volume.
        • +
        +
      +
        +
      • Selection contains pull-downs to + set the brain structure (top) and surface (bottom) to view.
      • +
      + +
        +
      • Clipping + contains settings for cutting down Surface, Volume, or + Features data to be viewed.
      • +
      +           Tab + contains cross-tab functions for yoking the display of two or more + Viewing Tabs and + for locking aspect ratio (used with annotations). 
      + Shading slightly alters + the coloring assigned to brainordinates to provide a better + quality image.  Shading only affects surfaces.
      +
      +
      +

      Line Series Old Chart Toolbar

      +
      +

      + Chart Type (Old), + determines which kind of chart is viewed. If one or more + Workbench-chartable files are loaded, one or more Chart Types + will be available for selection. Chartable files for the + selected Chart Type are listed and can be selected for display + in the Overlay Toolbox Charting + tab.
      +  
      + Chart Types currently available:
      + Data/Time + + + + + + + + Series (top image) chartable files are graphed + for a brainordinate identified in a non-Chart view Viewing Tab, + showing a data value (y-axis) for each Map Index/unit of time + (x-axis). The following file types are available for Data/Time + Series charting, as long as they contain more than one + map: +
      +
      + * + CIFTI + Data Series (.dtseries.nii)
      + * + CIFTI Parcel Scalar (.pscalar.nii)
      + * + CIFTI Parcel Series (.ptseries.nii)
      + * + CIFTI Scalar (.dscalar.nii)
      + * + Metric (.func.gii, .shape.gii)
      + * + Volume (.nii, .nii.gz)
      +
      + Matrix + (bottom image) chartable files:
      +
      + * + CIFTI Parcel to + + + + + + + parcel connectivity (.pconn.nii) -- matrix + + + + + + + + + + + with parcels as the y-axis rows, parcels as the x-axis + columns, and the colored cells indicate the correlation + (connectivity) values between the parcel pair.
      + * + CIFTI Parcel Scalar (.pscalar.nii) -- matrix + + + + + + + + + + with parcels as the y-axis rows, map indices as the x-axis + columns, and the colored cells indicate the scalar data value + for a parcel/map pair in a particular parcel + (identified in a non-Chart view Viewing Tab). 
      +
      +
      +
      +
      +

      Matrix Old Chart Toolbar

      +
      +
      +

      +

      +
      + + diff --git a/src/Resources/GuiResources/HelpFiles/Toolbar/Toolbar_icon.png b/src/Resources/Help/HelpFiles/Toolbar/Toolbar_icon.png similarity index 100% rename from src/Resources/GuiResources/HelpFiles/Toolbar/Toolbar_icon.png rename to src/Resources/Help/HelpFiles/Toolbar/Toolbar_icon.png diff --git a/src/Resources/Help/HelpFiles/Toolbar/VolumeToolbar.png b/src/Resources/Help/HelpFiles/Toolbar/VolumeToolbar.png new file mode 100644 index 0000000000000000000000000000000000000000..6b72701dee8fc8ee0b925b3106536604df70ba24 Binary files /dev/null and b/src/Resources/Help/HelpFiles/Toolbar/VolumeToolbar.png differ diff --git a/src/Resources/GuiResources/HelpFiles/Toolbar/VolumeView.png b/src/Resources/Help/HelpFiles/Toolbar/VolumeView.png similarity index 100% rename from src/Resources/GuiResources/HelpFiles/Toolbar/VolumeView.png rename to src/Resources/Help/HelpFiles/Toolbar/VolumeView.png diff --git a/src/Resources/GuiResources/HelpFiles/Toolbar/All_View/Volume_ID_button.png b/src/Resources/Help/HelpFiles/Toolbar/Volume_ID_button.png similarity index 100% rename from src/Resources/GuiResources/HelpFiles/Toolbar/All_View/Volume_ID_button.png rename to src/Resources/Help/HelpFiles/Toolbar/Volume_ID_button.png diff --git a/src/Resources/Help/HelpFiles/WBwindow_Horiz_Labels.png b/src/Resources/Help/HelpFiles/WBwindow_Horiz_Labels.png new file mode 100644 index 0000000000000000000000000000000000000000..59429475310104141103397a8c92518d2ef2808e Binary files /dev/null and b/src/Resources/Help/HelpFiles/WBwindow_Horiz_Labels.png differ diff --git a/src/Resources/Help/HelpFiles/WindowForScene.png b/src/Resources/Help/HelpFiles/WindowForScene.png new file mode 100644 index 0000000000000000000000000000000000000000..423319ab3d169870b425b513ddcf2c045fe0feec Binary files /dev/null and b/src/Resources/Help/HelpFiles/WindowForScene.png differ diff --git a/src/Resources/GuiResources/HelpFiles/Workbench_Window/Active_Tab/Active_Tab.html b/src/Resources/Help/HelpFiles/Workbench_Window/Active_Tab/Active_Tab.html similarity index 100% rename from src/Resources/GuiResources/HelpFiles/Workbench_Window/Active_Tab/Active_Tab.html rename to src/Resources/Help/HelpFiles/Workbench_Window/Active_Tab/Active_Tab.html diff --git a/src/Resources/GuiResources/HelpFiles/Workbench_Window/Active_Tab/Thumbs.db b/src/Resources/Help/HelpFiles/Workbench_Window/Active_Tab/Thumbs.db similarity index 100% rename from src/Resources/GuiResources/HelpFiles/Workbench_Window/Active_Tab/Thumbs.db rename to src/Resources/Help/HelpFiles/Workbench_Window/Active_Tab/Thumbs.db diff --git a/src/Resources/GuiResources/HelpFiles/Workbench_Window/Active_Tab/Viewing_Tabs.png b/src/Resources/Help/HelpFiles/Workbench_Window/Active_Tab/Viewing_Tabs.png similarity index 100% rename from src/Resources/GuiResources/HelpFiles/Workbench_Window/Active_Tab/Viewing_Tabs.png rename to src/Resources/Help/HelpFiles/Workbench_Window/Active_Tab/Viewing_Tabs.png diff --git a/src/Resources/GuiResources/HelpFiles/Workbench_Window/Thumbs.db b/src/Resources/Help/HelpFiles/Workbench_Window/Thumbs.db similarity index 100% rename from src/Resources/GuiResources/HelpFiles/Workbench_Window/Thumbs.db rename to src/Resources/Help/HelpFiles/Workbench_Window/Thumbs.db diff --git a/src/Resources/GuiResources/HelpFiles/Workbench_Window/Tooltip/Thumbs.db b/src/Resources/Help/HelpFiles/Workbench_Window/Tooltip/Thumbs.db similarity index 100% rename from src/Resources/GuiResources/HelpFiles/Workbench_Window/Tooltip/Thumbs.db rename to src/Resources/Help/HelpFiles/Workbench_Window/Tooltip/Thumbs.db diff --git a/src/Resources/GuiResources/HelpFiles/Workbench_Window/Tooltip/Tooltip.html b/src/Resources/Help/HelpFiles/Workbench_Window/Tooltip/Tooltip.html similarity index 100% rename from src/Resources/GuiResources/HelpFiles/Workbench_Window/Tooltip/Tooltip.html rename to src/Resources/Help/HelpFiles/Workbench_Window/Tooltip/Tooltip.html diff --git a/src/Resources/GuiResources/HelpFiles/Workbench_Window/Tooltip/Tooltip.png b/src/Resources/Help/HelpFiles/Workbench_Window/Tooltip/Tooltip.png similarity index 100% rename from src/Resources/GuiResources/HelpFiles/Workbench_Window/Tooltip/Tooltip.png rename to src/Resources/Help/HelpFiles/Workbench_Window/Tooltip/Tooltip.png diff --git a/src/Resources/GuiResources/HelpFiles/Workbench_Window/Viewing_Area/Thumbs.db b/src/Resources/Help/HelpFiles/Workbench_Window/Viewing_Area/Thumbs.db similarity index 100% rename from src/Resources/GuiResources/HelpFiles/Workbench_Window/Viewing_Area/Thumbs.db rename to src/Resources/Help/HelpFiles/Workbench_Window/Viewing_Area/Thumbs.db diff --git a/src/Resources/GuiResources/HelpFiles/Workbench_Window/Viewing_Area/Viewing_Area.html b/src/Resources/Help/HelpFiles/Workbench_Window/Viewing_Area/Viewing_Area.html similarity index 100% rename from src/Resources/GuiResources/HelpFiles/Workbench_Window/Viewing_Area/Viewing_Area.html rename to src/Resources/Help/HelpFiles/Workbench_Window/Viewing_Area/Viewing_Area.html diff --git a/src/Resources/Help/HelpFiles/Workbench_Window/Viewing_Area/WBwindow_Horiz_Labels.png b/src/Resources/Help/HelpFiles/Workbench_Window/Viewing_Area/WBwindow_Horiz_Labels.png new file mode 100644 index 0000000000000000000000000000000000000000..59429475310104141103397a8c92518d2ef2808e Binary files /dev/null and b/src/Resources/Help/HelpFiles/Workbench_Window/Viewing_Area/WBwindow_Horiz_Labels.png differ diff --git a/src/Resources/GuiResources/HelpFiles/Workbench_Window/Viewing_Tabs/Thumbs.db b/src/Resources/Help/HelpFiles/Workbench_Window/Viewing_Tabs/Thumbs.db similarity index 100% rename from src/Resources/GuiResources/HelpFiles/Workbench_Window/Viewing_Tabs/Thumbs.db rename to src/Resources/Help/HelpFiles/Workbench_Window/Viewing_Tabs/Thumbs.db diff --git a/src/Resources/GuiResources/HelpFiles/Workbench_Window/Viewing_Tabs/Viewing_Tabs.html b/src/Resources/Help/HelpFiles/Workbench_Window/Viewing_Tabs/Viewing_Tabs.html similarity index 100% rename from src/Resources/GuiResources/HelpFiles/Workbench_Window/Viewing_Tabs/Viewing_Tabs.html rename to src/Resources/Help/HelpFiles/Workbench_Window/Viewing_Tabs/Viewing_Tabs.html diff --git a/src/Resources/GuiResources/HelpFiles/Workbench_Window/Viewing_Tabs/Viewing_Tabs.png b/src/Resources/Help/HelpFiles/Workbench_Window/Viewing_Tabs/Viewing_Tabs.png similarity index 100% rename from src/Resources/GuiResources/HelpFiles/Workbench_Window/Viewing_Tabs/Viewing_Tabs.png rename to src/Resources/Help/HelpFiles/Workbench_Window/Viewing_Tabs/Viewing_Tabs.png diff --git a/src/Resources/GuiResources/HelpFiles/Workbench_Window/Viewing_Area/WBwindow_Horiz_Labels.png b/src/Resources/Help/HelpFiles/Workbench_Window/WBwindow_Horiz_Labels.png similarity index 100% rename from src/Resources/GuiResources/HelpFiles/Workbench_Window/Viewing_Area/WBwindow_Horiz_Labels.png rename to src/Resources/Help/HelpFiles/Workbench_Window/WBwindow_Horiz_Labels.png diff --git a/src/Resources/GuiResources/HelpFiles/Workbench_Window/Window_Elements_Hide-Show_Buttons/Features_TB_icon.png b/src/Resources/Help/HelpFiles/Workbench_Window/Window_Elements_Hide-Show_Buttons/Features_TB_icon.png similarity index 100% rename from src/Resources/GuiResources/HelpFiles/Workbench_Window/Window_Elements_Hide-Show_Buttons/Features_TB_icon.png rename to src/Resources/Help/HelpFiles/Workbench_Window/Window_Elements_Hide-Show_Buttons/Features_TB_icon.png diff --git a/src/Resources/GuiResources/HelpFiles/Workbench_Window/Window_Elements_Hide-Show_Buttons/Help_button.png b/src/Resources/Help/HelpFiles/Workbench_Window/Window_Elements_Hide-Show_Buttons/Help_button.png similarity index 100% rename from src/Resources/GuiResources/HelpFiles/Workbench_Window/Window_Elements_Hide-Show_Buttons/Help_button.png rename to src/Resources/Help/HelpFiles/Workbench_Window/Window_Elements_Hide-Show_Buttons/Help_button.png diff --git a/src/Resources/GuiResources/HelpFiles/Workbench_Window/Window_Elements_Hide-Show_Buttons/Identify_Brainordinate_Icon.png b/src/Resources/Help/HelpFiles/Workbench_Window/Window_Elements_Hide-Show_Buttons/Identify_Brainordinate_Icon.png similarity index 100% rename from src/Resources/GuiResources/HelpFiles/Workbench_Window/Window_Elements_Hide-Show_Buttons/Identify_Brainordinate_Icon.png rename to src/Resources/Help/HelpFiles/Workbench_Window/Window_Elements_Hide-Show_Buttons/Identify_Brainordinate_Icon.png diff --git a/src/Resources/GuiResources/HelpFiles/Workbench_Window/Window_Elements_Hide-Show_Buttons/Identify_icon.png b/src/Resources/Help/HelpFiles/Workbench_Window/Window_Elements_Hide-Show_Buttons/Identify_icon.png similarity index 100% rename from src/Resources/GuiResources/HelpFiles/Workbench_Window/Window_Elements_Hide-Show_Buttons/Identify_icon.png rename to src/Resources/Help/HelpFiles/Workbench_Window/Window_Elements_Hide-Show_Buttons/Identify_icon.png diff --git a/src/Resources/GuiResources/HelpFiles/Workbench_Window/Window_Elements_Hide-Show_Buttons/Information_icon.png b/src/Resources/Help/HelpFiles/Workbench_Window/Window_Elements_Hide-Show_Buttons/Information_icon.png similarity index 100% rename from src/Resources/GuiResources/HelpFiles/Workbench_Window/Window_Elements_Hide-Show_Buttons/Information_icon.png rename to src/Resources/Help/HelpFiles/Workbench_Window/Window_Elements_Hide-Show_Buttons/Information_icon.png diff --git a/src/Resources/GuiResources/HelpFiles/Workbench_Window/Window_Elements_Hide-Show_Buttons/Overlay_TB_icon.png b/src/Resources/Help/HelpFiles/Workbench_Window/Window_Elements_Hide-Show_Buttons/Overlay_TB_icon.png similarity index 100% rename from src/Resources/GuiResources/HelpFiles/Workbench_Window/Window_Elements_Hide-Show_Buttons/Overlay_TB_icon.png rename to src/Resources/Help/HelpFiles/Workbench_Window/Window_Elements_Hide-Show_Buttons/Overlay_TB_icon.png diff --git a/src/Resources/GuiResources/HelpFiles/Workbench_Window/Window_Elements_Hide-Show_Buttons/Scenes_icon.png b/src/Resources/Help/HelpFiles/Workbench_Window/Window_Elements_Hide-Show_Buttons/Scenes_icon.png similarity index 100% rename from src/Resources/GuiResources/HelpFiles/Workbench_Window/Window_Elements_Hide-Show_Buttons/Scenes_icon.png rename to src/Resources/Help/HelpFiles/Workbench_Window/Window_Elements_Hide-Show_Buttons/Scenes_icon.png diff --git a/src/Resources/GuiResources/HelpFiles/Workbench_Window/Window_Elements_Hide-Show_Buttons/Thumbs.db b/src/Resources/Help/HelpFiles/Workbench_Window/Window_Elements_Hide-Show_Buttons/Thumbs.db similarity index 100% rename from src/Resources/GuiResources/HelpFiles/Workbench_Window/Window_Elements_Hide-Show_Buttons/Thumbs.db rename to src/Resources/Help/HelpFiles/Workbench_Window/Window_Elements_Hide-Show_Buttons/Thumbs.db diff --git a/src/Resources/GuiResources/HelpFiles/Workbench_Window/Window_Elements_Hide-Show_Buttons/Toolbar_icon.png b/src/Resources/Help/HelpFiles/Workbench_Window/Window_Elements_Hide-Show_Buttons/Toolbar_icon.png similarity index 100% rename from src/Resources/GuiResources/HelpFiles/Workbench_Window/Window_Elements_Hide-Show_Buttons/Toolbar_icon.png rename to src/Resources/Help/HelpFiles/Workbench_Window/Window_Elements_Hide-Show_Buttons/Toolbar_icon.png diff --git a/src/Resources/Help/HelpFiles/Workbench_Window/Window_Elements_Hide-Show_Buttons/WindowForScene.tiff b/src/Resources/Help/HelpFiles/Workbench_Window/Window_Elements_Hide-Show_Buttons/WindowForScene.tiff new file mode 100644 index 0000000000000000000000000000000000000000..b37243e5d59562f9e45536fb00fa47c1303ca92a Binary files /dev/null and b/src/Resources/Help/HelpFiles/Workbench_Window/Window_Elements_Hide-Show_Buttons/WindowForScene.tiff differ diff --git a/src/Resources/GuiResources/HelpFiles/Workbench_Window/Window_Elements_Hide-Show_Buttons/Window_Elements_Hide-Show_Buttons.html b/src/Resources/Help/HelpFiles/Workbench_Window/Window_Elements_Hide-Show_Buttons/Window_Elements_Hide-Show_Buttons.html similarity index 100% rename from src/Resources/GuiResources/HelpFiles/Workbench_Window/Window_Elements_Hide-Show_Buttons/Window_Elements_Hide-Show_Buttons.html rename to src/Resources/Help/HelpFiles/Workbench_Window/Window_Elements_Hide-Show_Buttons/Window_Elements_Hide-Show_Buttons.html diff --git a/src/Resources/GuiResources/HelpFiles/Workbench_Window/Workbench_Window.html b/src/Resources/Help/HelpFiles/Workbench_Window/Workbench_Window.html similarity index 100% rename from src/Resources/GuiResources/HelpFiles/Workbench_Window/Workbench_Window.html rename to src/Resources/Help/HelpFiles/Workbench_Window/Workbench_Window.html diff --git a/src/Resources/GuiResources/gui_resources.qrc b/src/Resources/Help/help_resources.qrc similarity index 70% rename from src/Resources/GuiResources/gui_resources.qrc rename to src/Resources/Help/help_resources.qrc index ca40feb955698871b60a38029427768c95b8db01..d825c826840f44d4588d0e5c76b30852fd214706 100644 --- a/src/Resources/GuiResources/gui_resources.qrc +++ b/src/Resources/Help/help_resources.qrc @@ -1,29 +1,59 @@ -./gui_resources.qrc -./HelpFiles/Annotations/Annotations.fld/image001.png -./HelpFiles/Annotations/Annotations.fld/image002.png -./HelpFiles/Annotations/Annotations.fld/image003.png -./HelpFiles/Annotations/Annotations.fld/image004.png -./HelpFiles/Annotations/Annotations.fld/image005.png -./HelpFiles/Annotations/Annotations.fld/image006.png -./HelpFiles/Annotations/Annotations.fld/image007.png -./HelpFiles/Annotations/Annotations.fld/image008.png -./HelpFiles/Annotations/Annotations.fld/image009.png -./HelpFiles/Annotations/Annotations.fld/image010.png -./HelpFiles/Annotations/Annotations.fld/image011.png -./HelpFiles/Annotations/Annotations.fld/image012.png -./HelpFiles/Annotations/Annotations.fld/image013.png -./HelpFiles/Annotations/Annotations.fld/image014.png -./HelpFiles/Annotations/Annotations.fld/image015.png -./HelpFiles/Annotations/Annotations.fld/image016.png -./HelpFiles/Annotations/Annotations.fld/image017.png -./HelpFiles/Annotations/Annotations.fld/image018.png -./HelpFiles/Annotations/Annotations.fld/image019.png -./HelpFiles/Annotations/Annotations.fld/image020.png -./HelpFiles/Annotations/Annotations.fld/image021.png -./HelpFiles/Annotations/Annotations.fld/image022.png -./HelpFiles/Annotations/Annotations.html +./help_resources.qrc +./HelpFiles/Annotations/Annotations_1.3.fld/filelist.xml +./HelpFiles/Annotations/Annotations_1.3.fld/header.html +./HelpFiles/Annotations/Annotations_1.3.fld/image001.png +./HelpFiles/Annotations/Annotations_1.3.fld/image002.png +./HelpFiles/Annotations/Annotations_1.3.fld/image003.png +./HelpFiles/Annotations/Annotations_1.3.fld/image004.png +./HelpFiles/Annotations/Annotations_1.3.fld/image005.png +./HelpFiles/Annotations/Annotations_1.3.fld/image006.png +./HelpFiles/Annotations/Annotations_1.3.fld/image007.png +./HelpFiles/Annotations/Annotations_1.3.fld/image008.png +./HelpFiles/Annotations/Annotations_1.3.fld/image009.png +./HelpFiles/Annotations/Annotations_1.3.fld/image010.png +./HelpFiles/Annotations/Annotations_1.3.fld/image011.png +./HelpFiles/Annotations/Annotations_1.3.fld/image012.png +./HelpFiles/Annotations/Annotations_1.3.fld/image013.png +./HelpFiles/Annotations/Annotations_1.3.fld/image014.png +./HelpFiles/Annotations/Annotations_1.3.fld/image015.png +./HelpFiles/Annotations/Annotations_1.3.fld/image016.png +./HelpFiles/Annotations/Annotations_1.3.fld/image017.png +./HelpFiles/Annotations/Annotations_1.3.fld/image018.png +./HelpFiles/Annotations/Annotations_1.3.fld/image019.png +./HelpFiles/Annotations/Annotations_1.3.fld/image020.png +./HelpFiles/Annotations/Annotations_1.3.fld/image021.png +./HelpFiles/Annotations/Annotations_1.3.fld/image022.png +./HelpFiles/Annotations/Annotations_1.3.fld/image023.png +./HelpFiles/Annotations/Annotations_1.3.fld/image024.png +./HelpFiles/Annotations/Annotations_1.3.fld/image025.png +./HelpFiles/Annotations/Annotations_1.3.fld/image026.png +./HelpFiles/Annotations/Annotations_1.3.fld/image027.png +./HelpFiles/Annotations/Annotations_1.3.fld/image028.png +./HelpFiles/Annotations/Annotations_1.3.fld/image029.png +./HelpFiles/Annotations/Annotations_1.3.fld/image030.png +./HelpFiles/Annotations/Annotations_1.3.fld/image031.png +./HelpFiles/Annotations/Annotations_1.3.fld/image032.png +./HelpFiles/Annotations/Annotations_1.3.fld/image033.png +./HelpFiles/Annotations/Annotations_1.3.fld/image034.png +./HelpFiles/Annotations/Annotations_1.3.fld/image035.png +./HelpFiles/Annotations/Annotations_1.3.fld/item0001.xml +./HelpFiles/Annotations/Annotations_1.3.fld/props002.xml +./HelpFiles/Annotations/Annotations_1.3.fld/themedata.thmx +./HelpFiles/Annotations/Annotations_1.3.html +./HelpFiles/Best_Practices_Guides/Annotation_and_Scenes_Best_Practices.html +./HelpFiles/ChartingOverview/ChartingOverview.html +./HelpFiles/ChartingOverview/HistogramChart.png +./HelpFiles/ChartingOverview/HistogramControl.png +./HelpFiles/ChartingOverview/LineSeriesChart.png +./HelpFiles/ChartingOverview/LineSeriesHistory.png +./HelpFiles/ChartingOverview/MatrixChart.png +./HelpFiles/Features_Toolbox/Annotations/Annotations.fld/filelist.xml +./HelpFiles/Features_Toolbox/Annotations/Annotations.fld/image001.png +./HelpFiles/Features_Toolbox/Annotations/Annotations.fld/themedata.thmx +./HelpFiles/Features_Toolbox/Annotations/Annotations.html +./HelpFiles/Features_Toolbox/Annotations/annotations.png ./HelpFiles/Features_Toolbox/Borders/Borders.html ./HelpFiles/Features_Toolbox/Borders/Borders_Attributes.png ./HelpFiles/Features_Toolbox/Borders/Borders_Selection.png @@ -35,6 +65,7 @@ ./HelpFiles/Features_Toolbox/Foci/Foci_Attributes.png ./HelpFiles/Features_Toolbox/Foci/Foci_Selection.png ./HelpFiles/Features_Toolbox/Images/Images.html +./HelpFiles/Features_Toolbox/Images/Images_Attributes.png ./HelpFiles/Features_Toolbox/Images/Images_Selection.png ./HelpFiles/Features_Toolbox/Labels/Labels.html ./HelpFiles/Features_Toolbox/Labels/Labels_Attributes.png @@ -50,7 +81,6 @@ ./HelpFiles/Glossary/Scene/Scene.html ./HelpFiles/Glossary/Specification_File/Spec_File.png ./HelpFiles/Glossary/Specification_File/Specification_File.html -./HelpFiles/Glossary/Specification_File/Thumbs.db ./HelpFiles/Glossary/Whiteordinate/Whiteordinate.html ./HelpFiles/Information_Window/Information_Window.html ./HelpFiles/Information_Window/Information_Window.png @@ -60,24 +90,24 @@ ./HelpFiles/Menus/Data_Menu/Project_Foci_Dialog.png ./HelpFiles/Menus/Data_Menu/Thumbs.db ./HelpFiles/Menus/Develop_Menu/Develop_Menu.html +./HelpFiles/Menus/Edit_Menu/Edit_Menu.fld/filelist.xml +./HelpFiles/Menus/Edit_Menu/Edit_Menu.fld/themedata.thmx ./HelpFiles/Menus/Edit_Menu/Edit_Menu.html ./HelpFiles/Menus/Edit_Menu/Edit_Menu.png ./HelpFiles/Menus/File_Menu/Animation_Control/Animation_Control.html ./HelpFiles/Menus/File_Menu/Animation_Control/Animation_Control_dialog.png -./HelpFiles/Menus/File_Menu/Animation_Control/Thumbs.db ./HelpFiles/Menus/File_Menu/Capture_Image/Capture_Image.html ./HelpFiles/Menus/File_Menu/Capture_Image/Capture_Image_dialog.png ./HelpFiles/Menus/File_Menu/Capture_Image/File_menu.png -./HelpFiles/Menus/File_Menu/Capture_Image/Thumbs.db ./HelpFiles/Menus/File_Menu/File_Menu.html ./HelpFiles/Menus/File_Menu/File_menu.png ./HelpFiles/Menus/File_Menu/Preferences/Preferences.html ./HelpFiles/Menus/File_Menu/Preferences/Preferences_Colors.png ./HelpFiles/Menus/File_Menu/Preferences/Preferences_ID.png ./HelpFiles/Menus/File_Menu/Preferences/Preferences_Misc.png +./HelpFiles/Menus/File_Menu/Preferences/Preferences_NewTabs.png ./HelpFiles/Menus/File_Menu/Preferences/Preferences_OpenGL.png ./HelpFiles/Menus/File_Menu/Preferences/Preferences_Vol.png -./HelpFiles/Menus/File_Menu/Preferences/Thumbs.db ./HelpFiles/Menus/File_Menu/Save-Manage_Files/Gear_symbol.png ./HelpFiles/Menus/File_Menu/Save-Manage_Files/Reload_symbol.png ./HelpFiles/Menus/File_Menu/Save-Manage_Files/Remove_symbol.png @@ -85,7 +115,6 @@ ./HelpFiles/Menus/File_Menu/Save-Manage_Files/Save-Manage_Files_dialog.png ./HelpFiles/Menus/File_Menu/Save-Manage_Files/Thumbs.db ./HelpFiles/Menus/File_Menu/Save-Manage_Files/Upload_symbol.png -./HelpFiles/Menus/File_Menu/Thumbs.db ./HelpFiles/Menus/Help_Menu/Bug_Report_box.png ./HelpFiles/Menus/Help_Menu/Help_box.png ./HelpFiles/Menus/Help_Menu/Help_button.png @@ -93,14 +122,13 @@ ./HelpFiles/Menus/Help_Menu/Help_Menu.png ./HelpFiles/Menus/Help_Menu/Help_Search.png ./HelpFiles/Menus/Help_Menu/Workbench_Help.png +./HelpFiles/Menus/Menus.html ./HelpFiles/Menus/Surface_Menu/Surface_Menu.html ./HelpFiles/Menus/Surface_Menu/Surface_Properties.png ./HelpFiles/Menus/Surface_Menu/Volume_Interaction_Surfaces.png ./HelpFiles/Menus/Thumbs.db ./HelpFiles/Menus/View_Menu/Gaps_And_Margins/Gaps_And_Margins.html ./HelpFiles/Menus/View_Menu/Gaps_And_Margins/Gaps_And_Margins.png -./HelpFiles/Menus/View_Menu/Thumbs.db -./HelpFiles/Menus/View_Menu/Tile_Tabs_Configuration/Thumbs.db ./HelpFiles/Menus/View_Menu/Tile_Tabs_Configuration/Tile_Tabs_Configuration.html ./HelpFiles/Menus/View_Menu/Tile_Tabs_Configuration/Tile_Tabs_Configuration.png ./HelpFiles/Menus/View_Menu/View_Menu.html @@ -110,9 +138,9 @@ ./HelpFiles/Menus/wb_view_Menu/wb_view_Menu.png ./HelpFiles/Menus/Window_Menu/IdentifyBrainordinateDialog.png ./HelpFiles/Menus/Window_Menu/Rename_tab.png -./HelpFiles/Menus/Window_Menu/Thumbs.db ./HelpFiles/Menus/Window_Menu/Window_Menu.html ./HelpFiles/Menus/Window_Menu/Window_Menu.png +./HelpFiles/Menus/Window_Menu.png ./HelpFiles/Mouse_Controls/Mouse_Controls.html ./HelpFiles/Mouse_Controls/RightClick_Surface.png ./HelpFiles/Mouse_Controls/RightClick_Volume.png @@ -121,16 +149,18 @@ ./HelpFiles/Open_Spec_File_Dialog/Open_Spec.png ./HelpFiles/Open_Spec_File_Dialog/Open_Spec_File_Dialog.html ./HelpFiles/Open_Spec_File_Dialog/Thumbs.db +./HelpFiles/Overlay_Toolbox/Chart_Layers/Chart Layers.html +./HelpFiles/Overlay_Toolbox/Chart_Layers/LineChartingLayers.png ./HelpFiles/Overlay_Toolbox/Charting/Charting.html ./HelpFiles/Overlay_Toolbox/Charting/Charting_History.png ./HelpFiles/Overlay_Toolbox/Charting/Charting_Loading.png -./HelpFiles/Overlay_Toolbox/Charting/Thumbs.db ./HelpFiles/Overlay_Toolbox/Connectivity/Connectivity.html ./HelpFiles/Overlay_Toolbox/Connectivity/Connectivity.png ./HelpFiles/Overlay_Toolbox/Layers/Add_Layer.png ./HelpFiles/Overlay_Toolbox/Layers/Colorbar.png ./HelpFiles/Overlay_Toolbox/Layers/Layers.html ./HelpFiles/Overlay_Toolbox/Layers/Overlay_and_Map_Settings/Overlay_and_Map_Settings.html +./HelpFiles/Overlay_Toolbox/Layers/Overlay_and_Map_Settings/Overlay_Toolbox_horiz.png ./HelpFiles/Overlay_Toolbox/Layers/Overlay_and_Map_Settings/Palette_Settings.png ./HelpFiles/Overlay_Toolbox/Layers/Overlay_and_Map_Settings/Settings_icon.png ./HelpFiles/Overlay_Toolbox/Layers/Overlay_Toolbox_horiz.png @@ -138,7 +168,6 @@ ./HelpFiles/Overlay_Toolbox/Overlay_Toolbox.html ./HelpFiles/Overlay_Toolbox/Overlay_Toolbox_horiz.png ./HelpFiles/Overlay_Toolbox/Overlay_Toolbox_vert.png -./HelpFiles/Overlay_Toolbox/Thumbs.db ./HelpFiles/Overlay_Toolbox/Vol-Surf_Outline/Color-Vol-Surf_Outline.png ./HelpFiles/Overlay_Toolbox/Vol-Surf_Outline/Tab-Vol-Surf_Outline.png ./HelpFiles/Overlay_Toolbox/Vol-Surf_Outline/Vol-Surf_Outline.html @@ -149,37 +178,22 @@ ./HelpFiles/Scenes_Window/Create_New_Scene_File/Thumbs.db ./HelpFiles/Scenes_Window/Scenes_Window.html ./HelpFiles/Scenes_Window/Scenes_Window.png -./HelpFiles/Scenes_Window/Thumbs.db ./HelpFiles/Select_Brainordinate_Window/CiftiFileRow.png ./HelpFiles/Select_Brainordinate_Window/Indentify_Brainordinate.png ./HelpFiles/Select_Brainordinate_Window/Select_Brainordinate_Window.html ./HelpFiles/Splash_Screen/Splash_Screen.html ./HelpFiles/Splash_Screen/Splash_screen.png ./HelpFiles/Splash_Screen/Thumbs.db -./HelpFiles/Toolbar/All_View/All_Toolbar.png -./HelpFiles/Toolbar/All_View/All_View.html -./HelpFiles/Toolbar/All_View/All_view.png -./HelpFiles/Toolbar/All_View/Thumbs.db -./HelpFiles/Toolbar/All_View/Volume_ID_button.png -./HelpFiles/Toolbar/Chart_View/Chart_View.html -./HelpFiles/Toolbar/Chart_View/ChartView_dtseries_DataSeries.png -./HelpFiles/Toolbar/Chart_View/ChartView_pscalar_Matrix.png -./HelpFiles/Toolbar/Chart_View/Data-Time_Series_Chart_View/ChartDataSeries.png -./HelpFiles/Toolbar/Chart_View/Data-Time_Series_Chart_View/ChartView_DataSeries_Toolbar.png -./HelpFiles/Toolbar/Chart_View/Data-Time_Series_Chart_View/ChartView_dtseries_DataSeries.png -./HelpFiles/Toolbar/Chart_View/Data-Time_Series_Chart_View/ChartView_pscalar_Matrix.png -./HelpFiles/Toolbar/Chart_View/Data-Time_Series_Chart_View/Data-Time_Series_Chart_View.html -./HelpFiles/Toolbar/Chart_View/Matrix_Chart_View/ChartView_Matrix_Toolbar.png -./HelpFiles/Toolbar/Chart_View/Matrix_Chart_View/Matrix_Chart_View.html -./HelpFiles/Toolbar/Chart_View/Matrix_Chart_View/pconn_matrix_montage.png -./HelpFiles/Toolbar/Chart_View/Matrix_Chart_View/pscalar_matrix_surface.png -./HelpFiles/Toolbar/Chart_View/Montage_toolbar.png -./HelpFiles/Toolbar/Chart_View/Thumbs.db +./HelpFiles/Toolbar/AllToolbar.png ./HelpFiles/Toolbar/Clipping/Clipping.html ./HelpFiles/Toolbar/Clipping/Clipping_Planes.png ./HelpFiles/Toolbar/Clipping/Thumbs.db ./HelpFiles/Toolbar/Custom_Orientation/Custom_Orientation.html ./HelpFiles/Toolbar/Custom_Orientation/Custom_Orientation.png +./HelpFiles/Toolbar/HistogramLineChartToolbar.png +./HelpFiles/Toolbar/LineSeriesChartOldToolbar.png +./HelpFiles/Toolbar/MatrixChartOldToolbar.png +./HelpFiles/Toolbar/MatrixChartToolbar.png ./HelpFiles/Toolbar/Mode/Border_Drawing/Border_Drawing.html ./HelpFiles/Toolbar/Mode/Border_Drawing/Border_drawing.png ./HelpFiles/Toolbar/Mode/Border_Drawing/Border_Editing/Border_edit.png @@ -197,37 +211,27 @@ ./HelpFiles/Toolbar/Mode/Foci_Creation/Foci_Editing/Edit_Focus_box.png ./HelpFiles/Toolbar/Mode/Foci_Creation/Foci_Editing/Foci_edit.png ./HelpFiles/Toolbar/Mode/Foci_Creation/Foci_Editing/Foci_Editing.html -./HelpFiles/Toolbar/Mode/Foci_Creation/Foci_Editing/Thumbs.db ./HelpFiles/Toolbar/Mode/Foci_Creation/Foci_Tools.png -./HelpFiles/Toolbar/Mode/Foci_Creation/Thumbs.db ./HelpFiles/Toolbar/Mode/Foci_Tools.png ./HelpFiles/Toolbar/Mode/Mode.html ./HelpFiles/Toolbar/Mode/Thumbs.db ./HelpFiles/Toolbar/Mode/Volume_Tools.png -./HelpFiles/Toolbar/Montage_View/Montage_toolbar.png -./HelpFiles/Toolbar/Montage_View/Montage_View.html -./HelpFiles/Toolbar/Montage_View/Thumbs.db -./HelpFiles/Toolbar/Surface_View/Surface_View.html -./HelpFiles/Toolbar/Surface_View/SurfaceView.png -./HelpFiles/Toolbar/Surface_View/Thumbs.db +./HelpFiles/Toolbar/MontageToolbar.png +./HelpFiles/Toolbar/SurfaceToolbar.png ./HelpFiles/Toolbar/Tab_Functions/Tab_Functions.html ./HelpFiles/Toolbar/Tab_Functions/Tab_Functions.png ./HelpFiles/Toolbar/Thumbs.db ./HelpFiles/Toolbar/Toolbar.html ./HelpFiles/Toolbar/Toolbar_icon.png -./HelpFiles/Toolbar/Volume_View/Thumbs.db -./HelpFiles/Toolbar/Volume_View/Volume_ID_button.png -./HelpFiles/Toolbar/Volume_View/Volume_View.html -./HelpFiles/Toolbar/Volume_View/VolumeView.png +./HelpFiles/Toolbar/Volume_ID_button.png +./HelpFiles/Toolbar/VolumeToolbar.png ./HelpFiles/Toolbar/VolumeView.png +./HelpFiles/WBwindow_Horiz_Labels.png +./HelpFiles/WindowForScene.png ./HelpFiles/Workbench_Window/Active_Tab/Active_Tab.html -./HelpFiles/Workbench_Window/Active_Tab/Thumbs.db ./HelpFiles/Workbench_Window/Active_Tab/Viewing_Tabs.png -./HelpFiles/Workbench_Window/Thumbs.db -./HelpFiles/Workbench_Window/Tooltip/Thumbs.db ./HelpFiles/Workbench_Window/Tooltip/Tooltip.html ./HelpFiles/Workbench_Window/Tooltip/Tooltip.png -./HelpFiles/Workbench_Window/Viewing_Area/Thumbs.db ./HelpFiles/Workbench_Window/Viewing_Area/Viewing_Area.html ./HelpFiles/Workbench_Window/Viewing_Area/WBwindow_Horiz_Labels.png ./HelpFiles/Workbench_Window/Viewing_Tabs/Thumbs.db @@ -241,9 +245,10 @@ ./HelpFiles/Workbench_Window/Window_Elements_Hide-Show_Buttons/Information_icon.png ./HelpFiles/Workbench_Window/Window_Elements_Hide-Show_Buttons/Overlay_TB_icon.png ./HelpFiles/Workbench_Window/Window_Elements_Hide-Show_Buttons/Scenes_icon.png -./HelpFiles/Workbench_Window/Window_Elements_Hide-Show_Buttons/Thumbs.db ./HelpFiles/Workbench_Window/Window_Elements_Hide-Show_Buttons/Toolbar_icon.png ./HelpFiles/Workbench_Window/Window_Elements_Hide-Show_Buttons/Window_Elements_Hide-Show_Buttons.html +./HelpFiles/Workbench_Window/Window_Elements_Hide-Show_Buttons/WindowForScene.tiff ./HelpFiles/Workbench_Window/Workbench_Window.html +./update_resources.sh diff --git a/src/Resources/GuiResources/update_resources.sh b/src/Resources/Help/update_resources.sh similarity index 69% rename from src/Resources/GuiResources/update_resources.sh rename to src/Resources/Help/update_resources.sh index 4276cbc3a99742c8b1ae224280dde92bf94a09b7..4bb80c07f7a9b7481d2865c0cf0d9f7bca217e79 100755 --- a/src/Resources/GuiResources/update_resources.sh +++ b/src/Resources/Help/update_resources.sh @@ -1,12 +1,13 @@ #!/bin/sh +rm -rf OLD_HelpFiles mv HelpFiles OLD_HelpFiles -cp -r /mnt/myelin/shared/WB_Tutorial/WB_1.2_Help ./HelpFiles +cp -r /mnt/myelin/shared/WB_Tutorial/WB_1.3_Help ./HelpFiles rm -rf OLD_HelpFiles -rcc -project -o gui_resources.qrc +rcc -project -o help_resources.qrc echo "" echo "You now need to:" diff --git a/src/Scenes/CMakeLists.txt b/src/Scenes/CMakeLists.txt index 7708f3fe5f9dfbf561f328e4e4b32d8272d1e30e..ae9c8040434ffd7131e632301c8431491183db5a 100644 --- a/src/Scenes/CMakeLists.txt +++ b/src/Scenes/CMakeLists.txt @@ -14,15 +14,20 @@ PROJECT (Scenes) # #ADD_DEFINITIONS(-DCARET_SCENE_DEBUG) -# -# Use XML from Qt but not GUI. -# -SET(QT_DONT_USE_QTGUI TRUE) - # # QT include files # -INCLUDE(${QT_USE_FILE}) +SET(CARET_QT_LINK_MODULES "") +if(Qt5_FOUND) + include_directories(${Qt5Core_INCLUDE_DIRS}) + #include_directories(${Qt5Network_INCLUDE_DIRS}) + include_directories(${Qt5Xml_INCLUDE_DIRS}) + SET(CARET_QT_LINK_MODULES "Qt5::Core") +endif() +IF (QT4_FOUND) + SET(QT_DONT_USE_QTGUI TRUE) + INCLUDE(${QT_USE_FILE}) +ENDIF () # # Create a Library @@ -39,6 +44,7 @@ SceneClassArray.h SceneClassAssistant.h SceneEnumeratedTypeArray.h SceneEnumeratedType.h +SceneFileBasePathTypeEnum.h SceneFloat.h SceneFloatArray.h SceneInfo.h @@ -75,6 +81,7 @@ SceneClassArray.cxx SceneClassAssistant.cxx SceneEnumeratedTypeArray.cxx SceneEnumeratedType.cxx +SceneFileBasePathTypeEnum.cxx SceneFloat.cxx SceneFloatArray.cxx SceneInfo.cxx @@ -98,6 +105,8 @@ SceneUnsignedByteArray.cxx SceneWriterXml.cxx ) +TARGET_LINK_LIBRARIES(Scenes ${CARET_QT5_LINK}) + # # Find headers # diff --git a/src/Scenes/Scene.cxx b/src/Scenes/Scene.cxx index 4a99dff3398c5ec514ed3dd7590649dd0030a026..9f29f260d114918011cbdf002b28794e3a3e6d90 100644 --- a/src/Scenes/Scene.cxx +++ b/src/Scenes/Scene.cxx @@ -231,14 +231,16 @@ SomeClass::restoreFromScene(const SceneAttributes* sceneAttributes, * Type of scene. */ Scene::Scene(const SceneTypeEnum::Enum sceneType) -: CaretObject() +:CaretObjectTracksModification() { - m_sceneAttributes = new SceneAttributes(sceneType); + m_sceneAttributes = new SceneAttributes(sceneType, + this); m_hasFilesWithRemotePaths = false; m_sceneInfo = new SceneInfo(); } -Scene::Scene(const Scene& rhs) : CaretObject() +Scene::Scene(const Scene& rhs) +:CaretObjectTracksModification() { m_sceneAttributes = new SceneAttributes(*(rhs.m_sceneAttributes)); m_hasFilesWithRemotePaths = rhs.m_hasFilesWithRemotePaths; @@ -265,6 +267,28 @@ Scene::~Scene() delete m_sceneInfo; } +/** + * @return All descendant SceneClasses (children, grandchildren, etc.) of this instance. + */ +std::vector +Scene::getDescendants() const +{ + std::vector descendants; + + const int32_t numberOfSceneClasses = this->getNumberOfClasses(); + for (int32_t i = 0; i < numberOfSceneClasses; i++) { + SceneObject* object = m_sceneClasses[i]; + descendants.push_back(object); + + std::vector objectDescendants = object->getDescendants(); + descendants.insert(descendants.end(), + objectDescendants.begin(), + objectDescendants.end()); + } + + return descendants; +} + /** * @return Attributes of the scene */ @@ -288,6 +312,7 @@ Scene::addClass(SceneClass* sceneClass) { if (sceneClass != NULL) { m_sceneClasses.push_back(sceneClass); + setModified(); } } @@ -311,6 +336,7 @@ const SceneClass* Scene::getClassAtIndex(const int32_t indx) const { CaretAssertVectorIndex(m_sceneClasses, indx); + m_sceneClasses[indx]->setRestored(true); return m_sceneClasses[indx]; } @@ -326,6 +352,7 @@ Scene::getClassWithName(const AString& sceneClassName) const const int32_t numberOfSceneClasses = this->getNumberOfClasses(); for (int32_t i = 0; i < numberOfSceneClasses; i++) { if (m_sceneClasses[i]->getName() == sceneClassName) { + m_sceneClasses[i]->setRestored(true); return m_sceneClasses[i]; } } @@ -469,6 +496,33 @@ Scene::setSceneInfo(SceneInfo* sceneInfo) delete m_sceneInfo; } m_sceneInfo = sceneInfo; + setModified(); +} + +/** + * @return True if this scene is modified. + */ +bool +Scene::isModified() const +{ + if (CaretObjectTracksModification::isModified()) { + return true; + } + if (m_sceneInfo->isModified()) { + return true; + } + + return false; +} + +/** + * Clear the modified status of this scene. + */ +void +Scene::clearModified() +{ + CaretObjectTracksModification::clearModified(); + m_sceneInfo->clearModified(); } diff --git a/src/Scenes/Scene.h b/src/Scenes/Scene.h index 44232374a6c04ae29fd8556357744fc66b8c4ff4..939edc7d24d1d92f29abc02d5b064473c4fcd14e 100644 --- a/src/Scenes/Scene.h +++ b/src/Scenes/Scene.h @@ -22,15 +22,16 @@ /*LICENSE_END*/ -#include "CaretObject.h" +#include "CaretObjectTracksModification.h" #include "SceneTypeEnum.h" namespace caret { class SceneAttributes; class SceneClass; class SceneInfo; + class SceneObject; - class Scene : public CaretObject { + class Scene : public CaretObjectTracksModification { public: Scene(const SceneTypeEnum::Enum sceneType); @@ -48,6 +49,8 @@ namespace caret { SceneAttributes* getAttributes(); + std::vector getDescendants() const; + void addClass(SceneClass* sceneClass); int32_t getNumberOfClasses() const; @@ -78,6 +81,9 @@ namespace caret { void setHasFilesWithRemotePaths(const bool hasFilesWithRemotePaths); + virtual bool isModified() const override; + + virtual void clearModified() override; // ADD_NEW_METHODS_HERE diff --git a/src/Scenes/SceneAttributes.cxx b/src/Scenes/SceneAttributes.cxx index d034001c42561382963e9a8a09bb61d0ac953c6d..3199c7f65cf4d3cb411ed9217a0ea7f81fe01dfb 100644 --- a/src/Scenes/SceneAttributes.cxx +++ b/src/Scenes/SceneAttributes.cxx @@ -37,9 +37,15 @@ using namespace caret; /** * Constructor. + * + * @param sceneType + * Type of scene. + * @param scene + * The scene. */ -SceneAttributes::SceneAttributes(const SceneTypeEnum::Enum sceneType) -: CaretObject(), m_sceneType(sceneType) +SceneAttributes::SceneAttributes(const SceneTypeEnum::Enum sceneType, + const Scene* scene) +: CaretObject(), m_sceneType(sceneType), m_scene(scene) { m_restoreWindowBehaviorInSceneDisplay = RESTORE_WINDOW_POSITION_RELATIVE_TO_FIRST_AND_USE_SIZES; m_specFileNameSavedToScene = true; @@ -78,6 +84,16 @@ SceneAttributes::getSceneType() const return m_sceneType; } +/** + * @return The scene using these attributes. + */ +const Scene* +SceneAttributes::getScene() const +{ + return m_scene; +} + + /** * Set the indices of the tabs that are to be saved to the scene. * @param tabIndices diff --git a/src/Scenes/SceneAttributes.h b/src/Scenes/SceneAttributes.h index 39f6fab21b2d8b2120fec3133755465e780bcdf0..473954ed5ef02bc9feb5f16acbfc961d39779173 100644 --- a/src/Scenes/SceneAttributes.h +++ b/src/Scenes/SceneAttributes.h @@ -27,6 +27,8 @@ namespace caret { + class Scene; + class SceneAttributes : public CaretObject { public: @@ -36,7 +38,8 @@ namespace caret { RESTORE_WINDOW_IGNORE_ALL_POSITIONS_AND_SIZES }; - SceneAttributes(const SceneTypeEnum::Enum sceneType); + SceneAttributes(const SceneTypeEnum::Enum sceneType, + const Scene* scene); SceneAttributes(const SceneAttributes& rhs); @@ -44,6 +47,8 @@ namespace caret { SceneTypeEnum::Enum getSceneType() const; + const Scene* getScene() const; + void setIndicesOfTabsAndWindowsForSavingToScene(const std::vector& tabIndices, const std::vector& windowIndices); @@ -100,6 +105,8 @@ namespace caret { const SceneTypeEnum::Enum m_sceneType; + const Scene* m_scene; + std::vector m_indicesOfTabsForSavingToScene; std::vector m_indicesOfWindowsForSavingToScene; diff --git a/src/Scenes/SceneBoolean.cxx b/src/Scenes/SceneBoolean.cxx index db25df12337ea649deca5773bffc0f960d386828..6e057255af072e6032700cf8dca8219512888e96 100644 --- a/src/Scenes/SceneBoolean.cxx +++ b/src/Scenes/SceneBoolean.cxx @@ -86,6 +86,7 @@ SceneBoolean::setValue(const bool value) bool SceneBoolean::booleanValue() const { + m_restoredFlag = true; return m_value; } @@ -95,6 +96,7 @@ SceneBoolean::booleanValue() const float SceneBoolean::floatValue() const { + m_restoredFlag = true; const float f = (m_value ? 1.0 : 0.0); return f; } @@ -105,6 +107,7 @@ SceneBoolean::floatValue() const int32_t SceneBoolean::integerValue() const { + m_restoredFlag = true; const int32_t i = (m_value ? 1 : 0); return i; } @@ -115,6 +118,7 @@ SceneBoolean::integerValue() const AString SceneBoolean::stringValue() const { + m_restoredFlag = true; const AString s = (m_value ? "true" : "false"); return s; } @@ -125,5 +129,6 @@ SceneBoolean::stringValue() const uint8_t SceneBoolean::unsignedByteValue() const { + m_restoredFlag = true; return m_value; } diff --git a/src/Scenes/SceneClass.cxx b/src/Scenes/SceneClass.cxx index eac5b1a3ebc1b2d09ff733561fe438f4d97c6e33..36a60414ea82fecda1e720255289ec89f6eb177e 100644 --- a/src/Scenes/SceneClass.cxx +++ b/src/Scenes/SceneClass.cxx @@ -74,7 +74,7 @@ SceneClass::SceneClass(const AString& name, m_className(className), m_versionNumber(versionNumber) { - + } SceneClass::SceneClass(const SceneClass& rhs): SceneObject(rhs.getName(), SceneObjectDataTypeEnum::SCENE_CLASS), @@ -124,6 +124,30 @@ SceneClass::getVersionNumber() const return m_versionNumber; } +/** + * @return All descendant SceneClasses (children, grandchildren, etc.) of this instance. + */ +std::vector +SceneClass::getDescendants() const +{ + std::vector descendants; + + for (std::vector::const_iterator iter = m_childObjects.begin(); + iter != m_childObjects.end(); + iter++) { + SceneObject* so = *iter; + descendants.push_back(so); + + std::vector childDescendants = so->getDescendants(); + descendants.insert(descendants.end(), + childDescendants.begin(), + childDescendants.end()); + } + + return descendants; +} + + /** * Add a child to this class. * @param sceneObject @@ -486,10 +510,20 @@ AString SceneClass::getEnumeratedTypeValueAsString(const AString& name, const AString& defaultValue) const { - for (std::vector::const_iterator iter = m_childObjects.begin(); - iter != m_childObjects.end(); - iter++) { - const SceneObject* so = *iter; +// for (std::vector::const_iterator iter = m_childObjects.begin(); +// iter != m_childObjects.end(); +// iter++) { +// const SceneObject* so = *iter; +// const SceneEnumeratedType* st = dynamic_cast(so); +// if (st != NULL) { +// if (st->getName() == name) { +// return st->stringValue(); +// } +// } +// } + + const SceneObject* so = getObjectWithName(name); + if (so != NULL) { const SceneEnumeratedType* st = dynamic_cast(so); if (st != NULL) { if (st->getName() == name) { @@ -522,18 +556,29 @@ SceneClass::getEnumeratedTypeArrayValue(const AString& name, const int32_t arrayNumberOfElements, const AString& defaultValue) const { - for (std::vector::const_iterator iter = m_childObjects.begin(); - iter != m_childObjects.end(); - iter++) { - const SceneObject* so = *iter; +// for (std::vector::const_iterator iter = m_childObjects.begin(); +// iter != m_childObjects.end(); +// iter++) { +// const SceneObject* so = *iter; +// const SceneEnumeratedTypeArray* enumArray = dynamic_cast(so); +// if (enumArray != NULL) { +// if (enumArray->getName() == name) { +// enumArray->stringValues(values, +// arrayNumberOfElements, +// defaultValue); +// return enumArray->getNumberOfArrayElements(); +// } +// } +// } + + const SceneObject* so = getObjectWithName(name); + if (so != NULL) { const SceneEnumeratedTypeArray* enumArray = dynamic_cast(so); if (enumArray != NULL) { - if (enumArray->getName() == name) { - enumArray->stringValues(values, - arrayNumberOfElements, - defaultValue); - return enumArray->getNumberOfArrayElements(); - } + enumArray->stringValues(values, + arrayNumberOfElements, + defaultValue); + return enumArray->getNumberOfArrayElements(); } } @@ -789,15 +834,23 @@ SceneClass::getStringArrayValue(const AString& name, const ScenePrimitive* SceneClass::getPrimitive(const AString& name) const { - for (std::vector::const_iterator iter = m_childObjects.begin(); - iter != m_childObjects.end(); - iter++) { - const SceneObject* so = *iter; +// for (std::vector::const_iterator iter = m_childObjects.begin(); +// iter != m_childObjects.end(); +// iter++) { +// const SceneObject* so = *iter; +// const ScenePrimitive* sp = dynamic_cast(so); +// if (sp != NULL) { +// if (sp->getName() == name) { +// return sp; +// } +// } +// } + + const SceneObject* so = getObjectWithName(name); + if (so != NULL) { const ScenePrimitive* sp = dynamic_cast(so); if (sp != NULL) { - if (sp->getName() == name) { - return sp; - } + return sp; } } @@ -822,9 +875,7 @@ SceneClass::getPathName(const AString& name) const if (so != NULL) { const ScenePathName* sp = dynamic_cast(so); if (sp != NULL) { - if (sp->getName() == name) { - return sp; - } + return sp; } } @@ -845,15 +896,23 @@ SceneClass::getPathName(const AString& name) const const ScenePrimitiveArray* SceneClass::getPrimitiveArray(const AString& name) const { - for (std::vector::const_iterator iter = m_childObjects.begin(); - iter != m_childObjects.end(); - iter++) { - const SceneObject* so = *iter; +// for (std::vector::const_iterator iter = m_childObjects.begin(); +// iter != m_childObjects.end(); +// iter++) { +// const SceneObject* so = *iter; +// const ScenePrimitiveArray* spa = dynamic_cast(so); +// if (spa != NULL) { +// if (spa->getName() == name) { +// return spa; +// } +// } +// } + + const SceneObject* so = getObjectWithName(name); + if (so != NULL) { const ScenePrimitiveArray* spa = dynamic_cast(so); if (spa != NULL) { - if (spa->getName() == name) { - return spa; - } + return spa; } } @@ -873,15 +932,23 @@ SceneClass::getPrimitiveArray(const AString& name) const const ScenePathNameArray* SceneClass::getPathNameArray(const AString& name) const { - for (std::vector::const_iterator iter = m_childObjects.begin(); - iter != m_childObjects.end(); - iter++) { - const SceneObject* so = *iter; - if (so->getName() == name) { - const ScenePathNameArray* spa = dynamic_cast(so); - if (spa != NULL) { - return spa; - } +// for (std::vector::const_iterator iter = m_childObjects.begin(); +// iter != m_childObjects.end(); +// iter++) { +// const SceneObject* so = *iter; +// if (so->getName() == name) { +// const ScenePathNameArray* spa = dynamic_cast(so); +// if (spa != NULL) { +// return spa; +// } +// } +// } + + const SceneObject* so = getObjectWithName(name); + if (so != NULL) { + const ScenePathNameArray* spa = dynamic_cast(so); + if (spa != NULL) { + return spa; } } @@ -902,15 +969,23 @@ SceneClass::getPathNameArray(const AString& name) const const SceneClass* SceneClass::getClass(const AString& name) const { - for (std::vector::const_iterator iter = m_childObjects.begin(); - iter != m_childObjects.end(); - iter++) { - const SceneObject* so = *iter; +// for (std::vector::const_iterator iter = m_childObjects.begin(); +// iter != m_childObjects.end(); +// iter++) { +// const SceneObject* so = *iter; +// const SceneClass* sc = dynamic_cast(so); +// if (sc != NULL) { +// if (sc->getName() == name) { +// return sc; +// } +// } +// } + + const SceneObject* so = getObjectWithName(name); + if (so != NULL) { const SceneClass* sc = dynamic_cast(so); if (sc != NULL) { - if (sc->getName() == name) { - return sc; - } + return sc; } } @@ -931,15 +1006,24 @@ SceneClass::getClass(const AString& name) const SceneClass* SceneClass::getClass(const AString& name) { - for (std::vector::iterator iter = m_childObjects.begin(); - iter != m_childObjects.end(); - iter++) { - SceneObject* so = *iter; +// for (std::vector::iterator iter = m_childObjects.begin(); +// iter != m_childObjects.end(); +// iter++) { +// SceneObject* so = *iter; +// SceneClass* sc = dynamic_cast(so); +// if (sc != NULL) { +// if (sc->getName() == name) { +// return sc; +// } +// } +// } + + const SceneObject* soConst = getObjectWithName(name); + if (soConst != NULL) { + SceneObject* so = const_cast(soConst); SceneClass* sc = dynamic_cast(so); if (sc != NULL) { - if (sc->getName() == name) { - return sc; - } + return sc; } } @@ -987,15 +1071,24 @@ SceneClass::getMapIntegerKey(const AString& name) const SceneClassArray* SceneClass::getClassArray(const AString& name) { - for (std::vector::iterator iter = m_childObjects.begin(); - iter != m_childObjects.end(); - iter++) { - SceneObject* so = *iter; +// for (std::vector::iterator iter = m_childObjects.begin(); +// iter != m_childObjects.end(); +// iter++) { +// SceneObject* so = *iter; +// SceneClassArray* sca = dynamic_cast(so); +// if (sca != NULL) { +// if (sca->getName() == name) { +// return sca; +// } +// } +// } + + const SceneObject* soConst = getObjectWithName(name); + if (soConst != NULL) { + SceneObject* so = const_cast(soConst); SceneClassArray* sca = dynamic_cast(so); if (sca != NULL) { - if (sca->getName() == name) { - return sca; - } + return sca; } } @@ -1016,15 +1109,24 @@ SceneClass::getClassArray(const AString& name) const SceneClassArray* SceneClass::getClassArray(const AString& name) const { - for (std::vector::const_iterator iter = m_childObjects.begin(); - iter != m_childObjects.end(); - iter++) { - const SceneObject* so = *iter; +// for (std::vector::const_iterator iter = m_childObjects.begin(); +// iter != m_childObjects.end(); +// iter++) { +// const SceneObject* so = *iter; +// const SceneClassArray* sca = dynamic_cast(so); +// if (sca != NULL) { +// if (sca->getName() == name) { +// return sca; +// } +// } +// } + + const SceneObject* soConst = getObjectWithName(name); + if (soConst != NULL) { + SceneObject* so = const_cast(soConst); const SceneClassArray* sca = dynamic_cast(so); if (sca != NULL) { - if (sca->getName() == name) { - return sca; - } + return sca; } } @@ -1051,6 +1153,7 @@ const SceneObject* SceneClass::getObjectAtIndex(const int32_t indx) const { CaretAssertVectorIndex(m_childObjects, indx); + m_childObjects[indx]->m_restoredFlag = true; return m_childObjects[indx]; } @@ -1063,16 +1166,52 @@ SceneClass::getObjectAtIndex(const int32_t indx) const const SceneObject* SceneClass::getObjectWithName(const AString& name) const { + SceneObject* sceneObject = NULL; + bool wasLoggedFlag = false; + for (std::vector::const_iterator iter = m_childObjects.begin(); iter != m_childObjects.end(); iter++) { - const SceneObject* so = *iter; + SceneObject* so = *iter; if (so->getName() == name) { - return so; + so->m_restoredFlag = true; + + if (s_debugLoggingEnabledFlag) { + if (sceneObject != NULL) { + if ( ! wasLoggedFlag) { + CaretLogWarning("Scene class named \"" + + getName() + + "\" contains more than one child named \"" + + name + + "\". If replacing the scene does not eliminate this message " + "there is an error in the source code"); + wasLoggedFlag = true; + } + } + } + else { + return so; + } + + sceneObject = so; } } - return NULL; + return sceneObject; +} + +/** + * Set debug logging enabled. + * If enabled, message are logged at the warning level + * for duplicated objects and missing objects. + * + * @param status + * New status. + */ +void +SceneClass::setDebugLoggingEnabled(const bool status) +{ + s_debugLoggingEnabledFlag = status; } /** @@ -1085,7 +1224,13 @@ SceneClass::getObjectWithName(const AString& name) const void SceneClass::logMissing(const AString& missingInfo) const { - CaretLogFine(missingInfo); + if (missingInfo.isEmpty()) { + return; + } + + if (s_debugLoggingEnabledFlag) { + CaretLogFiner(missingInfo); + } } diff --git a/src/Scenes/SceneClass.h b/src/Scenes/SceneClass.h index 08a1bf68e1537d780cc9c6868d395dd069d5139a..758cd4c5a86963a7c4fd137872ae94835a291be4 100644 --- a/src/Scenes/SceneClass.h +++ b/src/Scenes/SceneClass.h @@ -50,6 +50,8 @@ namespace caret { int32_t getVersionNumber() const; + virtual std::vector getDescendants() const; + void addBoolean(const AString& name, const bool value); @@ -299,6 +301,8 @@ namespace caret { virtual SceneObject* clone() const; + static void setDebugLoggingEnabled(const bool status); + // ADD_NEW_METHODS_HERE // private: // and are not used @@ -336,12 +340,14 @@ namespace caret { std::vector m_childObjects; + static bool s_debugLoggingEnabledFlag; + // ADD_NEW_MEMBERS_HERE }; #ifdef __SCENE_CLASS_DECLARE__ - // + bool SceneClass::s_debugLoggingEnabledFlag = false; #endif // __SCENE_CLASS_DECLARE__ } // namespace diff --git a/src/Scenes/SceneClassArray.cxx b/src/Scenes/SceneClassArray.cxx index bb6ddb5dd410f458782b7bd9fd3801e4efda313d..6af530ff8673f4a60c0846874686803970f371e4 100644 --- a/src/Scenes/SceneClassArray.cxx +++ b/src/Scenes/SceneClassArray.cxx @@ -116,6 +116,29 @@ SceneClassArray::~SceneClassArray() m_values.clear(); } +/** + * @return All descendant SceneClasses (children, grandchildren, etc.) of this instance. + */ +std::vector +SceneClassArray::getDescendants() const +{ + std::vector descendants; + + for (std::vector::const_iterator iter = m_values.begin(); + iter != m_values.end(); + iter++) { + SceneClass* sceneClass = *iter; + descendants.push_back(sceneClass); + + std::vector classDescendants = sceneClass->getDescendants(); + descendants.insert(descendants.end(), + classDescendants.begin(), + classDescendants.end()); + } + + return descendants; +} + /** * Set the class for an array index. * @param arrayIndex @@ -156,5 +179,9 @@ const SceneClass* SceneClassArray::getClassAtIndex(const int32_t arrayIndex) const { CaretAssertVectorIndex(m_values, arrayIndex); - return m_values[arrayIndex]; + const SceneClass* sc = m_values[arrayIndex]; + if (sc != NULL) { + sc->m_restoredFlag = true; + } + return sc; } diff --git a/src/Scenes/SceneClassArray.h b/src/Scenes/SceneClassArray.h index 2b626842c5914b59308d9c09734db361e6c45c46..3ee4bb0ea54fc11e5b4589238c0c9089d6d05f02 100644 --- a/src/Scenes/SceneClassArray.h +++ b/src/Scenes/SceneClassArray.h @@ -50,6 +50,8 @@ namespace caret { const SceneClass* getClassAtIndex(const int32_t arrayIndex) const; + virtual std::vector getDescendants() const; + private: SceneClassArray& operator=(const SceneClassArray&); diff --git a/src/Scenes/SceneClassAssistant.h b/src/Scenes/SceneClassAssistant.h index 70f15c82b21049b83bb959b8482f2b42a9564b79..35c835cf3abad49c36cc35645c175868b420c0a1 100644 --- a/src/Scenes/SceneClassAssistant.h +++ b/src/Scenes/SceneClassAssistant.h @@ -185,6 +185,9 @@ namespace caret { const SceneClass& sceneClass) = 0; virtual void save(const SceneAttributes& sceneAttributes, SceneClass& sceneClass) = 0; + + AString getName() const { return m_name; } + protected: const AString m_name; diff --git a/src/Scenes/SceneFileBasePathTypeEnum.cxx b/src/Scenes/SceneFileBasePathTypeEnum.cxx new file mode 100644 index 0000000000000000000000000000000000000000..e64535c403284c4185a8dafab308dd6e9c596c48 --- /dev/null +++ b/src/Scenes/SceneFileBasePathTypeEnum.cxx @@ -0,0 +1,376 @@ + +/*LICENSE_START*/ +/* + * Copyright (C) 2017 Washington University School of Medicine + * + * 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 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ +/*LICENSE_END*/ + +#include +#define __SCENE_FILE_BASE_PATH_TYPE_ENUM_DECLARE__ +#include "SceneFileBasePathTypeEnum.h" +#undef __SCENE_FILE_BASE_PATH_TYPE_ENUM_DECLARE__ + +#include "CaretAssert.h" + +using namespace caret; + + +/** + * \class caret::SceneFileBasePathTypeEnum + * \brief Type of base path for the scene file. + * + * When Automatic, Workbench determines scene file at time of zipping + * When Custom, user chooses the base path + * + * Using this enumerated type in the GUI with an EnumComboBoxTemplate + * + * Header File (.h) + * Forward declare the data type: + * class EnumComboBoxTemplate; + * + * Declare the member: + * EnumComboBoxTemplate* m_sceneFileBasePathTypeEnumComboBox; + * + * Declare a slot that is called when user changes selection + * private slots: + * void sceneFileBasePathTypeEnumComboBoxItemActivated(); + * + * Implementation File (.cxx) + * Include the header files + * #include "EnumComboBoxTemplate.h" + * #include "SceneFileBasePathTypeEnum.h" + * + * Instatiate: + * m_sceneFileBasePathTypeEnumComboBox = new EnumComboBoxTemplate(this); + * m_sceneFileBasePathTypeEnumComboBox->setup(); + * + * Get notified when the user changes the selection: + * QObject::connect(m_sceneFileBasePathTypeEnumComboBox, SIGNAL(itemActivated()), + * this, SLOT(sceneFileBasePathTypeEnumComboBoxItemActivated())); + * + * Update the selection: + * m_sceneFileBasePathTypeEnumComboBox->setSelectedItem(NEW_VALUE); + * + * Read the selection: + * const SceneFileBasePathTypeEnum::Enum VARIABLE = m_sceneFileBasePathTypeEnumComboBox->getSelectedItem(); + * + */ + +/** + * Constructor. + * + * @param enumValue + * An enumerated value. + * @param name + * Name of enumerated value. + * + * @param guiName + * User-friendly name for use in user-interface. + */ +SceneFileBasePathTypeEnum::SceneFileBasePathTypeEnum(const Enum enumValue, + const AString& name, + const AString& guiName) +{ + this->enumValue = enumValue; + this->integerCode = integerCodeCounter++; + this->name = name; + this->guiName = guiName; +} + +/** + * Destructor. + */ +SceneFileBasePathTypeEnum::~SceneFileBasePathTypeEnum() +{ +} + +/** + * Initialize the enumerated metadata. + */ +void +SceneFileBasePathTypeEnum::initialize() +{ + if (initializedFlag) { + return; + } + initializedFlag = true; + + enumData.push_back(SceneFileBasePathTypeEnum(AUTOMATIC, + "AUTOMATIC", + "Automatic")); + + enumData.push_back(SceneFileBasePathTypeEnum(CUSTOM, + "CUSTOM", + "Custom")); + +} + +/** + * Find the data for and enumerated value. + * @param enumValue + * The enumerated value. + * @return Pointer to data for this enumerated type + * or NULL if no data for type or if type is invalid. + */ +const SceneFileBasePathTypeEnum* +SceneFileBasePathTypeEnum::findData(const Enum enumValue) +{ + if (initializedFlag == false) initialize(); + + size_t num = enumData.size(); + for (size_t i = 0; i < num; i++) { + const SceneFileBasePathTypeEnum* d = &enumData[i]; + if (d->enumValue == enumValue) { + return d; + } + } + + return NULL; +} + +/** + * Get a string representation of the enumerated type. + * @param enumValue + * Enumerated value. + * @return + * String representing enumerated value. + */ +AString +SceneFileBasePathTypeEnum::toName(Enum enumValue) { + if (initializedFlag == false) initialize(); + + const SceneFileBasePathTypeEnum* enumInstance = findData(enumValue); + return enumInstance->name; +} + +/** + * Get an enumerated value corresponding to its name. + * @param name + * Name of enumerated value. + * @param isValidOut + * If not NULL, it is set indicating that a + * enum value exists for the input name. + * @return + * Enumerated value. + */ +SceneFileBasePathTypeEnum::Enum +SceneFileBasePathTypeEnum::fromName(const AString& name, bool* isValidOut) +{ + if (initializedFlag == false) initialize(); + + bool validFlag = false; + Enum enumValue = SceneFileBasePathTypeEnum::enumData[0].enumValue; + + for (std::vector::iterator iter = enumData.begin(); + iter != enumData.end(); + iter++) { + const SceneFileBasePathTypeEnum& d = *iter; + if (d.name == name) { + enumValue = d.enumValue; + validFlag = true; + break; + } + } + + if (isValidOut != 0) { + *isValidOut = validFlag; + } + else if (validFlag == false) { + CaretAssertMessage(0, AString("Name " + name + "failed to match enumerated value for type SceneFileBasePathTypeEnum")); + } + return enumValue; +} + +/** + * Get a GUI string representation of the enumerated type. + * @param enumValue + * Enumerated value. + * @return + * String representing enumerated value. + */ +AString +SceneFileBasePathTypeEnum::toGuiName(Enum enumValue) { + if (initializedFlag == false) initialize(); + + const SceneFileBasePathTypeEnum* enumInstance = findData(enumValue); + return enumInstance->guiName; +} + +/** + * Get an enumerated value corresponding to its GUI name. + * @param s + * Name of enumerated value. + * @param isValidOut + * If not NULL, it is set indicating that a + * enum value exists for the input name. + * @return + * Enumerated value. + */ +SceneFileBasePathTypeEnum::Enum +SceneFileBasePathTypeEnum::fromGuiName(const AString& guiName, bool* isValidOut) +{ + if (initializedFlag == false) initialize(); + + bool validFlag = false; + Enum enumValue = SceneFileBasePathTypeEnum::enumData[0].enumValue; + + for (std::vector::iterator iter = enumData.begin(); + iter != enumData.end(); + iter++) { + const SceneFileBasePathTypeEnum& d = *iter; + if (d.guiName == guiName) { + enumValue = d.enumValue; + validFlag = true; + break; + } + } + + if (isValidOut != 0) { + *isValidOut = validFlag; + } + else if (validFlag == false) { + CaretAssertMessage(0, AString("guiName " + guiName + "failed to match enumerated value for type SceneFileBasePathTypeEnum")); + } + return enumValue; +} + +/** + * Get the integer code for a data type. + * + * @return + * Integer code for data type. + */ +int32_t +SceneFileBasePathTypeEnum::toIntegerCode(Enum enumValue) +{ + if (initializedFlag == false) initialize(); + const SceneFileBasePathTypeEnum* enumInstance = findData(enumValue); + return enumInstance->integerCode; +} + +/** + * Find the data type corresponding to an integer code. + * + * @param integerCode + * Integer code for enum. + * @param isValidOut + * If not NULL, on exit isValidOut will indicate if + * integer code is valid. + * @return + * Enum for integer code. + */ +SceneFileBasePathTypeEnum::Enum +SceneFileBasePathTypeEnum::fromIntegerCode(const int32_t integerCode, bool* isValidOut) +{ + if (initializedFlag == false) initialize(); + + bool validFlag = false; + Enum enumValue = SceneFileBasePathTypeEnum::enumData[0].enumValue; + + for (std::vector::iterator iter = enumData.begin(); + iter != enumData.end(); + iter++) { + const SceneFileBasePathTypeEnum& enumInstance = *iter; + if (enumInstance.integerCode == integerCode) { + enumValue = enumInstance.enumValue; + validFlag = true; + break; + } + } + + if (isValidOut != 0) { + *isValidOut = validFlag; + } + else if (validFlag == false) { + CaretAssertMessage(0, AString("Integer code " + AString::number(integerCode) + "failed to match enumerated value for type SceneFileBasePathTypeEnum")); + } + return enumValue; +} + +/** + * Get all of the enumerated type values. The values can be used + * as parameters to toXXX() methods to get associated metadata. + * + * @param allEnums + * A vector that is OUTPUT containing all of the enumerated values. + */ +void +SceneFileBasePathTypeEnum::getAllEnums(std::vector& allEnums) +{ + if (initializedFlag == false) initialize(); + + allEnums.clear(); + + for (std::vector::iterator iter = enumData.begin(); + iter != enumData.end(); + iter++) { + allEnums.push_back(iter->enumValue); + } +} + +/** + * Get all of the names of the enumerated type values. + * + * @param allNames + * A vector that is OUTPUT containing all of the names of the enumerated values. + * @param isSorted + * If true, the names are sorted in alphabetical order. + */ +void +SceneFileBasePathTypeEnum::getAllNames(std::vector& allNames, const bool isSorted) +{ + if (initializedFlag == false) initialize(); + + allNames.clear(); + + for (std::vector::iterator iter = enumData.begin(); + iter != enumData.end(); + iter++) { + allNames.push_back(SceneFileBasePathTypeEnum::toName(iter->enumValue)); + } + + if (isSorted) { + std::sort(allNames.begin(), allNames.end()); + } +} + +/** + * Get all of the GUI names of the enumerated type values. + * + * @param allNames + * A vector that is OUTPUT containing all of the GUI names of the enumerated values. + * @param isSorted + * If true, the names are sorted in alphabetical order. + */ +void +SceneFileBasePathTypeEnum::getAllGuiNames(std::vector& allGuiNames, const bool isSorted) +{ + if (initializedFlag == false) initialize(); + + allGuiNames.clear(); + + for (std::vector::iterator iter = enumData.begin(); + iter != enumData.end(); + iter++) { + allGuiNames.push_back(SceneFileBasePathTypeEnum::toGuiName(iter->enumValue)); + } + + if (isSorted) { + std::sort(allGuiNames.begin(), allGuiNames.end()); + } +} + diff --git a/src/Scenes/SceneFileBasePathTypeEnum.h b/src/Scenes/SceneFileBasePathTypeEnum.h new file mode 100644 index 0000000000000000000000000000000000000000..4079f01d7e05fba4349b5f90415d19d9b7253d08 --- /dev/null +++ b/src/Scenes/SceneFileBasePathTypeEnum.h @@ -0,0 +1,104 @@ +#ifndef __SCENE_FILE_BASE_PATH_TYPE_ENUM_H__ +#define __SCENE_FILE_BASE_PATH_TYPE_ENUM_H__ + +/*LICENSE_START*/ +/* + * Copyright (C) 2017 Washington University School of Medicine + * + * 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 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ +/*LICENSE_END*/ + + +#include +#include +#include "AString.h" + +namespace caret { + +class SceneFileBasePathTypeEnum { + +public: + /** + * Enumerated values. + */ + enum Enum { + /** Automatic */ + AUTOMATIC, + /** Custom */ + CUSTOM + }; + + + ~SceneFileBasePathTypeEnum(); + + static AString toName(Enum enumValue); + + static Enum fromName(const AString& name, bool* isValidOut); + + static AString toGuiName(Enum enumValue); + + static Enum fromGuiName(const AString& guiName, bool* isValidOut); + + static int32_t toIntegerCode(Enum enumValue); + + static Enum fromIntegerCode(const int32_t integerCode, bool* isValidOut); + + static void getAllEnums(std::vector& allEnums); + + static void getAllNames(std::vector& allNames, const bool isSorted); + + static void getAllGuiNames(std::vector& allGuiNames, const bool isSorted); + +private: + SceneFileBasePathTypeEnum(const Enum enumValue, + const AString& name, + const AString& guiName); + + static const SceneFileBasePathTypeEnum* findData(const Enum enumValue); + + /** Holds all instance of enum values and associated metadata */ + static std::vector enumData; + + /** Initialize instances that contain the enum values and metadata */ + static void initialize(); + + /** Indicates instance of enum values and metadata have been initialized */ + static bool initializedFlag; + + /** Auto generated integer codes */ + static int32_t integerCodeCounter; + + /** The enumerated type value for an instance */ + Enum enumValue; + + /** The integer code associated with an enumerated value */ + int32_t integerCode; + + /** The name, a text string that is identical to the enumerated value */ + AString name; + + /** A user-friendly name that is displayed in the GUI */ + AString guiName; +}; + +#ifdef __SCENE_FILE_BASE_PATH_TYPE_ENUM_DECLARE__ +std::vector SceneFileBasePathTypeEnum::enumData; +bool SceneFileBasePathTypeEnum::initializedFlag = false; +int32_t SceneFileBasePathTypeEnum::integerCodeCounter = 0; +#endif // __SCENE_FILE_BASE_PATH_TYPE_ENUM_DECLARE__ + +} // namespace +#endif //__SCENE_FILE_BASE_PATH_TYPE_ENUM_H__ diff --git a/src/Scenes/SceneFloat.cxx b/src/Scenes/SceneFloat.cxx index acf1d64f38016eeb1047d9dd94c3f8fab4a8bd06..25dedfc0da80de370771c94942dbf82034bb76ab 100644 --- a/src/Scenes/SceneFloat.cxx +++ b/src/Scenes/SceneFloat.cxx @@ -88,6 +88,7 @@ SceneFloat::setValue(const float value) bool SceneFloat::booleanValue() const { + m_restoredFlag = true; const bool b = ((m_value != 0.0) ? true : false); return b; } @@ -98,6 +99,7 @@ SceneFloat::booleanValue() const float SceneFloat::floatValue() const { + m_restoredFlag = true; return m_value; } @@ -107,6 +109,7 @@ SceneFloat::floatValue() const int32_t SceneFloat::integerValue() const { + m_restoredFlag = true; if (m_value > std::numeric_limits::max()) { return std::numeric_limits::max(); } @@ -124,6 +127,7 @@ SceneFloat::integerValue() const AString SceneFloat::stringValue() const { + m_restoredFlag = true; const AString s = AString::number(m_value); return s; } @@ -137,6 +141,7 @@ SceneFloat::stringValue() const uint8_t SceneFloat::unsignedByteValue() const { + m_restoredFlag = true; if (m_value > std::numeric_limits::max()) { return std::numeric_limits::max(); } diff --git a/src/Scenes/SceneInfo.cxx b/src/Scenes/SceneInfo.cxx index 24ec7a23aa3ca9d76e1dbcbc7dd80d7edc501161..a5e3cfbe4d8114270341d9491ee5e4e10f7af2ba 100644 --- a/src/Scenes/SceneInfo.cxx +++ b/src/Scenes/SceneInfo.cxx @@ -42,12 +42,12 @@ using namespace caret; * Constructor. */ SceneInfo::SceneInfo() -: CaretObject() +: CaretObjectTracksModification() { } -SceneInfo::SceneInfo(const SceneInfo& rhs) : CaretObject() +SceneInfo::SceneInfo(const SceneInfo& rhs) : CaretObjectTracksModification() { m_sceneName = rhs.m_sceneName; m_sceneDescription = rhs.m_sceneDescription; @@ -80,7 +80,10 @@ SceneInfo::getName() const void SceneInfo::setName(const AString& sceneName) { - m_sceneName = sceneName; + if (sceneName != m_sceneName) { + m_sceneName = sceneName; + setModified(); + } } /** @@ -100,7 +103,10 @@ SceneInfo::getDescription() const void SceneInfo::setDescription(const AString& sceneDescription) { - m_sceneDescription = sceneDescription; + if (sceneDescription != m_sceneDescription) { + m_sceneDescription = sceneDescription; + setModified(); + } } /** @@ -115,8 +121,11 @@ void SceneInfo::setImageBytes(const QByteArray& imageBytes, const AString& imageFormat) { - m_imageBytes = imageBytes; - m_imageFormat = imageFormat; + if ((imageBytes != m_imageBytes) + || (imageFormat != m_imageFormat)) { + m_imageBytes = imageBytes; + m_imageFormat = imageFormat; + } } /** @@ -169,7 +178,10 @@ SceneInfo::getBalsaSceneID() const void SceneInfo::setBalsaSceneID(const AString& balsaSceneID) { - m_balsaSceneID = balsaSceneID; + if (balsaSceneID != m_balsaSceneID) { + m_balsaSceneID = balsaSceneID; + setModified(); + } } /** @@ -233,7 +245,7 @@ SceneInfo::writeSceneInfoImage(XmlWriter& xmlWriter, if (imageBytes.length() > 0) { //QString base64String(imageBytes.toBase64()); const QByteArray base64ByteArray(imageBytes.toBase64()); - QString base64String = QString::fromAscii(base64ByteArray.constData(), + QString base64String = QString::fromLatin1(base64ByteArray.constData(), base64ByteArray.size()); XmlAttributes attributes; attributes.addAttribute(SceneXmlElements::SCENE_INFO_IMAGE_ENCODING_ATTRIBUTE, @@ -270,7 +282,7 @@ SceneInfo::setImageFromText(const AString& text, if ( ! text.isEmpty()) { if (encoding == SceneXmlElements::SCENE_INFO_ENCODING_BASE64_NAME) { - m_imageBytes = QByteArray::fromBase64(text.toAscii()); + m_imageBytes = QByteArray::fromBase64(text.toLatin1()); m_imageFormat = imageFormat; } else { diff --git a/src/Scenes/SceneInfo.h b/src/Scenes/SceneInfo.h index 5ccb646cb2aecadfd4c300afb337375c4276e6df..b4358c77e4811392f42ae39ec0a5867fd27b6f18 100644 --- a/src/Scenes/SceneInfo.h +++ b/src/Scenes/SceneInfo.h @@ -22,14 +22,14 @@ /*LICENSE_END*/ #include -#include "CaretObject.h" +#include "CaretObjectTracksModification.h" namespace caret { class XmlWriter; - class SceneInfo : public CaretObject { + class SceneInfo : public CaretObjectTracksModification { public: SceneInfo(); diff --git a/src/Scenes/SceneInteger.cxx b/src/Scenes/SceneInteger.cxx index f9dec5a7039093fec85276dd5dde97e4ccc6559e..83ec5b12900916aaddf549143fabe024c889bd5b 100644 --- a/src/Scenes/SceneInteger.cxx +++ b/src/Scenes/SceneInteger.cxx @@ -88,6 +88,7 @@ SceneInteger::setValue(const int32_t value) bool SceneInteger::booleanValue() const { + m_restoredFlag = true; const bool b = ((m_value != 0.0) ? true : false); return b; } @@ -98,6 +99,7 @@ SceneInteger::booleanValue() const float SceneInteger::floatValue() const { + m_restoredFlag = true; const float f = static_cast(m_value); return f; } @@ -108,6 +110,7 @@ SceneInteger::floatValue() const int32_t SceneInteger::integerValue() const { + m_restoredFlag = true; return m_value; } @@ -117,6 +120,7 @@ SceneInteger::integerValue() const AString SceneInteger::stringValue() const { + m_restoredFlag = true; const AString s = AString::number(m_value); return s; } @@ -130,6 +134,7 @@ SceneInteger::stringValue() const uint8_t SceneInteger::unsignedByteValue() const { + m_restoredFlag = true; if (m_value > std::numeric_limits::max()) { return std::numeric_limits::max(); } diff --git a/src/Scenes/SceneObject.cxx b/src/Scenes/SceneObject.cxx index 0e539d16fede48d8244def59833df22c15456ffc..f1b085d567f43bf055fbc6528689d55fbe32dd53 100644 --- a/src/Scenes/SceneObject.cxx +++ b/src/Scenes/SceneObject.cxx @@ -24,6 +24,7 @@ #undef __SCENE_OBJECT_DECLARE__ #include "CaretAssert.h" +#include "CaretLogger.h" using namespace caret; @@ -47,9 +48,9 @@ using namespace caret; SceneObject::SceneObject(const QString& name, const SceneObjectDataTypeEnum::Enum dataType) #ifdef CARET_SCENE_DEBUG -: CaretObject(), m_name(name), m_dataType(dataType) +: CaretObject(), m_name(name), m_dataType(dataType), m_restoredFlag(false) #else // CARET_SCENE_DEBUG -: m_name(name), m_dataType(dataType) +: m_name(name), m_dataType(dataType), m_restoredFlag(false) #endif // CARET_SCENE_DEBUG { CaretAssert(name.isEmpty() == false); @@ -81,6 +82,114 @@ SceneObject::getDataType() const return m_dataType; } +/** + * @return Has this instance been restored? + */ +bool +SceneObject::isRestored() const +{ + return m_restoredFlag; +} + +/** + * Set this instance restored. + * + * @param restoredFlag + * New restored status + */ +void +SceneObject::setRestored(const bool restoredFlag) const +{ + m_restoredFlag = restoredFlag; +} + +/** + * Set this instance and all descendants restored. + * + * @param restoredFlag + * New restored status + */ +void +SceneObject::setDescendantsRestored(const bool restoredFlag) const +{ + setRestored(restoredFlag); + std::vector descendants = getDescendants(); + setRestoredStatus(descendants, restoredFlag); +} + +/** + * Set the restored status for all scene objects. + * + * @param sceneObject + * Scene objects whose restored status is set. + * @param restoredStatus + * New status applied to all scene object. + */ +void +SceneObject::setRestoredStatus(std::vector sceneObjects, + const bool restoredStatus) +{ + for (std::vector::iterator iter = sceneObjects.begin(); + iter != sceneObjects.end(); + iter++) { + (*iter)->m_restoredFlag = restoredStatus; + } +} + +/** + * If scene objects failed to restore, log at the warning level. + * + * @param sceneName + * Name of the scene. + * @param sceneObjects + * Scene objects that are verified restored. + */ +void +SceneObject::logObjectsFailedRestore(const AString& sceneName, + const std::vector sceneObjects) +{ + int64_t failCount = 0; + + AString msg; + + for (std::vector::const_iterator iter = sceneObjects.begin(); + iter != sceneObjects.end(); + iter++) { + if ( ! (*iter)->m_restoredFlag) { + failCount++; + + if (failCount < 25) { + msg.appendWithNewLine(" " + + (*iter)->toString()); + } + } + } + + if (failCount > 0) { + CaretLogWarning(sceneName + + "\n" + "Failed to restore " + + AString::number(failCount) + + " scene objects.\n" + + msg); + } +} + + +/** + * Get all descendants of this instance. This method should be + * overridden by sub-classes that have children. + * + * @return All descendant SceneClasses (children, grandchildren, etc.) of this instance. + */ +std::vector +SceneObject::getDescendants() const +{ + std::vector descendants; + return descendants; +} + + /** * Get a description of this object's content. * @return String describing this object's content. @@ -88,5 +197,9 @@ SceneObject::getDataType() const AString SceneObject::toString() const { - return "SceneObject"; + QString objectInfo("Scene Object Type=" + + SceneObjectDataTypeEnum::toName(m_dataType) + + " name=" + + m_name); + return objectInfo; } diff --git a/src/Scenes/SceneObject.h b/src/Scenes/SceneObject.h index a6c73914394ef055bf479d3f7eb9555bdf6443f4..bd4b0070931802e3eae2005d47260611866ea286 100644 --- a/src/Scenes/SceneObject.h +++ b/src/Scenes/SceneObject.h @@ -40,6 +40,21 @@ namespace caret { SceneObjectDataTypeEnum::Enum getDataType() const; + bool isRestored() const; + + void setRestored(const bool restoredFlag) const; + + void setDescendantsRestored(const bool restoredFlag) const; + + static void setRestoredStatus(std::vector sceneObjects, + const bool restoredStatus); + + static void logObjectsFailedRestore(const AString& sceneName, + const std::vector sceneObjects); + + /// Should be overridden by any sub-classes that have children + virtual std::vector getDescendants() const; + protected: SceneObject(const QString& name, const SceneObjectDataTypeEnum::Enum dataType); @@ -66,6 +81,16 @@ namespace caret { /** Type of object */ const SceneObjectDataTypeEnum::Enum m_dataType; + protected: + mutable bool m_restoredFlag; + + private: + friend class SceneClass; + friend class SceneClassArray; + friend class SceneObjectMapIntegerKey; + friend class ScenePathName; + friend class ScenePathNameArray; + }; #ifdef __SCENE_OBJECT_DECLARE__ diff --git a/src/Scenes/SceneObjectArray.cxx b/src/Scenes/SceneObjectArray.cxx index a44c166eea66189474fd9265f7b24899b698b22b..7ef2a8d0f23ba37bda3972c7ff19bc70ab5dc61e 100644 --- a/src/Scenes/SceneObjectArray.cxx +++ b/src/Scenes/SceneObjectArray.cxx @@ -59,3 +59,4 @@ SceneObjectArray::~SceneObjectArray() { } + diff --git a/src/Scenes/SceneObjectMapIntegerKey.cxx b/src/Scenes/SceneObjectMapIntegerKey.cxx index e36ed985b55ec7f1b9a929e4a9266fc7a11fac5d..362a439369bac07ad7f075900f83fd3a9711405d 100644 --- a/src/Scenes/SceneObjectMapIntegerKey.cxx +++ b/src/Scenes/SceneObjectMapIntegerKey.cxx @@ -78,6 +78,38 @@ SceneObjectMapIntegerKey::~SceneObjectMapIntegerKey() } } +/** + * @return True if this map's content is empty, else false. + */ +bool +SceneObjectMapIntegerKey::isEmpty() const +{ + return m_dataMap.empty(); +} + +/** + * @return All descendant SceneClasses (children, grandchildren, etc.) of this instance. + */ +std::vector +SceneObjectMapIntegerKey::getDescendants() const +{ + std::vector descendants; + + for (DATA_MAP_CONST_ITERATOR iter = m_dataMap.begin(); + iter != m_dataMap.end(); + iter++) { + SceneObject* sceneObject = iter->second; + descendants.push_back(sceneObject); + + std::vector objectDescendants = sceneObject->getDescendants(); + descendants.insert(descendants.end(), + objectDescendants.begin(), + objectDescendants.end()); + } + + return descendants; +} + /** * Add the given boolean value to the map using the given key. * @param key @@ -223,6 +255,7 @@ SceneObjectMapIntegerKey::getObject(const int32_t key) const CaretAssert(iter != m_dataMap.end()); const SceneObject* object = iter->second; CaretAssert(object); + object->m_restoredFlag = true; return object; } diff --git a/src/Scenes/SceneObjectMapIntegerKey.h b/src/Scenes/SceneObjectMapIntegerKey.h index 6ab9baa10ef15999aee62d1706584edb536acc4b..9c430d16e338ac75cbce62319e8bc25fa1d97812 100644 --- a/src/Scenes/SceneObjectMapIntegerKey.h +++ b/src/Scenes/SceneObjectMapIntegerKey.h @@ -37,6 +37,8 @@ namespace caret { SceneObjectMapIntegerKey(const QString& name, const SceneObjectDataTypeEnum::Enum valueDataType); + virtual std::vector getDescendants() const; + private: SceneObjectMapIntegerKey(const SceneObjectMapIntegerKey&); @@ -86,9 +88,9 @@ namespace caret { SceneClass* value); std::vector getKeys() const; - + const std::map& getMap() const; - + const SceneObject* getObject(const int32_t key) const; const SceneClass* classValue(const int32_t key) const; @@ -121,6 +123,8 @@ namespace caret { virtual SceneObject* clone() const; + bool isEmpty() const; + private: typedef std::map DATA_MAP; diff --git a/src/Scenes/ScenePathName.cxx b/src/Scenes/ScenePathName.cxx index 51e867251c83dbd032936403427312b4dc07cebc..f37ad16d56fa7c3baa129cdd3a0be3f4e466e768 100644 --- a/src/Scenes/ScenePathName.cxx +++ b/src/Scenes/ScenePathName.cxx @@ -104,6 +104,7 @@ ScenePathName::setValue(const AString& value) AString ScenePathName::stringValue() const { + m_restoredFlag = true; return m_value; } diff --git a/src/Scenes/ScenePathNameArray.cxx b/src/Scenes/ScenePathNameArray.cxx index ed5676fd362c76fceaba7915460ff3a649365de1..4a53446f5215cd63e9abc417fca394e1b89acee0 100644 --- a/src/Scenes/ScenePathNameArray.cxx +++ b/src/Scenes/ScenePathNameArray.cxx @@ -122,6 +122,22 @@ ScenePathNameArray::~ScenePathNameArray() m_values.clear(); } +/** + * @return All descendant SceneClasses (children, grandchildren, etc.) of this instance. + */ +std::vector +ScenePathNameArray::getDescendants() const +{ + std::vector descendants; + + for (std::vector::const_iterator iter = m_values.begin(); + iter != m_values.end(); + iter++) { + descendants.push_back(*iter); + } + + return descendants; +} /** * Set the class for an array index. * @param arrayIndex @@ -172,6 +188,10 @@ ScenePathName* ScenePathNameArray::getScenePathNameAtIndex(const int32_t arrayIndex) const { CaretAssertVectorIndex(m_values, arrayIndex); - return m_values[arrayIndex]; + ScenePathName* spm = m_values[arrayIndex]; + if (spm != NULL) { + spm->m_restoredFlag = true; + } + return spm; } diff --git a/src/Scenes/ScenePathNameArray.h b/src/Scenes/ScenePathNameArray.h index 8f7f5d192708b8675aa1305a31e5ad32af363cda..dcc6d16ac193b9ee620e6c5548013c0cc1c9592d 100644 --- a/src/Scenes/ScenePathNameArray.h +++ b/src/Scenes/ScenePathNameArray.h @@ -51,6 +51,8 @@ namespace caret { ScenePathName* getScenePathNameAtIndex(const int32_t arrayIndex) const; + virtual std::vector getDescendants() const; + private: ScenePathNameArray& operator=(const ScenePathNameArray&); diff --git a/src/Scenes/ScenePrimitive.cxx b/src/Scenes/ScenePrimitive.cxx index d48fb376f49053d3d897237560df3205ebebe926..70b707b98f87664e0831c9c84cfba536391f392c 100644 --- a/src/Scenes/ScenePrimitive.cxx +++ b/src/Scenes/ScenePrimitive.cxx @@ -60,3 +60,17 @@ ScenePrimitive::~ScenePrimitive() } +/** + * Get a description of this object's content. + * @return String describing this object's content. + */ +AString +ScenePrimitive::toString() const +{ + QString objectInfo(SceneObject::toString() + + " value=" + + stringValue()); + return objectInfo; +} + + diff --git a/src/Scenes/ScenePrimitive.h b/src/Scenes/ScenePrimitive.h index 24be2be816c3f64e6230ddbe372a95e2cdc8dbe7..e64c158dfc70e4fd22ccfd97645902da1a53bb3a 100644 --- a/src/Scenes/ScenePrimitive.h +++ b/src/Scenes/ScenePrimitive.h @@ -40,6 +40,8 @@ namespace caret { ScenePrimitive& operator=(const ScenePrimitive&); + virtual AString toString() const; + public: // ADD_NEW_METHODS_HERE diff --git a/src/Scenes/ScenePrimitiveArray.cxx b/src/Scenes/ScenePrimitiveArray.cxx index 907df290d57af771e67eb9eb60eb33a5135be017..c8959fb0a63ba92e3d6c54ddf52e26f9de81a865 100644 --- a/src/Scenes/ScenePrimitiveArray.cxx +++ b/src/Scenes/ScenePrimitiveArray.cxx @@ -115,6 +115,23 @@ ScenePrimitiveArray::booleanValues(std::vector& valuesOut, } } +/** + * Load the vector with the boolean values from the scene. + * @param valuesOut + * Vector into which values are loaded. The size of the vector + * will be resized to the number of elements from the scene array. + */ +void +ScenePrimitiveArray::booleanVectorValues(std::vector& valuesOut) const +{ + const int32_t numElem = getNumberOfArrayElements(); + valuesOut.resize(numElem); + + for (int32_t i = 0; i < numElem; i++) { + valuesOut[i] = booleanValue(i); + } +} + /** * Load the array with float values. * @param valuesOut @@ -169,6 +186,24 @@ ScenePrimitiveArray::floatValues(std::vector& valuesOut, } } +/** + * Load the vector with the float values from the scene. + * @param valuesOut + * Vector into which values are loaded. The size of the vector + * will be resized to the number of elements from the scene array. + */ +void +ScenePrimitiveArray::floatVectorValues(std::vector& valuesOut) const +{ + const int32_t numElem = getNumberOfArrayElements(); + valuesOut.resize(numElem); + + for (int32_t i = 0; i < numElem; i++) { + valuesOut[i] = floatValue(i); + } +} + + /** * Load the array with integer values. * @param valuesOut @@ -223,6 +258,23 @@ ScenePrimitiveArray::integerValues(std::vector& valuesOut, } } +/** + * Load the vector with the integer values from the scene. + * @param valuesOut + * Vector into which values are loaded. The size of the vector + * will be resized to the number of elements from the scene array. + */ +void +ScenePrimitiveArray::integerVectorValues(std::vector& valuesOut) const +{ + const int32_t numElem = getNumberOfArrayElements(); + valuesOut.resize(numElem); + + for (int32_t i = 0; i < numElem; i++) { + valuesOut[i] = integerValue(i); + } +} + /** * Load the array with string values. * @param valuesOut @@ -277,6 +329,23 @@ ScenePrimitiveArray::stringValues(std::vector& valuesOut, } } +/** + * Load the vector with the string values from the scene. + * @param valuesOut + * Vector into which values are loaded. The size of the vector + * will be resized to the number of elements from the scene array. + */ +void +ScenePrimitiveArray::stringVectorValues(std::vector& valuesOut) const +{ + const int32_t numElem = getNumberOfArrayElements(); + valuesOut.resize(numElem); + + for (int32_t i = 0; i < numElem; i++) { + valuesOut[i] = stringValue(i); + } +} + /** * Load the array with unsigned byte values. * @param valuesOut @@ -331,6 +400,23 @@ ScenePrimitiveArray::unsignedByteValues(std::vector& valuesOut, } } +/** + * Load the vector with the unsigned byte values from the scene. + * @param valuesOut + * Vector into which values are loaded. The size of the vector + * will be resized to the number of elements from the scene array. + */ +void +ScenePrimitiveArray::unsignedByteVectorValues(std::vector& valuesOut) const +{ + const int32_t numElem = getNumberOfArrayElements(); + valuesOut.resize(numElem); + + for (int32_t i = 0; i < numElem; i++) { + valuesOut[i] = unsignedByteValue(i); + } +} + diff --git a/src/Scenes/ScenePrimitiveArray.h b/src/Scenes/ScenePrimitiveArray.h index 68df88838cb5d1989e77cd3953205a045133e71f..2424815efd4140f893edd2569ba5d5b72dca4970 100644 --- a/src/Scenes/ScenePrimitiveArray.h +++ b/src/Scenes/ScenePrimitiveArray.h @@ -91,6 +91,8 @@ namespace caret { virtual void booleanValues(std::vector& valuesOut, const bool defaultValue) const; + virtual void booleanVectorValues(std::vector& valuesOut) const; + virtual void floatValues(float valuesOut[], const int32_t arrayNumberOfElements, const float defaultValue) const; @@ -98,6 +100,8 @@ namespace caret { virtual void floatValues(std::vector& valuesOut, const float defaultValue) const; + virtual void floatVectorValues(std::vector& valuesOut) const; + virtual void integerValues(int32_t valuesOut[], const int32_t arrayNumberOfElements, const int32_t defaultValue) const; @@ -105,6 +109,8 @@ namespace caret { virtual void integerValues(std::vector& valuesOut, const int32_t defaultValue) const; + virtual void integerVectorValues(std::vector& valuesOut) const; + virtual void stringValues(AString valuesOut[], const int32_t arrayNumberOfElements, const AString& defaultValue) const; @@ -112,6 +118,8 @@ namespace caret { virtual void stringValues(std::vector& valuesOut, const AString& defaultValue) const; + virtual void stringVectorValues(std::vector& valuesOut) const; + virtual void unsignedByteValues(uint8_t valuesOut[], const int32_t arrayNumberOfElements, const uint8_t defaultValue) const; @@ -119,6 +127,8 @@ namespace caret { virtual void unsignedByteValues(std::vector& valuesOut, const uint8_t defaultValue) const; + virtual void unsignedByteVectorValues(std::vector& valuesOut) const; + private: diff --git a/src/Scenes/SceneString.cxx b/src/Scenes/SceneString.cxx index 402526bf2b2c92a64170e02c27d3cfc67bf6308f..c16f9898fc53d58a792a67b730b98854862d2e76 100644 --- a/src/Scenes/SceneString.cxx +++ b/src/Scenes/SceneString.cxx @@ -88,6 +88,7 @@ SceneString::setValue(const AString& value) bool SceneString::booleanValue() const { + m_restoredFlag = true; const bool b = m_value.toBool(); return b; } @@ -100,6 +101,7 @@ SceneString::booleanValue() const float SceneString::floatValue() const { + m_restoredFlag = true; bool isValid = false; float f = m_value.toFloat(&isValid); if (isValid == false) { @@ -116,6 +118,7 @@ SceneString::floatValue() const int32_t SceneString::integerValue() const { + m_restoredFlag = true; bool isValid = false; int32_t i = m_value.toInt(&isValid); if (isValid == false) { @@ -130,6 +133,7 @@ SceneString::integerValue() const AString SceneString::stringValue() const { + m_restoredFlag = true; return m_value; } @@ -142,19 +146,23 @@ SceneString::stringValue() const uint8_t SceneString::unsignedByteValue() const { + m_restoredFlag = true; bool isValid = false; const uint32_t i = m_value.toUInt(&isValid); if ( ! isValid) { return 0; } + /* + * Note since "i" is unsigned there is no need + * to compare with std::numeric_limits::min() + * since it is zero. + */ if (i > std::numeric_limits::max()) { return std::numeric_limits::max(); } - else if (i < std::numeric_limits::min()) { - return std::numeric_limits::min(); - } const uint8_t b = static_cast(i); return b; } + diff --git a/src/Scenes/SceneStringArray.cxx b/src/Scenes/SceneStringArray.cxx index 2f0eabcf3b3c8695e09a3dbc6814cb01aa3732e8..bdab564ad06e60ed8dcf7d01c79c8e155de1e767 100644 --- a/src/Scenes/SceneStringArray.cxx +++ b/src/Scenes/SceneStringArray.cxx @@ -135,6 +135,7 @@ SceneStringArray::setValue(const int32_t arrayIndex, bool SceneStringArray::booleanValue(const int32_t arrayIndex) const { + m_restoredFlag = true; CaretAssertVectorIndex(m_values, arrayIndex); const bool b = m_values[arrayIndex].toBool(); return b; @@ -149,6 +150,7 @@ SceneStringArray::booleanValue(const int32_t arrayIndex) const float SceneStringArray::floatValue(const int32_t arrayIndex) const { + m_restoredFlag = true; CaretAssertVectorIndex(m_values, arrayIndex); bool isValid = false; float f = m_values[arrayIndex].toFloat(&isValid); @@ -167,6 +169,7 @@ SceneStringArray::floatValue(const int32_t arrayIndex) const int32_t SceneStringArray::integerValue(const int32_t arrayIndex) const { + m_restoredFlag = true; CaretAssertVectorIndex(m_values, arrayIndex); bool isValid = false; int32_t i = m_values[arrayIndex].toInt(&isValid); @@ -185,6 +188,7 @@ SceneStringArray::integerValue(const int32_t arrayIndex) const AString SceneStringArray::stringValue(const int32_t arrayIndex) const { + m_restoredFlag = true; CaretAssertVectorIndex(m_values, arrayIndex); return m_values[arrayIndex]; } @@ -198,18 +202,21 @@ SceneStringArray::stringValue(const int32_t arrayIndex) const uint8_t SceneStringArray::unsignedByteValue(const int32_t arrayIndex) const { + m_restoredFlag = true; bool isValid = false; const uint32_t i = m_values[arrayIndex].toUInt(&isValid); if ( ! isValid) { return 0; } + /* + * Note since "i" is unsigned there is no need + * to compare with std::numeric_limits::min() + * since it is zero. + */ if (i > std::numeric_limits::max()) { return std::numeric_limits::max(); } - else if (i < std::numeric_limits::min()) { - return std::numeric_limits::min(); - } const uint8_t b = static_cast(i); return b; diff --git a/src/Scenes/SceneUnsignedByte.cxx b/src/Scenes/SceneUnsignedByte.cxx index 6dfab6b481a5a17a909d7c30b90c98462b1b306e..182d14f8344d389221e1f9d4fffaebd47932dada 100644 --- a/src/Scenes/SceneUnsignedByte.cxx +++ b/src/Scenes/SceneUnsignedByte.cxx @@ -88,6 +88,7 @@ SceneUnsignedByte::setValue(const uint8_t value) bool SceneUnsignedByte::booleanValue() const { + m_restoredFlag = true; const bool b = ((m_value != 0) ? true : false); return b; } @@ -98,6 +99,7 @@ SceneUnsignedByte::booleanValue() const float SceneUnsignedByte::floatValue() const { + m_restoredFlag = true; const float f = static_cast(m_value); return f; } @@ -108,6 +110,7 @@ SceneUnsignedByte::floatValue() const int32_t SceneUnsignedByte::integerValue() const { + m_restoredFlag = true; return m_value; } @@ -117,6 +120,7 @@ SceneUnsignedByte::integerValue() const AString SceneUnsignedByte::stringValue() const { + m_restoredFlag = true; const AString s = AString::number(m_value); return s; } @@ -127,5 +131,6 @@ SceneUnsignedByte::stringValue() const uint8_t SceneUnsignedByte::unsignedByteValue() const { + m_restoredFlag = true; return m_value; } diff --git a/src/Scenes/SceneXmlElements.h b/src/Scenes/SceneXmlElements.h index 69326f92867d5557f40fd0dc1fd479526e315bca..5ea4d3423ebb470e10e7798c7a5610ace2d8c173 100644 --- a/src/Scenes/SceneXmlElements.h +++ b/src/Scenes/SceneXmlElements.h @@ -155,12 +155,27 @@ namespace caret { * XML Tag for BALSA Study ID element. */ static const AString SCENE_INFO_BALSA_STUDY_ID_TAG = "BalsaStudyID"; - + + /** + * XML Tag for BALSA Study ID element. + */ + static const AString SCENE_INFO_BALSA_STUDY_TITLE_TAG = "BalsaStudyTitle"; /** * XML Tag for Base Directory ID element. */ - static const AString SCENE_INFO_BASE_DIRECTORY_TAG = "BaseDirectory"; + static const AString SCENE_INFO_BALSA_BASE_DIRECTORY_TAG = "BalsaBaseDirectory"; + static const AString SCENE_INFO_BALSA_BASE_DIRECTORY_TAG_OLD = "BaseDirectory"; + + /** + * XML Tag for Extract Directory ID element. + */ + static const AString SCENE_INFO_BALSA_EXTRACT_TO_DIRECTORY_TAG = "BalsaExtractToDirectory"; + + /** + * XML Tag for Base Path Type + */ + static const AString SCENE_INFO_BASE_PATH_TYPE = "BasePathType"; } // namespace SceneXmlElements diff --git a/src/Tests/CMakeLists.txt b/src/Tests/CMakeLists.txt index 6815a8f44eb1e6d55db14a323dd58179d083b9b5..38e051e5f1cc7dbd0e312eba6ca15dbb82526860 100644 --- a/src/Tests/CMakeLists.txt +++ b/src/Tests/CMakeLists.txt @@ -3,17 +3,18 @@ # PROJECT (Tests) -# -# Need XML from Qt -# -# needed QImage and it is from GUI: SET(QT_DONT_USE_QTGUI TRUE) -SET(QT_USE_QTXML TRUE) -SET(QT_USE_QTNETWORK TRUE) - # # Add QT for includes # -INCLUDE (${QT_USE_FILE}) +if(Qt5_FOUND) + include_directories(${Qt5Core_INCLUDE_DIRS}) + include_directories(${Qt5Network_INCLUDE_DIRS}) +endif() +IF (QT4_FOUND) + SET(QT_USE_QTXML TRUE) + SET(QT_USE_QTNETWORK TRUE) + INCLUDE(${QT_USE_FILE}) +ENDIF () # #The individual tests @@ -58,6 +59,7 @@ VolumeFileTest.cxx XnatTest.cxx ) +TARGET_LINK_LIBRARIES(Tests ${CARET_QT5_LINK}) # # Create the test1 executable @@ -78,6 +80,19 @@ ELSE (APPLE) ) ENDIF (APPLE) +if(Qt5_FOUND) + set(QT5_LINK_LIBS + Qt5::Concurrent + Qt5::Core + Qt5::Gui + Qt5::Network +# Qt5::OpenGL +# Qt5::PrintSupport + Qt5::Test +# Qt5::Widgets + Qt5::Xml) +endif() + # # Libraries that are linked # @@ -90,6 +105,7 @@ GuiQt Brain Files Annotations +Graphics Cifti Gifti Nifti @@ -99,13 +115,19 @@ Palette Scenes Xml Common +${QT5_LINK_LIBS} ${QT_LIBRARIES} +${GLEW_LIBRARIES} +${OSMESA_OFFSCREEN_LIBRARY} +${OSMESA_GL_LIBRARY} +${OSMESA_GLU_LIBRARY} ${ZLIB_LIBRARIES} #${LIBS} ) IF(WIN32) TARGET_LINK_LIBRARIES(test_driver + ${GLEW_LIBRARIES} opengl32 glu32 ) diff --git a/src/Tests/DotTest.cxx b/src/Tests/DotTest.cxx index ff3f8f80614675566ce381a011391c36f18da14b..024c7c1ddf5fb9a4085daba7301ffc6ca566b865 100644 --- a/src/Tests/DotTest.cxx +++ b/src/Tests/DotTest.cxx @@ -100,7 +100,7 @@ namespace } float stdev1 = sqrt(accum1); float stdev2 = sqrt(accum2); - double dotval = sddot(vec1.data(), vec2.data(), length);//function under test + double dotval = dsdot(vec1.data(), vec2.data(), length);//function under test return dotval / (stdev1 * stdev2); } @@ -156,7 +156,7 @@ void DotTest::execute() } else { cout << "skipping AVX, not supported" << endl; } - //avx + //avx+fma impl_in_use = dot_set_impl(DOT_AVXFMA); if (impl_in_use == DOT_AVXFMA) { @@ -169,4 +169,30 @@ void DotTest::execute() } else { cout << "skipping AVXFMA, not supported" << endl; } + //avx512 + impl_in_use = dot_set_impl(DOT_AVX512); + if (impl_in_use == DOT_AVX512) + { + checkVal(self_naive, correlate(rand1, rand1), "avx512 self-correlation"); + checkVal(unrelated_naive, correlate(rand1, rand2), "avx512 unrelated correlation"); + checkVal(lowsnr_naive, correlate(lowsnrA, lowsnrB), "avx512 low snr correlation"); + checkVal(midsnr_naive, correlate(midsnrA, midsnrB), "avx512 mid snr correlation"); + checkVal(highsnr_naive, correlate(highsnrA, highsnrB), "avx512 high snr correlation"); + checkVal(cross_snr_naive, correlate(lowsnrA, highsnrB), "avx512 cross snr correlation"); + } else { + cout << "skipping AVX512, not supported" << endl; + } + //avx512+fma + impl_in_use = dot_set_impl(DOT_AVX512FMA); + if (impl_in_use == DOT_AVX512FMA) + { + checkVal(self_naive, correlate(rand1, rand1), "avx512fma self-correlation"); + checkVal(unrelated_naive, correlate(rand1, rand2), "avx512fma unrelated correlation"); + checkVal(lowsnr_naive, correlate(lowsnrA, lowsnrB), "avx512fma low snr correlation"); + checkVal(midsnr_naive, correlate(midsnrA, midsnrB), "avx512fma mid snr correlation"); + checkVal(highsnr_naive, correlate(highsnrA, highsnrB), "avx512fma high snr correlation"); + checkVal(cross_snr_naive, correlate(lowsnrA, highsnrB), "avx512fma cross snr correlation"); + } else { + cout << "skipping AVX512FMA, not supported" << endl; + } } diff --git a/src/Tests/test_driver.cxx b/src/Tests/test_driver.cxx index 8660cf91fcd09845d9b854c3095d55c79789a775..8fb4eb0f73915f39a535dcf929038e0ea9532310 100644 --- a/src/Tests/test_driver.cxx +++ b/src/Tests/test_driver.cxx @@ -20,7 +20,7 @@ //program for running tests -#include +//#include #include #include #include diff --git a/src/Xml/CMakeLists.txt b/src/Xml/CMakeLists.txt index 325f413c4c92812763632b09ae42f499d502d8f9..3b5f09631055c2078e61dae2d02bdeefb700f302 100644 --- a/src/Xml/CMakeLists.txt +++ b/src/Xml/CMakeLists.txt @@ -3,38 +3,43 @@ # PROJECT (Xml) -# -# Use XML from Qt but not GUI. -# -SET(QT_USE_QTXML TRUE) -SET(QT_DONT_USE_QTGUI TRUE) -SET(QT_USE_QTNETWORK TRUE) - # # QT include files # -INCLUDE(${QT_USE_FILE}) +if(Qt5_FOUND) + include_directories(${Qt5Core_INCLUDE_DIRS}) + include_directories(${Qt5Network_INCLUDE_DIRS}) + include_directories(${Qt5Xml_INCLUDE_DIRS}) +endif() +IF (QT4_FOUND) + SET(QT_USE_QTXML TRUE) + SET(QT_DONT_USE_QTGUI TRUE) + SET(QT_USE_QTNETWORK TRUE) + INCLUDE(${QT_USE_FILE}) +ENDIF () # # Create a Library # ADD_LIBRARY(Xml -XmlWriter.h -XmlException.h XmlAttributes.h +XmlException.h XmlSaxParser.h XmlSaxParserException.h XmlSaxParserHandlerInterface.h XmlSaxParserWithQt.h +XmlUnexpectedElementSaxParser.h XmlUtilities.h +XmlWriter.h -XmlWriter.cxx -XmlException.cxx XmlAttributes.cxx +XmlException.cxx XmlSaxParser.cxx XmlSaxParserException.cxx XmlSaxParserWithQt.cxx +XmlUnexpectedElementSaxParser.cxx XmlUtilities.cxx +XmlWriter.cxx ) # @@ -45,3 +50,5 @@ ${CMAKE_SOURCE_DIR}/Xml ${CMAKE_SOURCE_DIR}/Common ) +TARGET_LINK_LIBRARIES(Xml ${CARET_QT5_LINK}) + diff --git a/src/Xml/XmlUnexpectedElementSaxParser.cxx b/src/Xml/XmlUnexpectedElementSaxParser.cxx new file mode 100644 index 0000000000000000000000000000000000000000..6b12837d511ac88de8bd99d12c6473549d380728 --- /dev/null +++ b/src/Xml/XmlUnexpectedElementSaxParser.cxx @@ -0,0 +1,172 @@ + +/*LICENSE_START*/ +/* + * Copyright (C) 2017 Washington University School of Medicine + * + * 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 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ +/*LICENSE_END*/ + +#define __XML_UNEXPECTED_ELEMENT_SAX_PARSER_DECLARE__ +#include "XmlUnexpectedElementSaxParser.h" +#undef __XML_UNEXPECTED_ELEMENT_SAX_PARSER_DECLARE__ + +#include +#include + +#include "CaretAssert.h" +using namespace caret; + + + +/** + * \class caret::XmlUnexpectedElementSaxParser + * \brief Assists with parsing unexpected elements in XML. + * \ingroup Xml + * + * Assists with processing unexpected XML elements that may actually be new elements + * or elements that may have been removed. It allows the XML parser to skip these + * new elements so that reading of XML does not prematurely terminate. It is able + * to handle nested child elements with the same name. It concludes processing when + * an element name that matches that passed to the first startElement() method and + * all children have been processed. + * + * When an unexpected element is encountered: + * (1) Create an instance of this class and call the startElement() method; + * (2) Call startElement(), characters() and endElement() methods of this class + * until endElement() returns a 'Done' status. + * (3) Call getUnexpectedContentXML() method to get/print the unexpected XML. + * (4) Destroy the instance of this class. + */ + +/** + * Constructor. + */ +XmlUnexpectedElementSaxParser::XmlUnexpectedElementSaxParser() +: CaretObject() +{ + m_textStreamString.reserve(1000); + m_textStream = std::unique_ptr(new QTextStream(&m_textStreamString, + QIODevice::WriteOnly)); + m_xmlWriter = std::unique_ptr(new XmlWriter(*m_textStream)); +} + +/** + * Destructor. + */ +XmlUnexpectedElementSaxParser::~XmlUnexpectedElementSaxParser() +{ +} + +/* + * Process characters. + * + * @param ch The characters from the XML document. + */ +void +XmlUnexpectedElementSaxParser::characters(const char* ch) +{ + if (AString(ch).trimmed().isEmpty()) { + /* all white space */ + return; + } + + if (m_hadElementCharacters) { + m_xmlWriter->writeCharacters(AString(ch)); + } + else { + m_xmlWriter->writeCharactersWithIndent(AString(ch)); + } + + m_hadElementCharacters = true; +} + +/** + * Process an end element. + * + * @param uri + * the Namespace URI, or the empty string if the element + * has no Namespace URI or if Namespace processing is not + * being performed + * @param localName + * the local name (without prefix), or the empty string if + * Namespace processing is not being performed + * @param qName + * the qualified name (with prefix), or the empty string + * if qualified names are not available + * @return + * The return code indicating status of element processing. + */ +XmlUnexpectedElementSaxParser::ReturnCodeEnum +XmlUnexpectedElementSaxParser::endElement(const AString& /*uri*/, const AString& localName, const AString& /*qName*/) +{ + if (m_elementNameStack.empty()) { + return ReturnCodeEnum::ERROR; + } + + if (localName == m_elementNameStack.top()) { + m_elementNameStack.pop(); + + if (m_hadElementCharacters) { + m_xmlWriter->writeCharacters("\n"); + m_hadElementCharacters = false; + } + + m_xmlWriter->writeEndElement(); + + if (m_elementNameStack.empty()) { + return ReturnCodeEnum::DONE; + } + + return ReturnCodeEnum::NOT_DONE; + } + + return ReturnCodeEnum::ERROR; +} + +/** + * Process a start element. + * + * @param uri + * the Namespace URI, or the empty string if the element + * has no Namespace URI or if Namespace processing is not + * being performed + * @param localName + * the local name (without prefix), or the empty string if + * Namespace processing is not being performed + * @param qName + * the qualified name (with prefix), or the empty string + * if qualified names are not available + * @param atts + * the attributes attached to the element. If there are + * no attributes, it shall be an empty Attributes object. + */ +void +XmlUnexpectedElementSaxParser::startElement(const AString& /*uri*/, const AString& localName, const AString& /*qName*/, const XmlAttributes& atts) +{ + m_elementNameStack.push(localName); + m_xmlWriter->writeStartElement(localName, atts); +} + +/** + * @return The unexpected content XML. + */ +AString +XmlUnexpectedElementSaxParser::getUnexpectedContentXML() const +{ + return m_textStreamString; +} + + diff --git a/src/Xml/XmlUnexpectedElementSaxParser.h b/src/Xml/XmlUnexpectedElementSaxParser.h new file mode 100644 index 0000000000000000000000000000000000000000..4220a404d18b42294f8700c182ac7a1374e22911 --- /dev/null +++ b/src/Xml/XmlUnexpectedElementSaxParser.h @@ -0,0 +1,94 @@ +#ifndef __XML_UNEXPECTED_ELEMENT_SAX_PARSER_H__ +#define __XML_UNEXPECTED_ELEMENT_SAX_PARSER_H__ + +/*LICENSE_START*/ +/* + * Copyright (C) 2017 Washington University School of Medicine + * + * 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 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ +/*LICENSE_END*/ + + +#include +#include + +#include "CaretObject.h" + +class QTextStream; + +namespace caret { + class XmlAttributes; + class XmlWriter; + + class XmlUnexpectedElementSaxParser : public CaretObject { + + public: + /** + * Return code from end element method. + */ + enum class ReturnCodeEnum { + /** Done processing unrecognized XML */ + DONE, + /** Error encountered (unbalanced elements, unlikely since the SAX parser should catch this condition */ + ERROR, + /** Still processing unrecognized XML */ + NOT_DONE + }; + + XmlUnexpectedElementSaxParser(); + + virtual ~XmlUnexpectedElementSaxParser(); + + void startElement(const AString& uri, + const AString& localName, + const AString& qName, + const XmlAttributes& atts); + + ReturnCodeEnum endElement(const AString& namespaceURI, + const AString& localName, + const AString& qName); + + void characters(const char* ch); + + AString getUnexpectedContentXML() const; + + // ADD_NEW_METHODS_HERE + + private: + XmlUnexpectedElementSaxParser(const XmlUnexpectedElementSaxParser&); + + XmlUnexpectedElementSaxParser& operator=(const XmlUnexpectedElementSaxParser&); + + std::stack m_elementNameStack; + + AString m_textStreamString; + + std::unique_ptr m_textStream; + + std::unique_ptr m_xmlWriter; + + bool m_hadElementCharacters = false; + + // ADD_NEW_MEMBERS_HERE + + }; + +#ifdef __XML_UNEXPECTED_ELEMENT_SAX_PARSER_DECLARE__ + // +#endif // __XML_UNEXPECTED_ELEMENT_SAX_PARSER_DECLARE__ + +} // namespace +#endif //__XML_UNEXPECTED_ELEMENT_SAX_PARSER_H__ diff --git a/src/Xml/XmlWriter.cxx b/src/Xml/XmlWriter.cxx index 04bb76eadf1c97a63dbc81cd0921e5429b39f248..6a71a4fc64abfea0c1454b5a4288bed94de73aaa 100644 --- a/src/Xml/XmlWriter.cxx +++ b/src/Xml/XmlWriter.cxx @@ -320,13 +320,18 @@ XmlWriter::writeStartElement(const AString& localName) { void XmlWriter::writeStartElement(const AString& localName, const XmlAttributes& attributes) { + int32_t numAtts = attributes.getNumberOfAttributes(); + if (numAtts <= 0) { + writeStartElement(localName); + return; + } + this->writeIndentation(); this->writeTextToOutputStream("<" + localName + " "); int32_t attIndentSpaces = localName.length() + 2; AString attIndentString(attIndentSpaces, ' '); - int32_t numAtts = attributes.getNumberOfAttributes(); for (int32_t i = 0; i < numAtts; i++) { if (i > 0) { this->writeIndentation(); diff --git a/src/debian/copyright b/src/debian/copyright index 34bb7910d69e638756ba013e213a09c9992b0731..0b135a55e8dd13c926d6db00a758ae0d6066cff6 100644 --- a/src/debian/copyright +++ b/src/debian/copyright @@ -3,11 +3,11 @@ Upstream-Name: Connectome Workbench Source: http://www.humanconnectome.org/software/get-connectome-workbench.html Files: * -Copyright: 2014-2016 Washington University School of Medicine +Copyright: 2014-2018 Washington University School of Medicine License: GPL-2+ Files: src/Files/SurfaceResamplingHelper.cxx src/Cifti/examples/* src/CommandLine/wb_shortcuts src/CommandLine/wb_command_completion -Copyright: 2014-2015 Washington University School of Medicine +Copyright: 2014-2017 Washington University School of Medicine License: Expat Files: src/kloewe/* @@ -51,14 +51,18 @@ Copyright: 2014 Washington University School of Medicine 1970-2003, Wm. Randolph Franklin License: GPL-2+ and PNPOLY-BSD-3-Clause -Files: src/Resources/FtglFonts/Vera*.ttf +Files: src/Resources/General/Fonts/FtglFonts/Vera*.ttf src/Resources/General/Fonts/VeraFonts/Vera*.ttf Copyright: 2003 Bitstream, Inc. License: Bitstream-Vera -Files: src/Resources/Fonts/LinuxLibertine/*ttf +Files: src/Resources/General/Fonts/LinuxLibertine/*.ttf Copyright: 2003-2009 Philipp H. Poll License: GPL-2+ with Font exception and OFL-1.1 +Files: src/Files/PaletteFile.cxx +Copyright: 2002 Cynthia Brewer, Mark Harrower, and The Pennsylvania State University + 2012-2015 Matplotlib Development Team +License: Apache-2 and MDT License: GPL-2+ This program is free software; you can redistribute it @@ -251,6 +255,8 @@ License: Expat SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. License: Bitstream-Vera + Bitstream Vera is a trademark of Bitstream, Inc. + . Permission is hereby granted, free of charge, to any person obtaining a copy of the fonts accompanying this license ("Fonts") and associated documentation files (the "Font Software"), to reproduce and distribute @@ -302,7 +308,6 @@ License: Qt-commercial conditions see http://qt.digia.com/licensing. For further information use the contact form at http://qt.digia.com/contact-us. - License: GPL-2+ with Font exception The linux-libertine fonts are dual-licensed under the GNU General Public License (GPL) with font exception, and the SIL Open Font License (OFL). @@ -428,3 +433,119 @@ License: OFL-1.1 DAMAGES, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF THE USE OR INABILITY TO USE THE FONT SOFTWARE OR FROM OTHER DEALINGS IN THE FONT SOFTWARE. + +License: MDT + License agreement for matplotlib versions 1.3.0 and later + ========================================================= + . + 1. This LICENSE AGREEMENT is between the Matplotlib Development Team + ("MDT"), and the Individual or Organization ("Licensee") accessing and + otherwise using matplotlib software in source or binary form and its + associated documentation. + . + 2. Subject to the terms and conditions of this License Agreement, MDT + hereby grants Licensee a nonexclusive, royalty-free, world-wide license + to reproduce, analyze, test, perform and/or display publicly, prepare + derivative works, distribute, and otherwise use matplotlib + alone or in any derivative version, provided, however, that MDT's + License Agreement and MDT's notice of copyright, i.e., "Copyright (c) + 2012- Matplotlib Development Team; All Rights Reserved" are retained in + matplotlib alone or in any derivative version prepared by + Licensee. + . + 3. In the event Licensee prepares a derivative work that is based on or + incorporates matplotlib or any part thereof, and wants to + make the derivative work available to others as provided herein, then + Licensee hereby agrees to include in any such work a brief summary of + the changes made to matplotlib . + . + 4. MDT is making matplotlib available to Licensee on an "AS + IS" basis. MDT MAKES NO REPRESENTATIONS OR WARRANTIES, EXPRESS OR + IMPLIED. BY WAY OF EXAMPLE, BUT NOT LIMITATION, MDT MAKES NO AND + DISCLAIMS ANY REPRESENTATION OR WARRANTY OF MERCHANTABILITY OR FITNESS + FOR ANY PARTICULAR PURPOSE OR THAT THE USE OF MATPLOTLIB + WILL NOT INFRINGE ANY THIRD PARTY RIGHTS. + . + 5. MDT SHALL NOT BE LIABLE TO LICENSEE OR ANY OTHER USERS OF MATPLOTLIB + FOR ANY INCIDENTAL, SPECIAL, OR CONSEQUENTIAL DAMAGES OR + LOSS AS A RESULT OF MODIFYING, DISTRIBUTING, OR OTHERWISE USING + MATPLOTLIB , OR ANY DERIVATIVE THEREOF, EVEN IF ADVISED OF + THE POSSIBILITY THEREOF. + . + 6. This License Agreement will automatically terminate upon a material + breach of its terms and conditions. + . + 7. Nothing in this License Agreement shall be deemed to create any + relationship of agency, partnership, or joint venture between MDT and + Licensee. This License Agreement does not grant permission to use MDT + trademarks or trade name in a trademark sense to endorse or promote + products or services of Licensee, or any third party. + . + 8. By copying, installing or otherwise using matplotlib , + Licensee agrees to be bound by the terms and conditions of this License + Agreement. + . + License agreement for matplotlib versions prior to 1.3.0 + ======================================================== + . + 1. This LICENSE AGREEMENT is between John D. Hunter ("JDH"), and the + Individual or Organization ("Licensee") accessing and otherwise using + matplotlib software in source or binary form and its associated + documentation. + . + 2. Subject to the terms and conditions of this License Agreement, JDH + hereby grants Licensee a nonexclusive, royalty-free, world-wide license + to reproduce, analyze, test, perform and/or display publicly, prepare + derivative works, distribute, and otherwise use matplotlib + alone or in any derivative version, provided, however, that JDH's + License Agreement and JDH's notice of copyright, i.e., "Copyright (c) + 2002-2011 John D. Hunter; All Rights Reserved" are retained in + matplotlib alone or in any derivative version prepared by + Licensee. + . + 3. In the event Licensee prepares a derivative work that is based on or + incorporates matplotlib or any part thereof, and wants to + make the derivative work available to others as provided herein, then + Licensee hereby agrees to include in any such work a brief summary of + the changes made to matplotlib. + . + 4. JDH is making matplotlib available to Licensee on an "AS + IS" basis. JDH MAKES NO REPRESENTATIONS OR WARRANTIES, EXPRESS OR + IMPLIED. BY WAY OF EXAMPLE, BUT NOT LIMITATION, JDH MAKES NO AND + DISCLAIMS ANY REPRESENTATION OR WARRANTY OF MERCHANTABILITY OR FITNESS + FOR ANY PARTICULAR PURPOSE OR THAT THE USE OF MATPLOTLIB + WILL NOT INFRINGE ANY THIRD PARTY RIGHTS. + . + 5. JDH SHALL NOT BE LIABLE TO LICENSEE OR ANY OTHER USERS OF MATPLOTLIB + FOR ANY INCIDENTAL, SPECIAL, OR CONSEQUENTIAL DAMAGES OR + LOSS AS A RESULT OF MODIFYING, DISTRIBUTING, OR OTHERWISE USING + MATPLOTLIB , OR ANY DERIVATIVE THEREOF, EVEN IF ADVISED OF + THE POSSIBILITY THEREOF. + . + 6. This License Agreement will automatically terminate upon a material + breach of its terms and conditions. + . + 7. Nothing in this License Agreement shall be deemed to create any + relationship of agency, partnership, or joint venture between JDH and + Licensee. This License Agreement does not grant permission to use JDH + trademarks or trade name in a trademark sense to endorse or promote + products or services of Licensee, or any third party. + . + 8. By copying, installing or otherwise using matplotlib, + Licensee agrees to be bound by the terms and conditions of this License + Agreement. + +License: Apache-2 + Apache-Style Software License for ColorBrewer software and ColorBrewer Color Schemes + . + Copyright (c) 2002 Cynthia Brewer, Mark Harrower, and The Pennsylvania State University. + . + Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. + You may obtain a copy of the License at + . + http://www.apache.org/licenses/LICENSE-2.0 + . + Unless required by applicable law or agreed to in writing, software distributed + under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR + CONDITIONS OF ANY KIND, either express or implied. See the License for the + specific language governing permissions and limitations under the License. diff --git a/src/kloewe/cpuinfo/CMakeLists.txt b/src/kloewe/cpuinfo/CMakeLists.txt index e17c05ddc6de4310687745495adc1c6bcd1aefd7..96e8df669f8aad13a687d4f0d40c9b57079323b6 100644 --- a/src/kloewe/cpuinfo/CMakeLists.txt +++ b/src/kloewe/cpuinfo/CMakeLists.txt @@ -12,3 +12,6 @@ add_compile_options(-std=c99 -Wall -Wextra -Wno-unused-parameter -Wconversion -W add_definitions(-DNDEBUG) add_library(cpuinfo src/cpuinfo.c) + +TARGET_LINK_LIBRARIES(cpuinfo ${CARET_QT5_LINK}) + diff --git a/src/kloewe/cpuinfo/LICENSE b/src/kloewe/cpuinfo/LICENSE index a64f28f98097ac5d3671fd718dac058d81b041a9..fcaf12d7999f76b43a110979979f76393cb12db0 100644 --- a/src/kloewe/cpuinfo/LICENSE +++ b/src/kloewe/cpuinfo/LICENSE @@ -1,6 +1,6 @@ The MIT License (MIT) -Copyright (c) 2013-2016 Kristian Loewe, Christian Borgelt +Copyright (c) 2013-2017 Kristian Loewe, Christian Borgelt Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/src/kloewe/cpuinfo/src/cpuinfo.c b/src/kloewe/cpuinfo/src/cpuinfo.c index f106c06acf1a2fa14c1fe566691fd31aec8bf555..b02c97c4f771b074b8cd3ce05c8ab3a3f34d615e 100644 --- a/src/kloewe/cpuinfo/src/cpuinfo.c +++ b/src/kloewe/cpuinfo/src/cpuinfo.c @@ -28,7 +28,10 @@ #ifdef NDEBUG #define DBGMSG(...) ((void)0) #else -#define DBGMSG(...) fprintf(stderr, __VA_ARGS__) +#line __LINE__ "cpuinfo.c" +#define DBGMSG(...) do { fprintf(stderr, "%s:%d:%s()\n", \ + __FILE__, __LINE__, __func__); \ + fprintf(stderr, __VA_ARGS__); } while(0) #endif /*---------------------------------------------------------------------------- @@ -42,23 +45,30 @@ typedef struct { /* --- processor ids --- */ /*---------------------------------------------------------------------------- Global Variables ----------------------------------------------------------------------------*/ -static int cpuinfo[5]; /* cpu information */ +static int cpuinfo[4]; /* cpu information (cpuid) */ +static int peax = -1; /* previous eax */ +static int pecx = -1; /* previous ecx */ +static int nphys = 0; /* # processors/packages/sockets */ +static int ncores = 0; /* # processor cores */ +static int nprocs = 0; /* # logical processors */ /*---------------------------------------------------------------------------- Functions ----------------------------------------------------------------------------*/ #ifdef _WIN32 /* if Microsoft Windows system */ -#define cpuid __cpuid /* map existing function */ +#define cpuid __cpuidex /* map existing function */ #else /* if Linux/Unix system */ -static void cpuid (int32_t info[4], int32_t type) +static void cpuid (int32_t info[4], int32_t eax, int32_t ecx) { /* --- get CPU information */ __asm__ __volatile__ ("cpuid" : "=a" (info[0]), "=b" (info[1]), "=c" (info[2]), "=d" (info[3]) - : "a" (type), "c" (0)); // : "a" (type)); + : "a" (eax), "c" (ecx)); + peax = eax; + pecx = ecx; } /* cpuid() */ #endif /* #ifdef _WIN32 .. #else .. */ @@ -107,17 +117,25 @@ static int cmpids (const void *p, const void *q) /*--------------------------------------------------------------------------*/ -int corecnt (void) -{ /* --- number of processor cores */ +static int enumerate (void) +{ /* --- enumerate topology */ FILE *fp; /* file for /proc/cpuinfo */ PROCIDS *pids; /* processor ids (physical & core) */ int n; /* # log. processors found in cpuinfo */ int i, p, c, np, nc; /* loop variables */ - int nphys = 0; /* number of physical processors */ - int ncores = 0; /* number of physical cores */ - int nprocs = proccnt(); /* # log. processors (reference) */ - if (nprocs < 0) return -1; /* abort if nprocs can't be determined */ + /* determine number of logical processors (reference) */ + #ifdef _SC_NPROCESSORS_ONLN /* if glibc's sysconf is available */ + nprocs = (int)sysconf(_SC_NPROCESSORS_ONLN); + #else + nprocs = -1; + #endif + if (nprocs < 0) { /* abort if nprocs can't be determined */ + nphys = -1; + ncores = -1; + nprocs = -1; + return -1; + } /* collect physical id and core id of each logical processor */ pids = calloc((size_t)nprocs, sizeof(PROCIDS)); @@ -133,7 +151,13 @@ int corecnt (void) while (((c = fgetc(fp)) != EOF) && (c != '\n')); } fclose(fp); - if (n != nprocs) { free(pids); return -1; } + if (n != nprocs) { /* abort if the number of log. procs */ + nphys = -1; /* found differs from the reference */ + ncores = -1; + nprocs = -1; + free(pids); + return -1; + } /* sort the array of processor ids and determine the number of cores */ qsort(pids, (size_t)nprocs, sizeof(PROCIDS), cmpids); @@ -142,7 +166,7 @@ int corecnt (void) for (i = np = nc = 0; i < n; i++) { if (pids[i].phys == p) { np += 1; if (pids[i].core == c) { nc += 1; continue; } - DBGMSG("phys %d, core %d: %d logical processor(s)\n", p, c, nc); + DBGMSG("phys %d, core %2d: %d logical processor(s)\n", p, c, nc); c = pids[i].core; nc = 1; ncores += 1; continue; } @@ -157,42 +181,59 @@ int corecnt (void) DBGMSG("number of physical processors: %d\n", nphys); DBGMSG("number of cores: %d\n", ncores); free(pids); - if ((ncores != nprocs) && (ncores != nprocs/2)) - return -1; /* check result for consistency */ - return ncores; /* return the number of cores */ -} /* corecnt() */ + if (nprocs % ncores) { /* check results for consistency */ + nphys = -1; + ncores = -1; + nprocs = -1; + return -1; } + else + return 0; +} /* enumerate() */ -#elif defined _WIN32 /* if Microsoft Windows system */ +/*--------------------------------------------------------------------------*/ -int corecnt (void) -{ /* --- number of processor cores */ - return -1; /* not yet implemented for Windows */ -} /* corecnt() */ +int physcnt (void) +{ /* --- number of physical processors */ + if (!nphys) + if (enumerate()) { return -1; } + return nphys; +} /* physcnt() */ -#elif defined __APPLE__ /* if Apple Mac OS system */ +/*--------------------------------------------------------------------------*/ int corecnt (void) { /* --- number of processor cores */ - int ncores; - size_t len = sizeof(ncores); - if (sysctlbyname("hw.physicalcpu", &ncores, &len, NULL, (size_t)0)) - return -1; + if (!ncores) + if (enumerate()) { return -1; } return ncores; } /* corecnt() */ -#endif /* #ifdef __linux__ .. #elif def. _WIN32 .. #elif def. __APPLE__ .. */ /*--------------------------------------------------------------------------*/ -#ifdef __linux__ /* if Linux system */ -#ifdef _SC_NPROCESSORS_ONLN /* if glibc's sysconf is available */ int proccnt (void) { /* --- number of logical processors */ - return (int)sysconf(_SC_NPROCESSORS_ONLN); + if (!nprocs) + if (enumerate()) { return -1; } + return nprocs; } /* proccnt() */ -#endif /* #ifdef _SC_NPROCESSORS_ONLN */ +/*--------------------------------------------------------------------------*/ #elif defined _WIN32 /* if Microsoft Windows system */ +int physcnt (void) +{ /* --- number of physical processors */ + return -1; /* not yet implemented for Windows */ +} /* physcnt() */ + +/*--------------------------------------------------------------------------*/ + +int corecnt (void) +{ /* --- number of processor cores */ + return -1; /* not yet implemented for Windows */ +} /* corecnt() */ + +/*--------------------------------------------------------------------------*/ + int proccnt (void) { /* --- number of logical processors */ SYSTEM_INFO sysinfo; /* system information structure */ @@ -200,8 +241,31 @@ int proccnt (void) return sysinfo.dwNumberOfProcessors; } /* proccnt() */ +/*--------------------------------------------------------------------------*/ #elif defined __APPLE__ /* if Apple Mac OS system */ +int physcnt (void) +{ /* --- number of physical processors */ + int nphys; + size_t len = sizeof(nphys); + if (sysctlbyname("hw.packages", &nphys, &len, NULL, (size_t)0)) + return -1; + return nphys; +} /* physcnt() */ + +/*--------------------------------------------------------------------------*/ + +int corecnt (void) +{ /* --- number of processor cores */ + int ncores; + size_t len = sizeof(ncores); + if (sysctlbyname("hw.physicalcpu", &ncores, &len, NULL, (size_t)0)) + return -1; + return ncores; +} /* corecnt() */ + +/*--------------------------------------------------------------------------*/ + int proccnt (void) { /* --- number of logical processors */ int nproc; @@ -230,7 +294,10 @@ Additional info and references (proccnt, Linux version): int proccntmax (void) { /* --- max. number of logical processors * per physical processor */ - if (!cpuinfo[4]) { cpuid(cpuinfo, 1); cpuinfo[4] = -1; } + int eax = 1; + int ecx = 0; + if ((eax != peax) || (ecx != pecx)) + cpuid(cpuinfo, eax, ecx); return (cpuinfo[1] >> 16) & 0xff; /* EBX[23:16] */ } /* proccntmax() */ @@ -248,7 +315,10 @@ References (proccntmax): int hasMMX (void) { /* --- check for MMX instructions */ - if (!cpuinfo[4]) { cpuid(cpuinfo, 1); cpuinfo[4] = -1; } + int eax = 1; + int ecx = 0; + if ((eax != peax) || (ecx != pecx)) + cpuid(cpuinfo, eax, ecx); return (cpuinfo[3] & (1 << 23)) != 0; } /* hasMMX() */ @@ -256,85 +326,180 @@ int hasMMX (void) int hasSSE (void) { /* --- check for SSE instructions */ - if (!cpuinfo[4]) { cpuid(cpuinfo, 1); cpuinfo[4] = -1; } - return (cpuinfo[3] & (1 << 25)) != 0; + int eax = 1; + int ecx = 0; + if ((eax != peax) || (ecx != pecx)) + cpuid(cpuinfo, eax, ecx); + return (cpuinfo[3] & (1 << 25)) != 0; /* EDX 25 */ } /* hasSSE() */ /*--------------------------------------------------------------------------*/ int hasSSE2 (void) { /* --- check for SSE2 instructions */ - if (!cpuinfo[4]) { cpuid(cpuinfo, 1); cpuinfo[4] = -1; } - return (cpuinfo[3] & (1 << 26)) != 0; + int eax = 1; + int ecx = 0; + if ((eax != peax) || (ecx != pecx)) + cpuid(cpuinfo, eax, ecx); + return (cpuinfo[3] & (1 << 26)) != 0; /* EDX 26 */ } /* hasSSE2() */ /*--------------------------------------------------------------------------*/ int hasSSE3 (void) { /* --- check for SSE3 instructions */ - if (!cpuinfo[4]) { cpuid(cpuinfo, 1); cpuinfo[4] = -1; } - return (cpuinfo[2] & (1 << 0)) != 0; + int eax = 1; + int ecx = 0; + if ((eax != peax) || (ecx != pecx)) + cpuid(cpuinfo, eax, ecx); + return (cpuinfo[2] & (1 << 0)) != 0; /* ECX 0 */ } /* hasSSE3() */ /*--------------------------------------------------------------------------*/ int hasSSSE3 (void) { /* --- check for SSSE3 instructions */ - if (!cpuinfo[4]) { cpuid(cpuinfo, 1); cpuinfo[4] = -1; } - return (cpuinfo[2] & (1 << 9)) != 0; + int eax = 1; + int ecx = 0; + if ((eax != peax) || (ecx != pecx)) + cpuid(cpuinfo, eax, ecx); + return (cpuinfo[2] & (1 << 9)) != 0; /* ECX 9 */ } /* hasSSSE3() */ /*--------------------------------------------------------------------------*/ int hasSSE41 (void) { /* --- check for SSE4.1 instructions */ - if (!cpuinfo[4]) { cpuid(cpuinfo, 1); cpuinfo[4] = -1; } - return (cpuinfo[2] & (1 << 19)) != 0; + int eax = 1; + int ecx = 0; + if ((eax != peax) || (ecx != pecx)) + cpuid(cpuinfo, eax, ecx); + return (cpuinfo[2] & (1 << 19)) != 0; /* ECX 19 */ } /* hasSSE41() */ /*--------------------------------------------------------------------------*/ int hasSSE42 (void) { /* --- check for SSE4.2 instructions */ - if (!cpuinfo[4]) { cpuid(cpuinfo, 1); cpuinfo[4] = -1; } - return (cpuinfo[2] & (1 << 20)) != 0; + int eax = 1; + int ecx = 0; + if ((eax != peax) || (ecx != pecx)) + cpuid(cpuinfo, eax, ecx); + return (cpuinfo[2] & (1 << 20)) != 0; /* ECX 20 */ } /* hasSSE42() */ /*--------------------------------------------------------------------------*/ int hasPOPCNT (void) { /* --- check for popcnt instructions */ - if (!cpuinfo[4]) { cpuid(cpuinfo, 1); cpuinfo[4] = -1; } - return (cpuinfo[2] & (1 << 23)) != 0; + int eax = 1; + int ecx = 0; + if ((eax != peax) || (ecx != pecx)) + cpuid(cpuinfo, eax, ecx); + return (cpuinfo[2] & (1 << 23)) != 0; /* ECX 23 */ } /* hasPOPCNT() */ /*--------------------------------------------------------------------------*/ int hasAVX (void) { /* --- check for AVX instructions */ - if (!cpuinfo[4]) { cpuid(cpuinfo, 1); cpuinfo[4] = -1; } - return (cpuinfo[2] & (1 << 28)) != 0; + int eax = 1; + int ecx = 0; + if ((eax != peax) || (ecx != pecx)) + cpuid(cpuinfo, eax, ecx); + return (cpuinfo[2] & (1 << 28)) != 0; /* ECX 28 */ } /* hasAVX() */ /*--------------------------------------------------------------------------*/ +int hasAVX2 (void) +{ /* --- check for AVX2 instructions */ + int eax = 7; + int ecx = 0; + if ((eax != peax) || (ecx != pecx)) + cpuid(cpuinfo, eax, ecx); + return (cpuinfo[1] & (1 << 5)) != 0; /* EBX 5 */ +} /* hasAVX2() */ + +/*--------------------------------------------------------------------------*/ + int hasFMA3 (void) { /* --- check for FMA3 */ - if (!cpuinfo[4]) { cpuid(cpuinfo, 1); cpuinfo[4] = -1; } - return (cpuinfo[2] & (1 << 12)) != 0; + int eax = 1; + int ecx = 0; + if ((eax != peax) || (ecx != pecx)) + cpuid(cpuinfo, eax, ecx); + return (cpuinfo[2] & (1 << 12)) != 0; /* ECX 12 */ } /* hasFMA3() */ /*--------------------------------------------------------------------------*/ +int hasAVX512f (void) +{ /* --- check for AVX512f instructions */ + int eax = 7; + int ecx = 0; + if ((eax != peax) || (ecx != pecx)) + cpuid(cpuinfo, eax, ecx); + return (cpuinfo[1] & (1 << 16)) != 0; /* EBX 16 */ +} /* hasAVX512f() */ + +/*--------------------------------------------------------------------------*/ + +int hasAVX512cd (void) +{ /* --- check for AVX512cd instructions */ + int eax = 7; + int ecx = 0; + if ((eax != peax) || (ecx != pecx)) + cpuid(cpuinfo, eax, ecx); + return (cpuinfo[1] & (1 << 28)) != 0; /* EBX 28 */ +} /* hasAVX512cd() */ + +/*--------------------------------------------------------------------------*/ + +int hasAVX512bw (void) +{ /* --- check for AVX512bw instructions */ + int eax = 7; + int ecx = 0; + if ((eax != peax) || (ecx != pecx)) + cpuid(cpuinfo, eax, ecx); + return (cpuinfo[1] & (1 << 30)) != 0; /* EBX 30 */ +} /* hasAVX512bw() */ + +/*--------------------------------------------------------------------------*/ + +int hasAVX512dq (void) +{ /* --- check for AVX512dq instructions */ + int eax = 7; + int ecx = 0; + if ((eax != peax) || (ecx != pecx)) + cpuid(cpuinfo, eax, ecx); + return (cpuinfo[1] & (1 << 17)) != 0; /* EBX 17 */ +} /* hasAVX512dq() */ + +/*--------------------------------------------------------------------------*/ + +int hasAVX512vl (void) +{ /* --- check for AVX512vl instructions */ + int eax = 7; + int ecx = 0; + if ((eax != peax) || (ecx != pecx)) + cpuid(cpuinfo, eax, ecx); + return (cpuinfo[1] & (1 << 31)) != 0; /* EBX 31 */ +} /* hasAVX512vl() */ + +/*--------------------------------------------------------------------------*/ + void getVendorID (char *buf) { /* --- get vendor id */ /* the string is going to be exactly 12 characters long, allocate the buffer outside this function accordingly */ - int regs[4]; - cpuid(regs, 0); - ((unsigned *)buf)[0] = (unsigned)regs[1]; // EBX - ((unsigned *)buf)[1] = (unsigned)regs[3]; // EDX - ((unsigned *)buf)[2] = (unsigned)regs[2]; // ECX + int eax = 0; + int ecx = 0; + if ((eax != peax) || (ecx != pecx)) + cpuid(cpuinfo, eax, ecx); + ((unsigned *)buf)[0] = (unsigned)cpuinfo[1]; /* EBX */ + ((unsigned *)buf)[1] = (unsigned)cpuinfo[3]; /* EDX */ + ((unsigned *)buf)[2] = (unsigned)cpuinfo[2]; /* ECX */ } /* getVendorID() */ /*---------------------------------------------------------------------------- @@ -347,28 +512,36 @@ int main (int argc, char* argv[]) { char vendor[12]; getVendorID(vendor); - printf("Vendor %s\n", vendor); - #ifndef _WIN32 - printf("Processor cores %d\n", corecnt()); - #endif - printf("Logical processors %d\n", proccnt()); - printf("MMX %d\n", hasMMX()); - printf("SSE %d\n", hasSSE()); - printf("SSE2 %d\n", hasSSE2()); - printf("SSE3 %d\n", hasSSE3()); - printf("SSSE3 %d\n", hasSSSE3()); - printf("SSE41 %d\n", hasSSE41()); - printf("SSE42 %d\n", hasSSE42()); - printf("POPCNT %d\n", hasPOPCNT()); - printf("AVX %d\n", hasAVX()); - printf("FMA3 %d\n", hasFMA3()); - -/* corecnt -> number of processor cores + printf("Vendor %s\n", vendor); + printf("Physical processors %d\n", physcnt()); + printf("Processor cores %d\n", corecnt()); + printf("Logical processors %d\n", proccnt()); + printf("MMX %d\n", hasMMX()); + printf("SSE %d\n", hasSSE()); + printf("SSE2 %d\n", hasSSE2()); + printf("SSE3 %d\n", hasSSE3()); + printf("SSSE3 %d\n", hasSSSE3()); + printf("SSE41 %d\n", hasSSE41()); + printf("SSE42 %d\n", hasSSE42()); + printf("POPCNT %d\n", hasPOPCNT()); + printf("AVX %d\n", hasAVX()); + printf("AVX2 %d\n", hasAVX2()); + printf("FMA3 %d\n", hasFMA3()); + printf("AVX512f %d\n", hasAVX512f()); + printf("AVX512cd %d\n", hasAVX512cd()); + printf("AVX512bw %d\n", hasAVX512bw()); + printf("AVX512dq %d\n", hasAVX512dq()); + printf("AVX512vl %d\n", hasAVX512vl()); + +/* + physcnt -> number of physical processors/packages/sockets + corecnt -> number of processor cores proccnt -> number of logical processors proccntmax -> max. number of logical processors per core See the glossary at: software.intel.com/en-us/articles/ - intel-64-architecture-processor-topology-enumeration */ + intel-64-architecture-processor-topology-enumeration +*/ } /* main() */ diff --git a/src/kloewe/cpuinfo/src/cpuinfo.h b/src/kloewe/cpuinfo/src/cpuinfo.h index 990609128905a989c8e660af08897c74cfc84782..348da9c066ed4daaff691f8f5248742a31c5c7c5 100644 --- a/src/kloewe/cpuinfo/src/cpuinfo.h +++ b/src/kloewe/cpuinfo/src/cpuinfo.h @@ -9,17 +9,9 @@ /*---------------------------------------------------------------------------- Functions ----------------------------------------------------------------------------*/ -extern int proccnt (void); /* # logical processors */ - -#ifndef _WIN32 /* if Linux or Apple Mac OS system */ +extern int physcnt (void); /* # physical processors */ extern int corecnt (void); /* # processor cores */ - -#ifdef __linux__ -#ifdef HAVE_HWLOC -extern int corecntHwloc (void); /* # processor cores */ -#endif -#endif -#endif +extern int proccnt (void); /* # logical processors */ extern int hasMMX (void); extern int hasSSE (void); @@ -30,6 +22,12 @@ extern int hasSSE41 (void); extern int hasSSE42 (void); extern int hasPOPCNT (void); extern int hasAVX (void); +extern int hasAVX2 (void); extern int hasFMA3 (void); +extern int hasAVX512f (void); +extern int hasAVX512cd (void); +extern int hasAVX512bw (void); +extern int hasAVX512dq (void); +extern int hasAVX512vl (void); #endif /* #ifndef CPUINFO_H */ diff --git a/src/kloewe/dot/CMakeLists.txt b/src/kloewe/dot/CMakeLists.txt index 704cb2f2e91e8cf8d53ae79fe00ddcd11e2a8823..2e4a4089262daab1c030f2aaf5f2d9577f6d647a 100644 --- a/src/kloewe/dot/CMakeLists.txt +++ b/src/kloewe/dot/CMakeLists.txt @@ -8,42 +8,77 @@ if(CMAKE_VERSION VERSION_LESS "2.8.12") endfunction(add_compile_options) endif() +add_compile_options(-std=c99 -Wall -Wextra -Wno-unused-parameter -Wconversion -Wshadow -pedantic) + SET(DOT_USEFMA 0) +SET(DOT_USEAVX512 0) + if (CMAKE_COMPILER_IS_GNUCC) execute_process(COMMAND ${CMAKE_C_COMPILER} -dumpversion OUTPUT_VARIABLE GCC_VERSION) + string(FIND ${GCC_VERSION} "." GCC_VERSION_HAS_DOT) + if (${GCC_VERSION_HAS_DOT} EQUAL -1) + execute_process(COMMAND ${CMAKE_C_COMPILER} -dumpfullversion OUTPUT_VARIABLE GCC_VERSION) + endif() if (GCC_VERSION VERSION_GREATER 4.7 OR GCC_VERSION VERSION_EQUAL 4.7) - message(STATUS "Version >= 4.7") + message(STATUS "GCC version >= 4.7") SET(DOT_USEFMA 1) endif() + if (GCC_VERSION VERSION_GREATER 7.1 OR GCC_VERSION VERSION_EQUAL 7.1) + message(STATUS "GCC version >= 7.1") + add_compile_options(-Wimplicit-fallthrough=0) + SET(DOT_USEAVX512 1) + endif() endif() -add_compile_options(-std=c99 -Wall -Wextra -Wno-unused-parameter -Wconversion -Wshadow -pedantic) - add_library(dot src/dot.c) add_library(dot_naive src/dot_naive.c) add_library(dot_sse2 src/dot_sse2.c) add_library(dot_avx src/dot_avx.c) -if(DOT_USEFMA) +if(DOT_USEFMA AND DOT_USEAVX512) add_library(dot_avxfma src/dot_avx.c) - target_link_libraries(dot dot_naive dot_sse2 dot_avx dot_avxfma cpuinfo) + add_library(dot_avx512 src/dot_avx512.c) + add_library(dot_avx512fma src/dot_avx512.c) + target_link_libraries(dot dot_naive dot_sse2 dot_avx dot_avxfma dot_avx512 dot_avx512fma cpuinfo ${CARET_QT5_LINK}) +elseif(DOT_USEFMA) + add_library(dot_avxfma src/dot_avx.c) + target_link_libraries(dot dot_naive dot_sse2 dot_avx dot_avxfma cpuinfo ${CARET_QT5_LINK}) +elseif(DOT_USEAVX512) + add_library(dot_avx512 src/dot_avx512.c) + target_link_libraries(dot dot_naive dot_sse2 dot_avx dot_avx512 cpuinfo ${CARET_QT5_LINK}) else() - target_link_libraries(dot dot_naive dot_sse2 dot_avx cpuinfo) + target_link_libraries(dot dot_naive dot_sse2 dot_avx cpuinfo ${CARET_QT5_LINK}) endif() if(CMAKE_VERSION VERSION_LESS "2.8.12") - if(DOT_USEFMA) + if(DOT_USEFMA AND DOT_USEAVX512) set_target_properties(dot_avxfma PROPERTIES COMPILE_FLAGS "-mfma -mavx -funroll-loops") - else() + set_target_properties(dot_avx512 PROPERTIES COMPILE_FLAGS "-mavx512f -funroll-loops") + set_target_properties(dot_avx512fma PROPERTIES COMPILE_FLAGS "-mavx512f -mfma -funroll-loops") + elseif(DOT_USEFMA) + set_target_properties(dot_avxfma PROPERTIES COMPILE_FLAGS "-mfma -mavx -funroll-loops") + set_target_properties(dot PROPERTIES COMPILE_FLAGS "-DDOT_NOAVX512") + elseif(DOT_USEAVX512) + set_target_properties(dot_avx512 PROPERTIES COMPILE_FLAGS "-mavx512f -funroll-loops") set_target_properties(dot PROPERTIES COMPILE_FLAGS "-DDOT_NOFMA") + else() + set_target_properties(dot PROPERTIES COMPILE_FLAGS "-DDOT_NOFMA -DDOT_NOAVX512") endif() set_target_properties(dot_avx PROPERTIES COMPILE_FLAGS "-mavx -funroll-loops") set_target_properties(dot_sse2 PROPERTIES COMPILE_FLAGS "-msse2") include_directories(../cpuinfo/src) else() - if(DOT_USEFMA) + if(DOT_USEFMA AND DOT_USEAVX512) target_compile_options(dot_avxfma PRIVATE -mfma -mavx -funroll-loops) - else() + target_compile_options(dot_avx512 PRIVATE -mavx512f -funroll-loops) + target_compile_options(dot_avx512fma PRIVATE -mavx512f -mfma -funroll-loops) + elseif(DOT_USEFMA) + target_compile_options(dot_avxfma PRIVATE -mfma -mavx -funroll-loops) + target_compile_definitions(dot PRIVATE "DOT_NOAVX512") + elseif(DOT_USEAVX512) + target_compile_options(dot_avx512 PRIVATE -mavx512f -funroll-loops) target_compile_definitions(dot PRIVATE "DOT_NOFMA") + else() + target_compile_definitions(dot PRIVATE "DOT_NOFMA" "DOT_NOAVX512") endif() target_compile_options(dot_avx PRIVATE -mavx -funroll-loops) target_compile_options(dot_sse2 PRIVATE -msse2) diff --git a/src/kloewe/dot/LICENSE b/src/kloewe/dot/LICENSE index 8b99578c0c0a00419a028e823c34815998cd664f..d7cddabbfc83f5db67b40d7666ec07b6490ce32a 100644 --- a/src/kloewe/dot/LICENSE +++ b/src/kloewe/dot/LICENSE @@ -1,6 +1,6 @@ The MIT License (MIT) -Copyright (c) 2012-2016 Kristian Loewe, Christian Borgelt +Copyright (c) 2012-2017 Kristian Loewe, Christian Borgelt Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/src/kloewe/dot/src/dot.c b/src/kloewe/dot/src/dot.c index d8130b1d2baa96c8edee91a3b9385e0e0fd9dbff..f33853040da9cde581069f052088ef9c1ecb0a68 100644 --- a/src/kloewe/dot/src/dot.c +++ b/src/kloewe/dot/src/dot.c @@ -3,22 +3,24 @@ Contents: dot product (cpu dispatcher) Author : Kristian Loewe ----------------------------------------------------------------------------*/ -#include "cpuinfo.h" #include "dot.h" +#ifdef ARCH_IS_X86_64 +#include "cpuinfo.h" +#endif /*---------------------------------------------------------------------------- Function Prototypes ----------------------------------------------------------------------------*/ extern float sdot (const float *a, const float *b, int n); extern double ddot (const double *a, const double *b, int n); -extern double sddot (const float *a, const float *b, int n); +extern double dsdot (const float *a, const float *b, int n); /*---------------------------------------------------------------------------- Global Variables ----------------------------------------------------------------------------*/ sdot_func *sdot_ptr = &sdot_select; ddot_func *ddot_ptr = &ddot_select; -sddot_func *sddot_ptr = &sddot_select; +dsdot_func *dsdot_ptr = &dsdot_select; /*---------------------------------------------------------------------------- Functions @@ -34,37 +36,68 @@ double ddot_select (const double *a, const double *b, int n) { return (*ddot_ptr)(a,b,n); } -double sddot_select (const float *a, const float *b, int n) { +double dsdot_select (const float *a, const float *b, int n) { dot_set_impl(DOT_AUTO); - return (*sddot_ptr)(a,b,n); + return (*dsdot_ptr)(a,b,n); } -dot_flags dot_set_impl (dot_flags impl) { - #ifndef DOT_NOFMA - // the AVX-FMA implementations are currently slower than the AVX - // implementations and are thus only used if explicitly requested - if (hasFMA3() && hasAVX() && (impl == DOT_AVXFMA)) { // AVX-FMA - sdot_ptr = &sdot_avxfma; - ddot_ptr = &ddot_avxfma; - sddot_ptr = &sddot_avxfma; - return DOT_AVXFMA; } - else if (hasAVX() && (impl >= DOT_AVX)) { // AVX - #else - if (hasAVX() && (impl >= DOT_AVX)) { // AVX +dot_flags dot_set_impl (dot_flags impl) { + + #ifndef ARCH_IS_X86_64 + impl = DOT_NAIVE; #endif - sdot_ptr = &sdot_avx; - ddot_ptr = &ddot_avx; - sddot_ptr = &sddot_avx; - return DOT_AVX; } - else if (hasSSE2() && (impl >= DOT_SSE2)) { // SSE2 - sdot_ptr = &sdot_sse2; - ddot_ptr = &ddot_sse2; - sddot_ptr = &sddot_sse2; - return DOT_SSE2; } - else { // naive - sdot_ptr = &sdot_naive; - ddot_ptr = &ddot_naive; - sddot_ptr = &sddot_naive; - return DOT_NAIVE; + + switch (impl) { + case DOT_AUTO : + #ifndef DOT_NOAVX512 + #ifndef DOT_NOFMA + case DOT_AVX512FMA : + if (hasAVX512f() && hasFMA3()) { + sdot_ptr = &sdot_avx512fma; + ddot_ptr = &ddot_avx512fma; + dsdot_ptr = &dsdot_avx512fma; + return DOT_AVX512FMA; + } + #endif + case DOT_AVX512 : + if (hasAVX512f()) { + sdot_ptr = &sdot_avx512; + ddot_ptr = &ddot_avx512; + dsdot_ptr = &dsdot_avx512; + return DOT_AVX512; + } + #endif + #ifndef DOT_NOFMA + // Note: The AVX-FMA implementations seem to be slower than the AVX + // implementations and are thus only used if explicitly requested. + case DOT_AVXFMA : + if ((impl == DOT_AVXFMA) && hasAVX() && hasFMA3()) { + sdot_ptr = &sdot_avxfma; + ddot_ptr = &ddot_avxfma; + dsdot_ptr = &dsdot_avxfma; + return DOT_AVXFMA; + } + #endif + case DOT_AVX : + if (hasAVX()) { + sdot_ptr = &sdot_avx; + ddot_ptr = &ddot_avx; + dsdot_ptr = &dsdot_avx; + return DOT_AVX; + } + case DOT_SSE2 : + if (hasSSE2()) { + sdot_ptr = &sdot_sse2; + ddot_ptr = &ddot_sse2; + dsdot_ptr = &dsdot_sse2; + return DOT_SSE2; + } + case DOT_NAIVE : + sdot_ptr = &sdot_naive; + ddot_ptr = &ddot_naive; + dsdot_ptr = &dsdot_naive; + return DOT_NAIVE; + default : + return dot_set_impl(DOT_AUTO); } -} +} // dot_set_impl() diff --git a/src/kloewe/dot/src/dot.h b/src/kloewe/dot/src/dot.h index 48674c030164e9f51c01e510dc6bd69c4d07bfea..ced9ac2d4f6b186a38771808fa87be9e59a11349 100644 --- a/src/kloewe/dot/src/dot.h +++ b/src/kloewe/dot/src/dot.h @@ -11,15 +11,25 @@ extern "C" { #endif +/*---------------------------------------------------------------------------- + Preprocessor Definitions +----------------------------------------------------------------------------*/ +#if defined(__x86_64) || defined(__x86_64__) || defined(__amd64) \ + || defined(__amd64__) || defined(_M_AMD64) || defined(_M_X64) +#define ARCH_IS_X86_64 +#endif + /*---------------------------------------------------------------------------- Enum to encode the sets of dot implementations ----------------------------------------------------------------------------*/ typedef enum { - DOT_NAIVE = 1, // plain C - DOT_SSE2 = 2, // SSE2 - DOT_AVX = 3, // AVX - DOT_AVXFMA = 4, // AVX+FMA3 - DOT_AUTO = 100 // automatic choice + DOT_NAIVE = 1, // plain C + DOT_SSE2 = 2, // SSE2 + DOT_AVX = 3, // AVX + DOT_AVXFMA = 4, // AVX+FMA3 + DOT_AVX512 = 5, // AVX512 + DOT_AVX512FMA = 6, // AVX512+FMA3 + DOT_AUTO = 100 // automatic choice } dot_flags; // Using dot_set_impl(), these values are used to specify the set of // implementations to be used. The values/sets are ordered chronologically wrt @@ -30,23 +40,23 @@ typedef enum { /*---------------------------------------------------------------------------- Type Definitions ----------------------------------------------------------------------------*/ -typedef float (sdot_func) (const float *a, const float *b, int n); -typedef double (ddot_func) (const double *a, const double *b, int n); -typedef double (sddot_func) (const float *a, const float *b, int n); +typedef float (sdot_func) (const float *a, const float *b, int n); +typedef double (ddot_func) (const double *a, const double *b, int n); +typedef double (dsdot_func) (const float *a, const float *b, int n); /*---------------------------------------------------------------------------- Global Variables ----------------------------------------------------------------------------*/ extern sdot_func *sdot_ptr; extern ddot_func *ddot_ptr; -extern sddot_func *sddot_ptr; +extern dsdot_func *dsdot_ptr; /*---------------------------------------------------------------------------- Function Prototypes ----------------------------------------------------------------------------*/ -inline float sdot (const float *a, const float *b, int n); -inline double ddot (const double *a, const double *b, int n); -inline double sddot (const float *a, const float *b, int n); +inline float sdot (const float *a, const float *b, int n); +inline double ddot (const double *a, const double *b, int n); +inline double dsdot (const float *a, const float *b, int n); /* dot_set_impl * ------------ @@ -57,41 +67,55 @@ inline double sddot (const float *a, const float *b, int n); * * parameters * impl indicates which set of implementations should be used - * DOT_NAIVE -> plain C implementations - * DOT_SSE2 -> SSE2 implementations - * DOT_AVX -> AVX implementations - * DOT_AVXFMA -> AVX+FMA3 implementations - * DOT_AUTO -> automatically choose the best available set + * DOT_NAIVE -> plain C implementations + * DOT_SSE2 -> SSE2 implementations + * DOT_AVX -> AVX implementations + * DOT_AVXFMA -> AVX+FMA3 implementations + * DOT_AVX512 -> AVX512 implementations + * DOT_AVX512FMA -> AVX512+FMA3 implementations + * DOT_AUTO -> automatically choose the best available set * (see also the above enum) * * returns * the enum value corresponding to the selected set of implementations */ -extern dot_flags dot_set_impl (dot_flags impl); - - -extern float sdot_select (const float *a, const float *b, int n); -extern double ddot_select (const double *a, const double *b, int n); -extern double sddot_select (const float *a, const float *b, int n); - -#ifndef DOT_NOFMA -extern float sdot_avxfma (const float *a, const float *b, int n); -extern double ddot_avxfma (const double *a, const double *b, int n); -extern double sddot_avxfma (const float *a, const float *b, int n); +extern dot_flags dot_set_impl (dot_flags impl); + + +extern float sdot_select (const float *a, const float *b, int n); +extern double ddot_select (const double *a, const double *b, int n); +extern double dsdot_select (const float *a, const float *b, int n); + +extern float sdot_naive (const float *a, const float *b, int n); +extern double ddot_naive (const double *a, const double *b, int n); +extern double dsdot_naive (const float *a, const float *b, int n); + +#ifdef ARCH_IS_X86_64 +extern float sdot_sse2 (const float *a, const float *b, int n); +extern double ddot_sse2 (const double *a, const double *b, int n); +extern double dsdot_sse2 (const float *a, const float *b, int n); + +extern float sdot_avx (const float *a, const float *b, int n); +extern double ddot_avx (const double *a, const double *b, int n); +extern double dsdot_avx (const float *a, const float *b, int n); + +# ifndef DOT_NOFMA +extern float sdot_avxfma (const float *a, const float *b, int n); +extern double ddot_avxfma (const double *a, const double *b, int n); +extern double dsdot_avxfma (const float *a, const float *b, int n); +# endif +# ifndef DOT_NOAVX512 +extern float sdot_avx512 (const float *a, const float *b, int n); +extern double ddot_avx512 (const double *a, const double *b, int n); +extern double dsdot_avx512 (const float *a, const float *b, int n); +# ifndef DOT_NOFMA +extern float sdot_avx512fma (const float *a, const float *b, int n); +extern double ddot_avx512fma (const double *a, const double *b, int n); +extern double dsdot_avx512fma (const float *a, const float *b, int n); +# endif +# endif #endif -extern float sdot_avx (const float *a, const float *b, int n); -extern double ddot_avx (const double *a, const double *b, int n); -extern double sddot_avx (const float *a, const float *b, int n); - -extern float sdot_sse2 (const float *a, const float *b, int n); -extern double ddot_sse2 (const double *a, const double *b, int n); -extern double sddot_sse2 (const float *a, const float *b, int n); - -extern float sdot_naive (const float *a, const float *b, int n); -extern double ddot_naive (const double *a, const double *b, int n); -extern double sddot_naive (const float *a, const float *b, int n); - /*---------------------------------------------------------------------------- Inline Functions ----------------------------------------------------------------------------*/ @@ -104,8 +128,8 @@ inline double ddot (const double *a, const double *b, int n) { return (*ddot_ptr)(a,b,n); } -inline double sddot (const float *a, const float *b, int n) { - return (*sddot_ptr)(a,b,n); +inline double dsdot (const float *a, const float *b, int n) { + return (*dsdot_ptr)(a,b,n); } #ifdef __cplusplus diff --git a/src/kloewe/dot/src/dot_avx.c b/src/kloewe/dot/src/dot_avx.c index 3372ac2f1f9e970acd17785fba4460815ef0974c..30521b7b9bcd271e13f21c37b5527ca037066d6d 100644 --- a/src/kloewe/dot/src/dot_avx.c +++ b/src/kloewe/dot/src/dot_avx.c @@ -11,9 +11,9 @@ #ifdef __FMA__ extern float sdot_avxfma (const float *a, const float *b, int n); extern double ddot_avxfma (const double *a, const double *b, int n); -extern double sddot_avxfma (const float *a, const float *b, int n); +extern double dsdot_avxfma (const float *a, const float *b, int n); #else extern float sdot_avx (const float *a, const float *b, int n); extern double ddot_avx (const double *a, const double *b, int n); -extern double sddot_avx (const float *a, const float *b, int n); +extern double dsdot_avx (const float *a, const float *b, int n); #endif diff --git a/src/kloewe/dot/src/dot_avx.h b/src/kloewe/dot/src/dot_avx.h index 32a5203cc2504e580d0cc96d4373e159ddc22d5d..2cbcff936969a14aef7c857727c0bdd08ae062e0 100644 --- a/src/kloewe/dot/src/dot_avx.h +++ b/src/kloewe/dot/src/dot_avx.h @@ -11,6 +11,12 @@ #endif #include +#include + +// alignment check +#include +#define is_aligned(POINTER, BYTE_COUNT) \ + (((uintptr_t)(const void *)(POINTER)) % (BYTE_COUNT) == 0) /*---------------------------------------------------------------------------- Function Prototypes @@ -18,11 +24,11 @@ #ifdef __FMA__ inline float sdot_avxfma (const float *a, const float *b, int n); inline double ddot_avxfma (const double *a, const double *b, int n); -inline double sddot_avxfma (const float *a, const float *b, int n); +inline double dsdot_avxfma (const float *a, const float *b, int n); #else inline float sdot_avx (const float *a, const float *b, int n); inline double ddot_avx (const double *a, const double *b, int n); -inline double sddot_avx (const float *a, const float *b, int n); +inline double dsdot_avx (const float *a, const float *b, int n); #endif /*---------------------------------------------------------------------------- @@ -36,6 +42,23 @@ inline float sdot_avxfma (const float *a, const float *b, int n) inline float sdot_avx (const float *a, const float *b, int n) #endif { + // initialize total sum + float s = 0.0f; + + // compute and add up to 7 products without SIMD to achieve alignment + int aligned = is_aligned(a, 32) && is_aligned(b, 32); + if (!aligned) { + int k = 0; + while (!aligned) { + s += (*a) * (*b); + n--; a++; b++; + aligned = is_aligned(a, 32) && is_aligned(b, 32); + if (aligned || (++k > 6) || (n == 0)) + break; + } + } + assert(aligned || (n < 8)); + // initialize 8 sums __m256 s8 = _mm256_setzero_ps(); @@ -60,7 +83,7 @@ inline float sdot_avx (const float *a, const float *b, int n) __m128 sh = _mm_add_ss(_mm256_castps256_ps128(s8), _mm256_extractf128_ps(s8, 1)); #endif - float s = _mm_cvtss_f32(sh); // extract horizontal sum from 1st elem. + s += _mm_cvtss_f32(sh); // extract horizontal sum from 1st elem. // add the remaining products for (int k = 8*(n/8); k < n; k++) @@ -78,6 +101,23 @@ inline double ddot_avxfma (const double *a, const double *b, int n) inline double ddot_avx (const double *a, const double *b, int n) #endif { + // initialize total sum + double s = 0.0; + + // compute and add up to 3 products without SIMD to achieve alignment + int aligned = is_aligned(a, 32) && is_aligned(b, 32); + if (!aligned) { + int k = 0; + while (!aligned) { + s += (*a) * (*b); + n--; a++; b++; + aligned = is_aligned(a, 32) && is_aligned(b, 32); + if (aligned || (++k > 2) || (n == 0)) + break; + } + } + assert(aligned || (n < 4)); + // initialize 4 sums __m256d s4 = _mm256_setzero_pd(); @@ -89,11 +129,12 @@ inline double ddot_avx (const double *a, const double *b, int n) s4 = _mm256_add_pd( _mm256_mul_pd(_mm256_loadu_pd(a+k), _mm256_loadu_pd(b+k)), s4); #endif + // compute horizontal sum __m128d sh = _mm_add_pd(_mm256_extractf128_pd(s4, 0), _mm256_extractf128_pd(s4, 1)); sh = _mm_add_pd(sh, _mm_shuffle_pd(sh, sh, 1)); - double s = _mm_cvtsd_f64(sh); // extract horizontal sum from 1st elem. + s += _mm_cvtsd_f64(sh); // extract horizontal sum from 1st elem. // add the remaining products for (int k = 4*(n/4); k < n; k++) @@ -106,11 +147,28 @@ inline double ddot_avx (const double *a, const double *b, int n) // --- dot product (input: single; intermediate and output: double) #ifdef __FMA__ -inline double sddot_avxfma (const float *a, const float *b, int n) +inline double dsdot_avxfma (const float *a, const float *b, int n) #else -inline double sddot_avx (const float *a, const float *b, int n) +inline double dsdot_avx (const float *a, const float *b, int n) #endif { + // initialize total sum + double s = 0.0; + + // compute and add up to 7 products without SIMD to achieve alignment + int aligned = is_aligned(a, 32) && is_aligned(b, 32); + if (!aligned) { + int k = 0; + while (!aligned) { + s += (*a) * (*b); + n--; a++; b++; + aligned = is_aligned(a, 32) && is_aligned(b, 32); + if (aligned || (++k > 6) || (n == 0)) + break; + } + } + assert(aligned || (n < 8)); + // initialize 4 sums __m256d s4 = _mm256_setzero_pd(); @@ -123,17 +181,18 @@ inline double sddot_avx (const float *a, const float *b, int n) s4 = _mm256_add_pd( _mm256_cvtps_pd(_mm_mul_ps(_mm_loadu_ps(a+k), _mm_loadu_ps(b+k))), s4); #endif + // compute horizontal sum __m128d sh = _mm_add_pd(_mm256_extractf128_pd(s4, 0), _mm256_extractf128_pd(s4, 1)); sh = _mm_add_pd(sh, _mm_shuffle_pd(sh, sh, 1)); - double s = _mm_cvtsd_f64(sh); // extract horizontal sum from 1st elem. + s += _mm_cvtsd_f64(sh); // extract horizontal sum from 1st elem. // add the remaining products for (int k = 4*(n/4); k < n; k++) s += a[k] * b[k]; return s; -} // sddot_avx() +} // dsdot_avx() #endif // DOT_AVX_H diff --git a/src/kloewe/dot/src/dot_avx512.c b/src/kloewe/dot/src/dot_avx512.c new file mode 100644 index 0000000000000000000000000000000000000000..f8f5bc24087887705f2f1efe5598fe53e7972150 --- /dev/null +++ b/src/kloewe/dot/src/dot_avx512.c @@ -0,0 +1,19 @@ +/*---------------------------------------------------------------------------- + File : dot_avx512.c + Contents: dot product (AVX512-based implementations) + Author : Kristian Loewe +----------------------------------------------------------------------------*/ +#include "dot_avx512.h" + +/*---------------------------------------------------------------------------- + Function Prototypes +----------------------------------------------------------------------------*/ +#ifdef __FMA__ +extern float sdot_avx512fma (const float *a, const float *b, int n); +extern double ddot_avx512fma (const double *a, const double *b, int n); +extern double dsdot_avx512fma (const float *a, const float *b, int n); +#else +extern float sdot_avx512 (const float *a, const float *b, int n); +extern double ddot_avx512 (const double *a, const double *b, int n); +extern double dsdot_avx512 (const float *a, const float *b, int n); +#endif diff --git a/src/kloewe/dot/src/dot_avx512.h b/src/kloewe/dot/src/dot_avx512.h new file mode 100644 index 0000000000000000000000000000000000000000..c7ecfa9ba2008e847a8619e6c96b4125f4f20f5a --- /dev/null +++ b/src/kloewe/dot/src/dot_avx512.h @@ -0,0 +1,124 @@ +/*---------------------------------------------------------------------------- + File : dot_avx512.h + Contents: dot product (AVX512-based implementations) + Author : Kristian Loewe, Christian Borgelt +----------------------------------------------------------------------------*/ +#ifndef DOT_AVX512_H +#define DOT_AVX512_H + +#ifndef __AVX512F__ +# error "AXV512F is not enabled" +#endif + +#include + +/*---------------------------------------------------------------------------- + Function Prototypes +----------------------------------------------------------------------------*/ +#ifdef __FMA__ +inline float sdot_avx512fma (const float *a, const float *b, int n); +inline double ddot_avx512fma (const double *a, const double *b, int n); +inline double dsdot_avx512fma (const float *a, const float *b, int n); +#else +inline float sdot_avx512 (const float *a, const float *b, int n); +inline double ddot_avx512 (const double *a, const double *b, int n); +inline double dsdot_avx512 (const float *a, const float *b, int n); +#endif + +/*---------------------------------------------------------------------------- + Inline Functions +----------------------------------------------------------------------------*/ + +// --- dot product (single precision) +#ifdef __FMA__ +inline float sdot_avx512fma (const float *a, const float *b, int n) +#else +inline float sdot_avx512 (const float *a, const float *b, int n) +#endif +{ + // initialize 16 sums + __m512 s16 = _mm512_setzero_ps(); + + // in each iteration, add 1 product to each of the 16 sums in parallel + for (int k = 0, nq = 16*(n/16); k < nq; k += 16) + #ifdef __FMA__ + s16 = _mm512_fmadd_ps(_mm512_loadu_ps(a+k), _mm512_loadu_ps(b+k), s16); + #else + s16 = _mm512_add_ps( + _mm512_mul_ps(_mm512_loadu_ps(a+k), _mm512_loadu_ps(b+k)), s16); + #endif + + // compute horizontal sum + float s = _mm512_reduce_add_ps(s16); + + // add the remaining products + for (int k = 16*(n/16); k < n; k++) + s += a[k] * b[k]; + + return s; +} // sdot_avx512() + +/*--------------------------------------------------------------------------*/ + +// --- dot product (double precision) +#ifdef __FMA__ +inline double ddot_avx512fma (const double *a, const double *b, int n) +#else +inline double ddot_avx512 (const double *a, const double *b, int n) +#endif +{ + // initialize 8 sums + __m512d s8 = _mm512_setzero_pd(); + + // in each iteration, add 1 product to each of the 8 sums in parallel + for (int k = 0, nq = 8*(n/8); k < nq; k += 8) + #ifdef __FMA__ + s8 = _mm512_fmadd_pd(_mm512_loadu_pd(a+k), _mm512_loadu_pd(b+k), s8); + #else + s8 = _mm512_add_pd( + _mm512_mul_pd(_mm512_loadu_pd(a+k), _mm512_loadu_pd(b+k)), s8); + #endif + + // compute horizontal sum + double s = _mm512_reduce_add_pd(s8); + + // add the remaining products + for (int k = 8*(n/8); k < n; k++) + s += a[k] * b[k]; + + return s; +} // ddot_avx512() + +/*--------------------------------------------------------------------------*/ + +// --- dot product (input: single; intermediate and output: double) +#ifdef __FMA__ +inline double dsdot_avx512fma (const float *a, const float *b, int n) +#else +inline double dsdot_avx512 (const float *a, const float *b, int n) +#endif +{ + // initialize 8 sums + __m512d s8 = _mm512_setzero_pd(); + + // in each iteration, add 1 product to each of the 4 sums in parallel + for (int k = 0, nq = 8*(n/8); k < nq; k += 8) + #ifdef __FMA__ + s8 = _mm512_fmadd_pd(_mm512_cvtps_pd(_mm256_loadu_ps(a+k)), + _mm512_cvtps_pd(_mm256_loadu_ps(b+k)), s8); + #else + s8 = _mm512_add_pd(_mm512_cvtps_pd( + _mm256_mul_ps(_mm256_loadu_ps(a+k), _mm256_loadu_ps(b+k))), s8); + #endif + + // compute horizontal sum + double s = _mm512_reduce_add_pd(s8); + + // add the remaining products + for (int k = 8*(n/8); k < n; k++) + s += a[k] * b[k]; + + return s; +} // dsdot_avx512() + +#endif // DOT_AVX512_H diff --git a/src/kloewe/dot/src/dot_avxfma.c b/src/kloewe/dot/src/dot_avxfma.c new file mode 100644 index 0000000000000000000000000000000000000000..5b362675213fdae23fed48f63fc687836ae18dd4 --- /dev/null +++ b/src/kloewe/dot/src/dot_avxfma.c @@ -0,0 +1,6 @@ +/*---------------------------------------------------------------------------- + File : dot_avxfma.c + Contents: dot product (AVX-based implementations) + Author : Kristian Loewe +----------------------------------------------------------------------------*/ +#include "dot_avx.c" diff --git a/src/kloewe/dot/src/dot_naive.c b/src/kloewe/dot/src/dot_naive.c index cdab2e9a78dfb3c2bdaec88969324a7ed34d34a1..4506bfffcea0cf9a1ebc4be6bf70dfb7c95edb0f 100644 --- a/src/kloewe/dot/src/dot_naive.c +++ b/src/kloewe/dot/src/dot_naive.c @@ -10,4 +10,4 @@ ----------------------------------------------------------------------------*/ extern float sdot_naive (const float *a, const float *b, int n); extern double ddot_naive (const double *a, const double *b, int n); -extern double sddot_naive (const float *a, const float *b, int n); +extern double dsdot_naive (const float *a, const float *b, int n); diff --git a/src/kloewe/dot/src/dot_naive.h b/src/kloewe/dot/src/dot_naive.h index 00ef1ef4a8103d6733c7ae64903e2eb289e3aa12..754411d6d6e9b27351c310732ccac3f5e4b9059f 100644 --- a/src/kloewe/dot/src/dot_naive.h +++ b/src/kloewe/dot/src/dot_naive.h @@ -11,7 +11,7 @@ ----------------------------------------------------------------------------*/ inline float sdot_naive (const float *a, const float *b, int n); inline double ddot_naive (const double *a, const double *b, int n); -inline double sddot_naive (const float *a, const float *b, int n); +inline double dsdot_naive (const float *a, const float *b, int n); /*---------------------------------------------------------------------------- Inline Functions @@ -52,12 +52,12 @@ inline double sddot_naive (const float *a, const float *b, int n); /*--------------------------------------------------------------------------*/ // --- dot product (input: single; intermediate and output: double) -inline double sddot_naive (const float *a, const float *b, int n) +inline double dsdot_naive (const float *a, const float *b, int n) { double sum = 0; for (int k = 0; k < n; k++) sum += a[k] * b[k]; return sum; -} // sddot_naive() +} // dsdot_naive() #endif // DOT_NAIVE_H diff --git a/src/kloewe/dot/src/dot_sse2.c b/src/kloewe/dot/src/dot_sse2.c index 2c832330ade47f9291e52c82fb5c84c973379722..3fbc699ee5f957709e56b10919ddcb04b4865880 100644 --- a/src/kloewe/dot/src/dot_sse2.c +++ b/src/kloewe/dot/src/dot_sse2.c @@ -10,4 +10,4 @@ ----------------------------------------------------------------------------*/ extern float sdot_sse2 (const float *a, const float *b, int n); extern double ddot_sse2 (const double *a, const double *b, int n); -extern double sddot_sse2 (const float *a, const float *b, int n); +extern double dsdot_sse2 (const float *a, const float *b, int n); diff --git a/src/kloewe/dot/src/dot_sse2.h b/src/kloewe/dot/src/dot_sse2.h index 5b2c0595911274dc0a8b41f3351a2cd628f53593..b35ea92733492652733baf4dfdace757a9efa4a9 100644 --- a/src/kloewe/dot/src/dot_sse2.h +++ b/src/kloewe/dot/src/dot_sse2.h @@ -1,13 +1,6 @@ /*---------------------------------------------------------------------------- File: dot_sse2.h Contents: dot product (SSE2-based implementations) - - Notes: - On older CPU models such as the Xeon E5440, unaligned load (_mm_loadu_ps) - is significantly slower than aligned load (_mm_load_ps) which is why we - try to avoid it here if possible. On newer CPU models, the AVX version - should be used. - Authors: Kristian Loewe, Christian Borgelt ----------------------------------------------------------------------------*/ #ifndef DOT_SSE2_H @@ -38,7 +31,7 @@ ----------------------------------------------------------------------------*/ inline float sdot_sse2 (const float *a, const float *b, int n); inline double ddot_sse2 (const double *a, const double *b, int n); -inline double sddot_sse2 (const float *a, const float *b, int n); +inline double dsdot_sse2 (const float *a, const float *b, int n); /*---------------------------------------------------------------------------- Inline Functions @@ -48,9 +41,9 @@ inline double sddot_sse2 (const float *a, const float *b, int n); inline float sdot_sse2 (const float *a, const float *b, int n) { // initialize total sum - float s = 0.0; + float s = 0.0f; - // compute and add up to 3 products without SIMD, hoping for alignment + // compute and add up to 3 products without SIMD to achieve alignment int aligned = is_aligned(a, 16) && is_aligned(b, 16); if (!aligned) { int k = 0; @@ -58,7 +51,7 @@ inline float sdot_sse2 (const float *a, const float *b, int n) s += (*a) * (*b); n--; a++; b++; aligned = is_aligned(a, 16) && is_aligned(b, 16); - if (aligned || (++k > 2) || (++k > n)) + if (aligned || (++k > 2) || (n == 0)) break; } } @@ -99,7 +92,7 @@ inline double ddot_sse2 (const double *a, const double *b, int n) // initialize total sum double s = 0.0; - // compute and add up to 1 product without SIMD, hoping for alignment + // compute and add up to 1 product without SIMD to achieve alignment int aligned = is_aligned(a, 16) && is_aligned(b, 16); if (!aligned) { s += (*a) * (*b); @@ -134,12 +127,12 @@ inline double ddot_sse2 (const double *a, const double *b, int n) /*--------------------------------------------------------------------------*/ // --- dot product (input: single; intermediate and output: double) -inline double sddot_sse2 (const float *a, const float *b, int n) +inline double dsdot_sse2 (const float *a, const float *b, int n) { // initialize total sum double s = 0.0; - // compute and add up to 3 products without SIMD, hoping for alignment + // compute and add up to 3 products without SIMD to achieve alignment int aligned = is_aligned(a, 16) && is_aligned(b, 16); if (!aligned) { int k = 0; @@ -147,7 +140,7 @@ inline double sddot_sse2 (const float *a, const float *b, int n) s += (*a) * (*b); n--; a++; b++; aligned = is_aligned(a, 16) && is_aligned(b, 16); - if (aligned || (++k > 2) || (++k > n)) + if (aligned || (++k > 2) || (n == 0)) break; } } @@ -186,6 +179,6 @@ inline double sddot_sse2 (const float *a, const float *b, int n) s += a[k] * b[k]; return s; -} // sddot_sse2() +} // dsdot_sse2() #endif // DOT_SSE2_H